Merge "Prevent Scrolling using DPAD when `userScrollEnabled=false`" into androidx-main
diff --git a/OWNERS b/OWNERS
index 13e8cd6..e6c536a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -26,6 +26,7 @@
per-file *settings.gradle = set noparent
per-file *settings.gradle = [email protected], [email protected]
per-file *libraryversions.toml = [email protected]
+per-file *libraryversions.toml = [email protected], [email protected], [email protected]
# Copybara can self-approve CLs within synced docs.
per-file docs/** = [email protected]
\ No newline at end of file
diff --git a/activity/activity-compose/samples/src/main/java/androidx/activity/compose/samples/BackHandlerSample.kt b/activity/activity-compose/samples/src/main/java/androidx/activity/compose/samples/BackHandlerSample.kt
index 1174984..c5a9c26528 100644
--- a/activity/activity-compose/samples/src/main/java/androidx/activity/compose/samples/BackHandlerSample.kt
+++ b/activity/activity-compose/samples/src/main/java/androidx/activity/compose/samples/BackHandlerSample.kt
@@ -17,10 +17,8 @@
package androidx.activity.compose.samples
import androidx.activity.compose.BackHandler
-import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.annotation.Sampled
-import androidx.compose.material.Button
-import androidx.compose.material.Text
+import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -30,12 +28,14 @@
@Sampled
@Composable
fun BackHandler() {
- var backPressedCount by remember { mutableStateOf(0) }
- BackHandler { backPressedCount++ }
+ var text by remember { mutableStateOf("") }
- val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
+ TextField(
+ value = text,
+ onValueChange = { text = it }
+ )
- Button(onClick = { dispatcher.onBackPressed() }) {
- Text("Press Back count $backPressedCount")
+ BackHandler(text.isNotEmpty()) {
+ // handle back event
}
}
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 021c371..5362c61 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
@@ -77,7 +77,7 @@
) {
private var id: String? = null
private var property: PropertyT? = null
- private var capabilityExecutor: CapabilityExecutor<ArgumentsT, OutputT>? = null
+ private var executionCallback: ExecutionCallback<ArgumentsT, OutputT>? = null
private var sessionFactory: ExecutionSessionFactory<ExecutionSessionT>? = null
/**
@@ -111,39 +111,40 @@
}
/**
- * Sets the CapabilityExecutor for this capability.
+ * Sets the ExecutionCallback for this capability.
*
- * setExecutionSessionFactory and setExecutor are mutually exclusive, so calling one will
- * nullify the other.
+ * [setExecutionSessionFactory] and [setExecutionCallback] are mutually exclusive, so
+ * calling one will nullify the other.
*
- * This method accepts a coroutine-based CapabilityExecutor instance. There is also an overload
- * which accepts the CapabilityExecutorAsync instead.
+ * This method accepts a coroutine-based ExecutionCallback instance. There is also an
+ * overload which accepts the ExecutionCallbackAsync instead.
*/
- fun setExecutor(capabilityExecutor: CapabilityExecutor<ArgumentsT, OutputT>) =
+ fun setExecutionCallback(executionCallback: ExecutionCallback<ArgumentsT, OutputT>) =
asBuilder().apply {
- this.capabilityExecutor = capabilityExecutor
+ this.executionCallback = executionCallback
}
/**
- * Sets the CapabilityExecutorAsync for this capability.
+ * Sets the ExecutionCallbackAsync for this capability.
*
- * setExecutionSessionFactory and setExecutor are mutually exclusive, so calling one will
- * nullify the other.
+ * setExecutionSessionFactory and setExecutionCallback are mutually exclusive, so calling
+ * one will nullify the other.
*
- * This method accepts the CapabilityExecutorAsync interface which returns a ListenableFuture.
+ * This method accepts the ExecutionCallbackAsync interface which returns a
+ * []ListenableFuture].
*/
- fun setExecutor(
- capabilityExecutorAsync: CapabilityExecutorAsync<ArgumentsT, OutputT>
+ fun setExecutionCallback(
+ executionCallbackAsync: ExecutionCallbackAsync<ArgumentsT, OutputT>
) = asBuilder().apply {
- this.capabilityExecutor = capabilityExecutorAsync.toCapabilityExecutor()
+ this.executionCallback = executionCallbackAsync.toExecutionCallback()
}
/**
* Sets the SessionBuilder instance which is used to create Session instaces for this
* capability.
*
- * [setExecutionSessionFactory] and [setExecutor] are mutually exclusive, so calling one
- * will nullify the other.
+ * [setExecutionSessionFactory] and [setExecutionCallback] are mutually exclusive, so
+ * calling one will nullify the other.
*/
protected open fun setExecutionSessionFactory(
sessionFactory: ExecutionSessionFactory<ExecutionSessionT>
@@ -155,16 +156,17 @@
open fun build(): Capability {
val checkedId = requireNotNull(id) { "setId must be called before build" }
val checkedProperty = requireNotNull(property) { "property must not be null." }
- if (capabilityExecutor != null) {
+ if (executionCallback != null) {
return SingleTurnCapabilityImpl(
checkedId,
actionSpec,
checkedProperty,
- capabilityExecutor!!
+ executionCallback!!
)
} else {
val checkedSessionFactory = requireNotNull(sessionFactory) {
- "either setExecutor or setExecutionSessionFactory must be called before build"
+ "either setExecutionCallback or setExecutionSessionFactory" +
+ " must be called before build"
}
return TaskCapabilityImpl(
checkedId,
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutor.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallback.kt
similarity index 76%
rename from appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutor.kt
rename to appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallback.kt
index 01e9aae..87ff079 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutor.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallback.kt
@@ -23,9 +23,9 @@
* An interface of executing the action.
*
* Actions are executed asynchronously using Kotlin coroutines.
- * For a Future-based solution, see CapabilityExecutorAsync.
+ * For a Future-based solution, see ExecutionCallbackAsync.
*/
-fun interface CapabilityExecutor<ArgumentsT, OutputT> {
+fun interface ExecutionCallback<ArgumentsT, OutputT> {
@get:RestrictTo(RestrictTo.Scope.LIBRARY)
val uiHandle: Any
get() = this
@@ -40,9 +40,9 @@
}
internal fun <ArgumentsT, OutputT>
- CapabilityExecutorAsync<ArgumentsT, OutputT>.toCapabilityExecutor():
- CapabilityExecutor<ArgumentsT, OutputT> = object : CapabilityExecutor<ArgumentsT, OutputT> {
- override val uiHandle = this@toCapabilityExecutor
+ ExecutionCallbackAsync<ArgumentsT, OutputT>.toExecutionCallback():
+ ExecutionCallback<ArgumentsT, OutputT> = object : ExecutionCallback<ArgumentsT, OutputT> {
+ override val uiHandle = this@toExecutionCallback
override suspend fun onExecute(arguments: ArgumentsT): ExecutionResult<OutputT> =
- [email protected](arguments).await()
+ [email protected](arguments).await()
}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutorAsync.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallbackAsync.kt
similarity index 94%
rename from appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutorAsync.kt
rename to appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallbackAsync.kt
index 73d04fd..239f6a4 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityExecutorAsync.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/ExecutionCallbackAsync.kt
@@ -19,7 +19,7 @@
import com.google.common.util.concurrent.ListenableFuture
/** An ListenableFuture-based interface of executing an action. */
-fun interface CapabilityExecutorAsync<ArgumentsT, OutputT> {
+fun interface ExecutionCallbackAsync<ArgumentsT, OutputT> {
/**
* Calls to execute the action.
*
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/CapabilitySession.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/CapabilitySession.kt
index 632ce37..e2ced1d 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/CapabilitySession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/CapabilitySession.kt
@@ -62,7 +62,7 @@
/**
* The developer-provided external object (either a BaseExecutionSession instance or an
- * CapabilityExecutor instance).
+ * ExecutionCallback instance).
*/
val uiHandle: Any
}
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 4ff60e0..050d370 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,7 +17,7 @@
package androidx.appactions.interaction.capabilities.core.impl
import androidx.annotation.RestrictTo
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
+import androidx.appactions.interaction.capabilities.core.ExecutionCallback
import androidx.appactions.interaction.capabilities.core.Capability
import androidx.appactions.interaction.capabilities.core.HostProperties
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
@@ -35,11 +35,11 @@
id: String,
val actionSpec: ActionSpec<PropertyT, ArgumentsT, OutputT>,
val property: PropertyT,
- val capabilityExecutor: CapabilityExecutor<ArgumentsT, OutputT>,
+ val executionCallback: ExecutionCallback<ArgumentsT, OutputT>,
) : Capability(id) {
private val mutex = Mutex()
- override val appAction: AppAction =
+ override val appAction: AppAction get() =
actionSpec.convertPropertyToProto(property).toBuilder()
.setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(false))
.setIdentifier(id)
@@ -52,7 +52,7 @@
return SingleTurnCapabilitySession(
sessionId,
actionSpec,
- capabilityExecutor,
+ executionCallback,
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 0bb184a..015147a 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
@@ -17,7 +17,7 @@
package androidx.appactions.interaction.capabilities.core.impl
import androidx.annotation.RestrictTo
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
+import androidx.appactions.interaction.capabilities.core.ExecutionCallback
import androidx.appactions.interaction.capabilities.core.ExecutionResult
import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
import androidx.appactions.interaction.proto.AppActionsContext.AppDialogState
@@ -42,7 +42,7 @@
>(
override val sessionId: String,
private val actionSpec: ActionSpec<*, ArgumentsT, OutputT>,
- private val capabilityExecutor: CapabilityExecutor<ArgumentsT, OutputT>,
+ private val executionCallback: ExecutionCallback<ArgumentsT, OutputT>,
private val mutex: Mutex,
private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) : CapabilitySession {
@@ -51,7 +51,7 @@
override val state: AppDialogState? = null
override val isActive: Boolean get() = isActiveAtomic.get()
- override val uiHandle: Any = capabilityExecutor.uiHandle
+ override val uiHandle: Any = executionCallback.uiHandle
override fun destroy() {}
@@ -75,7 +75,7 @@
try {
mutex.lock(owner = this@SingleTurnCapabilitySession)
UiHandleRegistry.registerUiHandle(uiHandle, sessionId)
- val output = capabilityExecutor.onExecute(arguments)
+ val output = executionCallback.onExecute(arguments)
callback.onSuccess(convertToFulfillmentResponse(output))
} catch (t: Throwable) {
callback.onError(ErrorStatusInternal.CANCELLED)
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java
index 34f1ddc..075d36f3 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java
@@ -42,6 +42,7 @@
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Optional;
@@ -213,8 +214,9 @@
@NonNull
@Override
public ParamValue toParamValue(LocalDate value) {
- // TODO(b/275456249): Implement backwards conversion.
- return ParamValue.getDefaultInstance();
+ return ParamValue.newBuilder()
+ .setStringValue(value.format(DateTimeFormatter.ISO_LOCAL_DATE))
+ .build();
}
@Override
@@ -237,8 +239,9 @@
@NonNull
@Override
public ParamValue toParamValue(LocalTime value) {
- // TODO(b/275456249)): Implement backwards conversion.
- return ParamValue.getDefaultInstance();
+ return ParamValue.newBuilder()
+ .setStringValue(value.format(DateTimeFormatter.ISO_LOCAL_TIME))
+ .build();
}
@Override
@@ -261,8 +264,7 @@
@NonNull
@Override
public ParamValue toParamValue(ZoneId value) {
- // TODO(b/275456249)): Implement backwards conversion.
- return ParamValue.getDefaultInstance();
+ return ParamValue.newBuilder().setStringValue(value.getId()).build();
}
@Override
@@ -285,8 +287,9 @@
@NonNull
@Override
public ParamValue toParamValue(ZonedDateTime value) {
- // TODO(b/275456249)): Implement backwards conversion.
- return ParamValue.getDefaultInstance();
+ return ParamValue.newBuilder()
+ .setStringValue(value.format(DateTimeFormatter.ISO_ZONED_DATE_TIME))
+ .build();
}
@Override
@@ -310,8 +313,7 @@
@NonNull
@Override
public ParamValue toParamValue(Duration value) {
- // TODO(b/275456249)): Implement backwards conversion.
- return ParamValue.getDefaultInstance();
+ return ParamValue.newBuilder().setStringValue(value.toString()).build();
}
@Override
@@ -337,8 +339,9 @@
@NonNull
@Override
public ParamValue toParamValue(Call.CanonicalValue.CallFormat value) {
- // TODO(b/275456249)): Implement backwards conversion.
- return ParamValue.getDefaultInstance();
+ return ParamValue.newBuilder()
+ .setStringValue(value.getTextValue())
+ .build();
}
@Override
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 fade42a..3c51e47 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
@@ -51,7 +51,7 @@
private val sessionUpdaterSupplier: Supplier<SessionUpdaterT>
) : Capability(id) {
- override val appAction: AppAction =
+ override val appAction: AppAction get() =
actionSpec
.convertPropertyToProto(property)
.toBuilder()
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 a8b66c3..b6d8e63 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
@@ -17,9 +17,9 @@
package androidx.appactions.interaction.capabilities.core.impl
import android.util.SizeF
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutorAsync
-import androidx.appactions.interaction.capabilities.core.toCapabilityExecutor
+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
@@ -35,10 +35,13 @@
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.proto.AppActionsContext.AppAction
+import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter
import androidx.appactions.interaction.proto.FulfillmentResponse
import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput
import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput.OutputValue
import androidx.appactions.interaction.proto.ParamValue
+import androidx.appactions.interaction.proto.TaskInfo
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.runBlocking
@@ -54,9 +57,66 @@
private val fakeSessionId = "fakeSessionId"
@Test
+ fun appAction_computedProperty() {
+ val mutableEntityList = mutableListOf<Entity>()
+ val capability = SingleTurnCapabilityImpl(
+ id = "capabilityId",
+ actionSpec = ACTION_SPEC,
+ property = Properties.newBuilder()
+ .setRequiredEntityField(
+ Property.Builder<Entity>().setPossibleValueSupplier(
+ mutableEntityList::toList
+ ).build()
+ )
+ .build(),
+ executionCallback = ExecutionCallback<Arguments, Output> {
+ ExecutionResult.Builder<Output>().build()
+ }
+ )
+ mutableEntityList.add(Entity.Builder().setName("entity1").build())
+
+ assertThat(capability.appAction).isEqualTo(
+ AppAction.newBuilder()
+ .setIdentifier("capabilityId")
+ .setName("actions.intent.TEST")
+ .addParams(
+ IntentParameter.newBuilder()
+ .setName("requiredEntity")
+ .addPossibleEntities(
+ androidx.appactions.interaction.proto.Entity.newBuilder()
+ .setName("entity1")
+ )
+ )
+ .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(false))
+ .build()
+ )
+
+ mutableEntityList.add(Entity.Builder().setName("entity2").build())
+ assertThat(capability.appAction).isEqualTo(
+ AppAction.newBuilder()
+ .setIdentifier("capabilityId")
+ .setName("actions.intent.TEST")
+ .addParams(
+ IntentParameter.newBuilder()
+ .setName("requiredEntity")
+ .addPossibleEntities(
+ androidx.appactions.interaction.proto.Entity.newBuilder()
+ .setName("entity1")
+ )
+ .addPossibleEntities(
+ androidx.appactions.interaction.proto.Entity.newBuilder()
+ .setName("entity2")
+ )
+ )
+ .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(false))
+ .build()
+ )
+ }
+
+ @Test
fun oneShotCapability_successWithOutput() {
- val capabilityExecutor =
- CapabilityExecutor<Arguments, Output> {
+ val executionCallback =
+ ExecutionCallback<Arguments, Output> {
ExecutionResult.Builder<Output>()
.setOutput(
Output.builder().setOptionalStringField("stringOutput").build()
@@ -74,7 +134,7 @@
)
.setOptionalStringField(Property.prohibited())
.build(),
- capabilityExecutor = capabilityExecutor
+ executionCallback = executionCallback
)
val capabilitySession = capability.createSession(fakeSessionId, hostProperties)
@@ -119,8 +179,8 @@
@Test
fun oneShotCapability_failure() {
- val capabilityExecutor =
- CapabilityExecutor<Arguments, Output> { throw IllegalStateException("") }
+ val executionCallback =
+ ExecutionCallback<Arguments, Output> { throw IllegalStateException("") }
val capability =
SingleTurnCapabilityImpl(
id = "capabilityId",
@@ -132,7 +192,7 @@
)
.setOptionalStringField(Property.prohibited())
.build(),
- capabilityExecutor = capabilityExecutor
+ executionCallback = executionCallback
)
val capabilitySession = capability.createSession(fakeSessionId, hostProperties)
@@ -153,9 +213,9 @@
}
@Test
- fun oneShotSession_uiHandle_withCapabilityExecutor() {
- val capabilityExecutor =
- CapabilityExecutor<Arguments, Output> { ExecutionResult.Builder<Output>().build() }
+ fun oneShotSession_uiHandle_withExecutionCallback() {
+ val executionCallback =
+ ExecutionCallback<Arguments, Output> { ExecutionResult.Builder<Output>().build() }
val capability =
SingleTurnCapabilityImpl(
id = "capabilityId",
@@ -166,16 +226,16 @@
Property.Builder<Entity>().build()
)
.build(),
- capabilityExecutor = capabilityExecutor
+ executionCallback = executionCallback
)
val session = capability.createSession(fakeSessionId, hostProperties)
- assertThat(session.uiHandle).isSameInstanceAs(capabilityExecutor)
+ assertThat(session.uiHandle).isSameInstanceAs(executionCallback)
}
@Test
- fun oneShotSession_uiHandle_withCapabilityExecutorAsync() {
- val capabilityExecutorAsync =
- CapabilityExecutorAsync<Arguments, Output> {
+ fun oneShotSession_uiHandle_withExecutionCallbackAsync() {
+ val executionCallbackAsync =
+ ExecutionCallbackAsync<Arguments, Output> {
Futures.immediateFuture(ExecutionResult.Builder<Output>().build())
}
val capability =
@@ -188,10 +248,10 @@
Property.Builder<Entity>().build()
)
.build(),
- capabilityExecutor = capabilityExecutorAsync.toCapabilityExecutor()
+ executionCallback = executionCallbackAsync.toExecutionCallback()
)
val session = capability.createSession(fakeSessionId, hostProperties)
- assertThat(session.uiHandle).isSameInstanceAs(capabilityExecutorAsync)
+ assertThat(session.uiHandle).isSameInstanceAs(executionCallbackAsync)
}
@Test
@@ -199,7 +259,7 @@
val executionResultChannel = Channel<ExecutionResult<Output>>()
val argumentChannel = Channel<Arguments>()
- val capabilityExecutor = CapabilityExecutor<Arguments, Output> {
+ val executionCallback = ExecutionCallback<Arguments, Output> {
argumentChannel.send(it)
executionResultChannel.receive()
}
@@ -209,7 +269,7 @@
property = Properties.newBuilder().setRequiredEntityField(
Property.Builder<Entity>().build()
).build(),
- capabilityExecutor = capabilityExecutor
+ executionCallback = executionCallback
)
val session1 = capability.createSession("session1", hostProperties)
val session2 = capability.createSession("session2", hostProperties)
@@ -236,7 +296,7 @@
callbackInternal2
)
- // verify CapabilityExecutor receives 1st request.
+ // verify ExecutionCallback receives 1st request.
assertThat(argumentChannel.receive()).isEqualTo(
Arguments.newBuilder().setOptionalStringField("string value 1").build()
)
@@ -266,6 +326,13 @@
.setDescriptor(Properties::class.java)
.setArguments(Arguments::class.java, Arguments::newBuilder)
.setOutput(Output::class.java)
+ .bindParameter(
+ "requiredEntity",
+ Properties::requiredEntityField,
+ Arguments.Builder::setRequiredEntityField,
+ TypeConverters.ENTITY_PARAM_VALUE_CONVERTER,
+ TypeConverters.ENTITY_ENTITY_CONVERTER
+ )
.bindOptionalParameter(
"optionalString",
Properties::optionalStringField,
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConvertersTest.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConvertersTest.java
index 074820d..3deb2a0 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConvertersTest.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConvertersTest.java
@@ -490,16 +490,13 @@
}
@Test
- public void toLocalDate_success() throws Exception {
- List<ParamValue> input =
- Collections.singletonList(
- ParamValue.newBuilder().setStringValue("2018-06-17").build());
+ public void localDate_success() throws Exception {
+ ParamValueConverter<LocalDate> converter = TypeConverters.LOCAL_DATE_PARAM_VALUE_CONVERTER;
+ ParamValue paramValue = ParamValue.newBuilder().setStringValue("2018-06-17").build();
+ LocalDate localDate = LocalDate.of(2018, 6, 17);
- assertThat(
- SlotTypeConverter.ofSingular(
- TypeConverters.LOCAL_DATE_PARAM_VALUE_CONVERTER)
- .convert(input))
- .isEqualTo(LocalDate.of(2018, 6, 17));
+ assertThat(converter.fromParamValue(paramValue)).isEqualTo(localDate);
+ assertThat(converter.toParamValue(localDate)).isEqualTo(paramValue);
}
@Test
@@ -537,16 +534,13 @@
}
@Test
- public void toLocalTime_success() throws Exception {
- List<ParamValue> input =
- Collections.singletonList(
- ParamValue.newBuilder().setStringValue("15:10:05").build());
+ public void localTime_success() throws Exception {
+ ParamValueConverter<LocalTime> converter = TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER;
+ ParamValue paramValue = ParamValue.newBuilder().setStringValue("15:10:05").build();
+ LocalTime localTime = LocalTime.of(15, 10, 5);
- assertThat(
- SlotTypeConverter.ofSingular(
- TypeConverters.LOCAL_TIME_PARAM_VALUE_CONVERTER)
- .convert(input))
- .isEqualTo(LocalTime.of(15, 10, 5));
+ assertThat(converter.fromParamValue(paramValue)).isEqualTo(localTime);
+ assertThat(converter.toParamValue(localTime)).isEqualTo(paramValue);
}
@Test
@@ -583,15 +577,13 @@
}
@Test
- public void toZoneId_success() throws Exception {
- List<ParamValue> input =
- Collections.singletonList(
- ParamValue.newBuilder().setStringValue("America/New_York").build());
+ public void zoneId_success() throws Exception {
+ ParamValueConverter<ZoneId> converter = TypeConverters.ZONE_ID_PARAM_VALUE_CONVERTER;
+ ParamValue paramValue = ParamValue.newBuilder().setStringValue("America/New_York").build();
+ ZoneId zoneId = ZoneId.of("America/New_York");
- assertThat(
- SlotTypeConverter.ofSingular(TypeConverters.ZONE_ID_PARAM_VALUE_CONVERTER)
- .convert(input))
- .isEqualTo(ZoneId.of("America/New_York"));
+ assertThat(converter.fromParamValue(paramValue)).isEqualTo(zoneId);
+ assertThat(converter.toParamValue(zoneId)).isEqualTo(paramValue);
}
@Test
@@ -627,16 +619,15 @@
}
@Test
- public void toZonedDateTime_fromList() throws Exception {
- List<ParamValue> input =
- Collections.singletonList(
- ParamValue.newBuilder().setStringValue("2018-06-17T15:10:05Z").build());
+ public void zonedDateTime_success() throws Exception {
+ ParamValueConverter<ZonedDateTime> converter =
+ TypeConverters.ZONED_DATETIME_PARAM_VALUE_CONVERTER;
+ ParamValue paramValue =
+ ParamValue.newBuilder().setStringValue("2018-06-17T15:10:05Z").build();
+ ZonedDateTime zonedDateTime = ZonedDateTime.of(2018, 6, 17, 15, 10, 5, 0, ZoneOffset.UTC);
- assertThat(
- SlotTypeConverter.ofSingular(
- TypeConverters.ZONED_DATETIME_PARAM_VALUE_CONVERTER)
- .convert(input))
- .isEqualTo(ZonedDateTime.of(2018, 6, 17, 15, 10, 5, 0, ZoneOffset.UTC));
+ assertThat(converter.fromParamValue(paramValue)).isEqualTo(zonedDateTime);
+ assertThat(converter.toParamValue(zonedDateTime)).isEqualTo(paramValue);
}
@Test
@@ -678,15 +669,14 @@
}
@Test
- public void toDuration_success() throws Exception {
- List<ParamValue> input =
- Collections.singletonList(ParamValue.newBuilder().setStringValue("PT5M").build());
+ public void duration_success() throws Exception {
+ ParamValueConverter<Duration> converter = TypeConverters.DURATION_PARAM_VALUE_CONVERTER;
+ ParamValue paramValue =
+ ParamValue.newBuilder().setStringValue("PT5M").build();
+ Duration duration = Duration.ofMinutes(5);
- Duration convertedDuration =
- SlotTypeConverter.ofSingular(TypeConverters.DURATION_PARAM_VALUE_CONVERTER)
- .convert(input);
-
- assertThat(convertedDuration).isEqualTo(Duration.ofMinutes(5));
+ assertThat(converter.fromParamValue(paramValue)).isEqualTo(duration);
+ assertThat(converter.toParamValue(duration)).isEqualTo(paramValue);
}
@Test
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 8c6546b..d4c4273 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
@@ -103,7 +103,7 @@
private val fakeSessionId = "fakeSessionId"
@Test
- fun getAppAction_smokeTest() {
+ fun appAction_smokeTest() {
assertThat(capability.appAction)
.isEqualTo(
AppAction.newBuilder()
@@ -120,6 +120,68 @@
}
@Test
+ fun appAction_computedProperty() {
+ val mutableEntityList = mutableListOf<
+ androidx.appactions.interaction.capabilities.core.properties.Entity
+ >()
+ val capability = createCapability<EmptyTaskUpdater>(
+ Properties.newBuilder()
+ .setRequiredEntityField(
+ Property.Builder<
+ androidx.appactions.interaction.capabilities.core.properties.Entity
+ >().setPossibleValueSupplier(
+ mutableEntityList::toList
+ ).build()
+ )
+ .build(),
+ sessionFactory =
+ {
+ object : ExecutionSession {
+ override fun onExecuteAsync(arguments: Arguments) =
+ Futures.immediateFuture(ExecutionResult.Builder<Output>().build())
+ }
+ },
+ sessionBridge = { TaskHandler.Builder<Confirmation>().build() },
+ sessionUpdaterSupplier = ::EmptyTaskUpdater,
+ )
+ mutableEntityList.add(
+ androidx.appactions.interaction.capabilities.core.properties.Entity.Builder()
+ .setName("entity1").build()
+ )
+
+ assertThat(capability.appAction).isEqualTo(
+ AppAction.newBuilder()
+ .setIdentifier("id")
+ .setName("actions.intent.TEST")
+ .addParams(
+ IntentParameter.newBuilder()
+ .setName("required")
+ .addPossibleEntities(Entity.newBuilder().setName("entity1"))
+ )
+ .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(true))
+ .build()
+ )
+
+ mutableEntityList.add(
+ androidx.appactions.interaction.capabilities.core.properties.Entity.Builder()
+ .setName("entity2").build()
+ )
+ assertThat(capability.appAction).isEqualTo(
+ AppAction.newBuilder()
+ .setIdentifier("id")
+ .setName("actions.intent.TEST")
+ .addParams(
+ IntentParameter.newBuilder()
+ .setName("required")
+ .addPossibleEntities(Entity.newBuilder().setName("entity1"))
+ .addPossibleEntities(Entity.newBuilder().setName("entity2"))
+ )
+ .setTaskInfo(TaskInfo.newBuilder().setSupportsPartialFulfillment(true))
+ .build()
+ )
+ }
+
+ @Test
fun capabilitySession_getUiHandle() {
val externalSession = object : ExecutionSession {}
val capability =
diff --git a/appactions/interaction/interaction-capabilities-testing/src/main/java/androidx/appactions/interaction/capabilities/testing/internal/TestingUtils.kt b/appactions/interaction/interaction-capabilities-testing/src/main/java/androidx/appactions/interaction/capabilities/testing/internal/TestingUtils.kt
index 46494c3..39c7d77 100644
--- a/appactions/interaction/interaction-capabilities-testing/src/main/java/androidx/appactions/interaction/capabilities/testing/internal/TestingUtils.kt
+++ b/appactions/interaction/interaction-capabilities-testing/src/main/java/androidx/appactions/interaction/capabilities/testing/internal/TestingUtils.kt
@@ -16,7 +16,7 @@
package androidx.appactions.interaction.capabilities.testing.internal
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutorAsync
+import androidx.appactions.interaction.capabilities.core.ExecutionCallbackAsync
import androidx.appactions.interaction.capabilities.core.ExecutionResult
import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
import kotlinx.coroutines.Deferred
@@ -30,9 +30,9 @@
// use this timeout for waiting an arbitrary period of time.
const val BLOCKING_TIMEOUT = 300L
- fun <ArgumentsT, OutputT> createFakeCapabilityExecutor():
- CapabilityExecutorAsync<ArgumentsT, OutputT> {
- return CapabilityExecutorAsync { _: ArgumentsT ->
+ fun <ArgumentsT, OutputT> createFakeExecutionCallback():
+ ExecutionCallbackAsync<ArgumentsT, OutputT> {
+ return ExecutionCallbackAsync { _: ArgumentsT ->
Futures.immediateFuture(
ExecutionResult.Builder<OutputT>().build()
)
diff --git a/appactions/interaction/interaction-service-proto/build.gradle b/appactions/interaction/interaction-service-proto/build.gradle
index 38a00154..884a1aa 100644
--- a/appactions/interaction/interaction-service-proto/build.gradle
+++ b/appactions/interaction/interaction-service-proto/build.gradle
@@ -47,6 +47,10 @@
compileOnly("androidx.annotation:annotation:1.1.0")
}
+jar {
+ exclude "**/*.proto"
+}
+
protobuf {
protoc {
artifact = libs.protobufCompiler.get()
diff --git a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiSessions.kt b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiSessions.kt
index 8c7a81f..1774fa6 100644
--- a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiSessions.kt
+++ b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/UiSessions.kt
@@ -18,7 +18,7 @@
package androidx.appactions.interaction.service
import androidx.annotation.GuardedBy
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
+import androidx.appactions.interaction.capabilities.core.ExecutionCallback
import androidx.appactions.interaction.capabilities.core.BaseExecutionSession
import androidx.appactions.interaction.capabilities.core.impl.UiHandleRegistry
import androidx.appactions.interaction.service.UiSessions.removeUiCache
@@ -70,8 +70,8 @@
UiHandleRegistry.getSessionIdFromUiHandle(this)!!
).updateUiInternal(uiResponse)
-/** Return a UI associated with this [CapabilityExecutor]. */
-fun CapabilityExecutor<*, *>.updateUi(uiResponse: UiResponse) =
+/** Return a UI associated with this [ExecutionCallback]. */
+fun ExecutionCallback<*, *>.updateUi(uiResponse: UiResponse) =
UiSessions.getOrCreateUiCache(
UiHandleRegistry.getSessionIdFromUiHandle(this)!!
).updateUiInternal(uiResponse)
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiSessionsTest.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiSessionsTest.kt
index e9e60c5..d72a575 100644
--- a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiSessionsTest.kt
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/UiSessionsTest.kt
@@ -19,7 +19,7 @@
import android.content.Context
import android.util.SizeF
import android.widget.RemoteViews
-import androidx.appactions.interaction.capabilities.core.CapabilityExecutor
+import androidx.appactions.interaction.capabilities.core.ExecutionCallback
import androidx.appactions.interaction.capabilities.core.ExecutionResult
import androidx.appactions.interaction.capabilities.core.HostProperties
import androidx.appactions.interaction.capabilities.core.ExecutionSessionFactory
@@ -252,11 +252,11 @@
}
@Test
- fun capabilityExecutor_hasUpdateUiExtension() {
+ fun executionCallback_hasUpdateUiExtension() {
assertThat(UiSessions.getUiCacheOrNull(sessionId)).isNull()
val oneShotCapability = FakeCapability.CapabilityBuilder().setId(
"oneShotCapability",
- ).setExecutor(object : CapabilityExecutor<Arguments, Output> {
+ ).setExecutionCallback(object : ExecutionCallback<Arguments, Output> {
override suspend fun onExecute(arguments: Arguments): ExecutionResult<Output> {
this.updateUi(remoteViewsUiResponse)
return ExecutionResult.Builder<Output>().build()
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
index bfb32b8..b48c66c 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
@@ -18,7 +18,6 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -56,6 +55,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
+import androidx.annotation.VisibleForTesting;
import androidx.appcompat.R;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.content.res.AppCompatResources;
@@ -1607,7 +1607,7 @@
* Returns the navigation button view.
*
*/
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
@Nullable
View getNavButtonView() {
return mNavButtonView;
@@ -2418,7 +2418,7 @@
/**
*/
- @RestrictTo(TESTS)
+ @VisibleForTesting
@Nullable
final TextView getTitleTextView() {
return mTitleTextView;
@@ -2426,7 +2426,7 @@
/**
*/
- @RestrictTo(TESTS)
+ @VisibleForTesting
@Nullable
final TextView getSubtitleTextView() {
return mSubtitleTextView;
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
index 8b9c181..81151eb 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
@@ -19,6 +19,7 @@
import android.os.Bundle
import android.util.Log
import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
import androidx.test.platform.app.InstrumentationRegistry
/**
@@ -26,13 +27,13 @@
*
* @hide
*/
-@RestrictTo(RestrictTo.Scope.TESTS)
+@VisibleForTesting
public var argumentSource: Bundle? = null
/**
* Allows tests to override profiler
*/
-@RestrictTo(RestrictTo.Scope.TESTS)
+@VisibleForTesting
internal var profilerOverride: Profiler? = null
/**
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt
index 9e372d7..7028319 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt
@@ -20,7 +20,7 @@
import android.os.Debug
import android.util.Log
import androidx.annotation.RequiresApi
-import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
import androidx.benchmark.BenchmarkState.Companion.TAG
import androidx.benchmark.Outputs.dateToFileName
import androidx.benchmark.simpleperf.ProfileSession
@@ -132,7 +132,7 @@
}
internal object StackSamplingLegacy : Profiler() {
- @get:RestrictTo(RestrictTo.Scope.TESTS)
+ @get:VisibleForTesting
var isRunning = false
override fun start(traceUniqueName: String): ResultFile {
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/FrameTimingQueryTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/FrameTimingQueryTest.kt
index 66ed720..6dfc931 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/FrameTimingQueryTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/FrameTimingQueryTest.kt
@@ -20,6 +20,7 @@
import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric.FrameDurationCpuNs
import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric.FrameDurationUiNs
import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric.FrameOverrunNs
+import androidx.benchmark.macro.perfetto.FrameTimingQuery.getFrameSubMetrics
import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
import androidx.benchmark.perfetto.PerfettoTraceProcessor
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -40,11 +41,11 @@
val frameSubMetrics = PerfettoTraceProcessor.runSingleSessionServer(
traceFile.absolutePath
) {
- FrameTimingQuery.getFrameSubMetrics(
+ FrameTimingQuery.getFrameData(
session = this,
captureApiLevel = 28,
packageName = "androidx.benchmark.integration.macrobenchmark.target"
- )
+ ).getFrameSubMetrics(captureApiLevel = 28)
}
assertEquals(
@@ -72,11 +73,11 @@
val frameSubMetrics = PerfettoTraceProcessor.runSingleSessionServer(
traceFile.absolutePath
) {
- FrameTimingQuery.getFrameSubMetrics(
+ FrameTimingQuery.getFrameData(
session = this,
captureApiLevel = 31,
packageName = "androidx.benchmark.integration.macrobenchmark.target"
- )
+ ).getFrameSubMetrics(captureApiLevel = 31)
}
assertEquals(
@@ -95,4 +96,61 @@
message = "Expect same number of frames for each metric"
)
}
+
+ /**
+ * This validates that we're able to see all frames, even if expected/actual frame IDs don't
+ * match UI thread and Renderthread (b/279088460)
+ */
+ @MediumTest
+ @Test
+ fun fixedTrace33_mismatchExpectedActualFrameIds() {
+ assumeTrue(isAbiSupported())
+ val traceFile =
+ createTempFileFromAsset("api33_motionlayout_messagejson", ".perfetto-trace")
+
+ val frameData = PerfettoTraceProcessor.runSingleSessionServer(
+ traceFile.absolutePath
+ ) {
+ FrameTimingQuery.getFrameData(
+ session = this,
+ captureApiLevel = 33,
+ packageName = "androidx.constraintlayout.compose.integration.macrobenchmark.target"
+ )
+ }
+
+ // although there are 58 frames in the trace, the last 4
+ // don't have associated complete expected/actual events
+ assertEquals(54, frameData.size)
+
+ // first frame, with matching IDs
+ frameData.single {
+ it.rtSlice.frameId == 1370854
+ }.run {
+ assertEquals(1370854, this.uiSlice.frameId)
+ assertEquals(1370854, this.expectedSlice!!.frameId)
+ assertEquals(1370854, this.actualSlice!!.frameId)
+ }
+
+ // second frame, where IDs don't match
+ frameData.single {
+ it.rtSlice.frameId == 1370869
+ }.run {
+ assertEquals(1370869, this.uiSlice.frameId) // matches
+ assertEquals(1370876, this.expectedSlice!!.frameId) // doesn't match!
+ assertEquals(1370876, this.actualSlice!!.frameId) // doesn't match!
+ }
+
+ assertEquals(
+ // Note: it's correct for UI to be > CPU in cases below,
+ // since UI is be sleeping after RT is done
+ expected = mapOf(
+ FrameDurationCpuNs to listOf(7304479L, 7567188L, 8064897L, 8434115L),
+ FrameDurationUiNs to listOf(4253646L, 7592761L, 8088855L, 8461876L),
+ FrameOverrunNs to listOf(-9009770L, -12199949L, -11299378L, -11708522L)
+ ),
+ actual = frameData.getFrameSubMetrics(captureApiLevel = 33).mapValues {
+ it.value.subList(0, 4)
+ }
+ )
+ }
}
\ No newline at end of file
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
index f60a946..8a73eea 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
@@ -101,8 +101,8 @@
val output = Shell.executeScriptCaptureStdout(
"cmd package compile --reset $packageName"
)
- check(output.trim() == "Success") {
- "Unable to recompile $packageName ($output)"
+ check(output.trim() == "Success" || output.contains("PERFORMED")) {
+ "Unable to recompile $packageName (out=$output)"
}
} else {
// User builds pre-U. Kick off a full uninstall-reinstall
@@ -154,8 +154,8 @@
// correctly installed. (b/231294733)
output = Shell.executeScriptCaptureStdout("pm install -t $tempApkPathsString")
- check(output.trim() == "Success") {
- "Unable to install $packageName ($output)"
+ check(output.trim() == "Success" || output.contains("PERFORMED")) {
+ "Unable to install $packageName (out=$output)"
}
} finally {
// Cleanup the temporary APK
@@ -433,7 +433,9 @@
val stdout = Shell.executeScriptCaptureStdout(
"cmd package compile -f -m $compileArgument $packageName"
)
- check(stdout.trim() == "Success")
+ check(stdout.trim() == "Success" || stdout.contains("PERFORMED")) {
+ "Failed to compile (out=$stdout)"
+ }
}
}
}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
index 6c6c458..36241de 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -28,6 +28,7 @@
import androidx.benchmark.macro.perfetto.BatteryDischargeQuery
import androidx.benchmark.macro.perfetto.FrameTimingQuery
import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric
+import androidx.benchmark.macro.perfetto.FrameTimingQuery.getFrameSubMetrics
import androidx.benchmark.macro.perfetto.MemoryCountersQuery
import androidx.benchmark.macro.perfetto.PowerQuery
import androidx.benchmark.macro.perfetto.StartupTimingQuery
@@ -193,11 +194,12 @@
captureInfo: CaptureInfo,
traceSession: PerfettoTraceProcessor.Session
): List<Measurement> {
- return FrameTimingQuery.getFrameSubMetrics(
+ return FrameTimingQuery.getFrameData(
session = traceSession,
captureApiLevel = Build.VERSION.SDK_INT,
packageName = captureInfo.targetPackageName
)
+ .getFrameSubMetrics(Build.VERSION.SDK_INT)
.filterKeys { it == SubMetric.FrameDurationCpuNs || it == SubMetric.FrameOverrunNs }
.map {
Measurement(
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
index ac49d29..d0446bc 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/FrameTimingQuery.kt
@@ -83,7 +83,7 @@
*
* Nullable slices are always present on API 31+
*/
- private class FrameData(
+ internal class FrameData(
val uiSlice: Slice,
val rtSlice: Slice,
val expectedSlice: Slice?,
@@ -93,7 +93,10 @@
return when (subMetric) {
SubMetric.FrameDurationCpuNs -> rtSlice.endTs - uiSlice.ts
SubMetric.FrameDurationUiNs -> uiSlice.dur
- SubMetric.FrameOverrunNs -> actualSlice!!.endTs - expectedSlice!!.endTs
+ SubMetric.FrameOverrunNs -> {
+ // workaround b/279088460, where actual slice ends too early
+ maxOf(actualSlice!!.endTs, rtSlice.endTs) - expectedSlice!!.endTs
+ }
}
}
companion object {
@@ -141,11 +144,11 @@
}
}
- fun getFrameSubMetrics(
+ internal fun getFrameData(
session: PerfettoTraceProcessor.Session,
captureApiLevel: Int,
packageName: String,
- ): Map<SubMetric, List<Long>> {
+ ): List<FrameData> {
val queryResultIterator = session.query(
query = getFullQuery(packageName)
)
@@ -173,7 +176,7 @@
val expectedSlices = groupedData.getOrElse(FrameSliceType.Expected) { listOf() }
if (uiSlices.isEmpty()) {
- return emptyMap()
+ return emptyList()
}
// check data looks reasonable
@@ -181,17 +184,40 @@
require(actualSlices.isEmpty() == newSlicesShouldBeEmpty)
require(expectedSlices.isEmpty() == newSlicesShouldBeEmpty)
- val frameData = if (captureApiLevel >= 31) {
+ return if (captureApiLevel >= 31) {
// No slice should be missing a frameId
require(slices.none { it.frameId == null })
+
+ val actualSlicesPool = actualSlices.toMutableList()
rtSlices.mapNotNull { rtSlice ->
val frameId = rtSlice.frameId!!
- FrameData.tryCreate31(
- uiSlice = uiSlices.binarySearchFrameId(frameId),
- rtSlice = rtSlice,
- expectedSlice = expectedSlices.binarySearchFrameId(frameId),
- actualSlice = actualSlices.binarySearchFrameId(frameId)
- )
+
+ val uiSlice = uiSlices.binarySearchFrameId(frameId)
+
+ // Ideally, we'd rely on frameIds, but these can fall out of sync due to b/279088460
+ // expectedSlice = expectedSlices.binarySearchFrameId(frameId),
+ // actualSlice = actualSlices.binarySearchFrameId(frameId)
+ // A pool of actual slices is used to prevent incorrect duplicate mapping. At the
+ // end of the trace, the synthetic expect/actual slices may be missing even if
+ // the complete end of frame is present, and we want to discard those. This
+ // doesn't happen at front of trace, since we find actuals from the end.
+ if (uiSlice != null) {
+ // Use fixed offset since synthetic tracepoint for actual may start after the
+ // actual UI slice (have observed 2us in practice)
+ val actualSlice = actualSlicesPool.lastOrNull { it.ts < uiSlice.ts + 50_000 }
+ actualSlicesPool.remove(actualSlice)
+ val expectedSlice = actualSlice?.frameId?.run {
+ expectedSlices.binarySearchFrameId(this)
+ }
+ FrameData.tryCreate31(
+ uiSlice = uiSlice,
+ rtSlice = rtSlice,
+ expectedSlice = expectedSlice,
+ actualSlice = actualSlice
+ )
+ } else {
+ null
+ }
}
} else {
require(slices.none { it.frameId != null })
@@ -202,11 +228,13 @@
)
}
}
+ }
+ fun List<FrameData>.getFrameSubMetrics(captureApiLevel: Int): Map<SubMetric, List<Long>> {
return SubMetric.values()
.filter { it.supportedOnApiLevel(captureApiLevel) }
.associateWith { subMetric ->
- frameData.map { frame -> frame.get(subMetric) }
+ map { frame -> frame.get(subMetric) }
}
}
}
\ No newline at end of file
diff --git a/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh b/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh
index b33734b..cc07a21 100755
--- a/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh
+++ b/benchmark/gradle-plugin/src/main/resources/scripts/lockClocks.sh
@@ -381,7 +381,7 @@
function_lock_cpu
-if [ "$DEVICE" -ne "wembley" ]; then
+if [ ${DEVICE} != "wembley" ]; then
function_lock_gpu_kgsl
else
echo "Unable to lock gpu clocks of $MODEL ($DEVICE)."
diff --git a/biometric/biometric/src/main/res/values-am/strings.xml b/biometric/biometric/src/main/res/values-am/strings.xml
index a32005d..28d2673 100644
--- a/biometric/biometric/src/main/res/values-am/strings.xml
+++ b/biometric/biometric/src/main/res/values-am/strings.xml
@@ -33,15 +33,15 @@
<string name="use_fingerprint_label" msgid="6961788485681412417">"የጣት አሻራን ተጠቀም"</string>
<string name="use_face_label" msgid="6533512708069459542">"መልክን ተጠቀም"</string>
<string name="use_biometric_label" msgid="6524145989441579428">"ባዮሜትሪኮችን ተጠቀም"</string>
- <string name="use_screen_lock_label" msgid="5459869335976243512">"የማያ ገጽ መቆለፊያን ተጠቀም"</string>
- <string name="use_fingerprint_or_screen_lock_label" msgid="7577690399303139443">"የጣት አሻራን ወይም የማያ ገጽ መቆለፊያን ይጠቀሙ"</string>
- <string name="use_face_or_screen_lock_label" msgid="2116180187159450292">"የመልክ ወይም የማያ ገጽ መቆለፊያን ተጠቀም"</string>
- <string name="use_biometric_or_screen_lock_label" msgid="5385448280139639016">"ባዮሜትሪኮችን ወይም ማያ ገጽ መቆለፊያን ይጠቀሙ"</string>
+ <string name="use_screen_lock_label" msgid="5459869335976243512">"የማያ ገፅ መቆለፊያን ተጠቀም"</string>
+ <string name="use_fingerprint_or_screen_lock_label" msgid="7577690399303139443">"የጣት አሻራን ወይም የማያ ገፅ መቆለፊያን ይጠቀሙ"</string>
+ <string name="use_face_or_screen_lock_label" msgid="2116180187159450292">"የመልክ ወይም የማያ ገፅ መቆለፊያን ተጠቀም"</string>
+ <string name="use_biometric_or_screen_lock_label" msgid="5385448280139639016">"ባዮሜትሪኮችን ወይም ማያ ገፅ መቆለፊያን ይጠቀሙ"</string>
<string name="fingerprint_prompt_message" msgid="7449360011861769080">"ለመቀጠል የእርስዎን የጣት አሻራ ይጠቀሙ"</string>
<string name="face_prompt_message" msgid="2282389249605674226">"ለመቀጠል የእርስዎን መልክ ይጠቀሙ"</string>
<string name="biometric_prompt_message" msgid="1160635338192065472">"ለመቀጠል የእርስዎን ባዮሜትሪክ ይጠቀሙ"</string>
- <string name="screen_lock_prompt_message" msgid="5659570757430909869">"ለመቀጠል የእርስዎን የማያ ገጽ ቁልፍ ያስገቡ"</string>
- <string name="fingerprint_or_screen_lock_prompt_message" msgid="8382576858490514495">"ለመቀጠል የእርስዎን የጣት አሻራ ወይም የማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
- <string name="face_or_screen_lock_prompt_message" msgid="4562557128765735254">"ለመቀጠል የእርስዎን መልክ ወይም የማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
- <string name="biometric_or_screen_lock_prompt_message" msgid="2102429900219199821">"ለመቀጠል የእርስዎን የባዮሜትሪክ ወይም የማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
+ <string name="screen_lock_prompt_message" msgid="5659570757430909869">"ለመቀጠል የእርስዎን የማያ ገፅ ቁልፍ ያስገቡ"</string>
+ <string name="fingerprint_or_screen_lock_prompt_message" msgid="8382576858490514495">"ለመቀጠል የእርስዎን የጣት አሻራ ወይም የማያ ገፅ መቆለፊያ ይጠቀሙ"</string>
+ <string name="face_or_screen_lock_prompt_message" msgid="4562557128765735254">"ለመቀጠል የእርስዎን መልክ ወይም የማያ ገፅ መቆለፊያ ይጠቀሙ"</string>
+ <string name="biometric_or_screen_lock_prompt_message" msgid="2102429900219199821">"ለመቀጠል የእርስዎን የባዮሜትሪክ ወይም የማያ ገፅ መቆለፊያ ይጠቀሙ"</string>
</resources>
diff --git a/biometric/biometric/src/main/res/values-ky/strings.xml b/biometric/biometric/src/main/res/values-ky/strings.xml
index 4c50879..97be746 100644
--- a/biometric/biometric/src/main/res/values-ky/strings.xml
+++ b/biometric/biometric/src/main/res/values-ky/strings.xml
@@ -32,7 +32,7 @@
<string name="fingerprint_dialog_icon_description" msgid="5462024216548165325">"Манжа изинин сүрөтчөсү"</string>
<string name="use_fingerprint_label" msgid="6961788485681412417">"Манжа изин колдонуу"</string>
<string name="use_face_label" msgid="6533512708069459542">"Жүзүнөн таанып ачууну колдонуу"</string>
- <string name="use_biometric_label" msgid="6524145989441579428">"Биометрикалык жөндөөлөрдү колдонуу"</string>
+ <string name="use_biometric_label" msgid="6524145989441579428">"Биометрикалык параметрлерди колдонуу"</string>
<string name="use_screen_lock_label" msgid="5459869335976243512">"Экран кулпусун колдонуу"</string>
<string name="use_fingerprint_or_screen_lock_label" msgid="7577690399303139443">"Манжа изин же экрандын кулпусун колдонуу"</string>
<string name="use_face_or_screen_lock_label" msgid="2116180187159450292">"Жүзүнөн таанып ачууну же экрандын кулпусун колдонуу"</string>
diff --git a/biometric/biometric/src/main/res/values-zh-rHK/strings.xml b/biometric/biometric/src/main/res/values-zh-rHK/strings.xml
index c732b33..1be8ed3 100644
--- a/biometric/biometric/src/main/res/values-zh-rHK/strings.xml
+++ b/biometric/biometric/src/main/res/values-zh-rHK/strings.xml
@@ -38,7 +38,7 @@
<string name="use_face_or_screen_lock_label" msgid="2116180187159450292">"使用面孔或螢幕鎖定"</string>
<string name="use_biometric_or_screen_lock_label" msgid="5385448280139639016">"使用生物識別或螢幕鎖定"</string>
<string name="fingerprint_prompt_message" msgid="7449360011861769080">"請使用指紋驗證身分,才能繼續操作"</string>
- <string name="face_prompt_message" msgid="2282389249605674226">"請使用您的面孔驗證身分,才能繼續操作"</string>
+ <string name="face_prompt_message" msgid="2282389249605674226">"請使用你的面孔驗證身分,才能繼續操作"</string>
<string name="biometric_prompt_message" msgid="1160635338192065472">"請使用使用生物識別驗證身分,才能繼續操作"</string>
<string name="screen_lock_prompt_message" msgid="5659570757430909869">"請輸入螢幕鎖定解鎖憑證,才能繼續操作"</string>
<string name="fingerprint_or_screen_lock_prompt_message" msgid="8382576858490514495">"請使用指紋解鎖或螢幕鎖定功能驗證身分,才能繼續操作"</string>
diff --git a/bluetooth/integration-tests/testapp/build.gradle b/bluetooth/integration-tests/testapp/build.gradle
index ba207e7..55523d6 100644
--- a/bluetooth/integration-tests/testapp/build.gradle
+++ b/bluetooth/integration-tests/testapp/build.gradle
@@ -46,7 +46,7 @@
implementation(libs.kotlinStdlib)
implementation(project(":bluetooth:bluetooth"))
- implementation("androidx.activity:activity-ktx:1.7.0")
+ implementation("androidx.activity:activity-ktx:1.7.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation(libs.constraintLayout)
implementation("androidx.core:core-ktx:1.10.0")
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/advertiser/AdvertiserFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/advertiser/AdvertiserFragment.kt
index 73c1935..e5f71ad 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/advertiser/AdvertiserFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/advertiser/AdvertiserFragment.kt
@@ -30,6 +30,7 @@
import androidx.bluetooth.BluetoothLe
import androidx.bluetooth.integration.testapp.R
import androidx.bluetooth.integration.testapp.databinding.FragmentAdvertiserBinding
+import androidx.bluetooth.integration.testapp.ui.common.getColor
import androidx.bluetooth.integration.testapp.ui.common.setViewEditText
import androidx.bluetooth.integration.testapp.ui.common.toast
import androidx.core.view.isVisible
@@ -63,12 +64,19 @@
field = value
if (value) {
_binding?.buttonAdvertise?.text = getString(R.string.stop_advertising)
+ _binding?.buttonAdvertise?.backgroundTintList = getColor(R.color.red_500)
} else {
_binding?.buttonAdvertise?.text = getString(R.string.start_advertising)
+ _binding?.buttonAdvertise?.backgroundTintList = getColor(R.color.indigo_500)
advertiseJob?.cancel()
advertiseJob = null
}
- _binding?.viewOverlay?.isVisible = value
+ _binding?.textInputEditTextDisplayName?.isEnabled = !value
+ _binding?.checkBoxIncludeDeviceName?.isEnabled = !value
+ _binding?.checkBoxConnectable?.isEnabled = !value
+ _binding?.checkBoxDiscoverable?.isEnabled = !value
+ _binding?.buttonAddData?.isEnabled = !value
+ _binding?.viewRecyclerViewOverlay?.isVisible = value
}
private var _binding: FragmentAdvertiserBinding? = null
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/common/Fragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/common/Fragment.kt
index 395bd0d7..aaa4232 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/common/Fragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/common/Fragment.kt
@@ -16,9 +16,15 @@
package androidx.bluetooth.integration.testapp.ui.common
+import android.content.res.ColorStateList
import android.widget.Toast
+import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
+fun Fragment.getColor(color: Int): ColorStateList? {
+ return ContextCompat.getColorStateList(requireContext(), color)
+}
+
fun Fragment.toast(msg: String): Toast {
return Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT)
}
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/DeviceServicesAdapter.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/DeviceServicesAdapter.kt
new file mode 100644
index 0000000..8e5ab73
--- /dev/null
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/DeviceServicesAdapter.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.bluetooth.integration.testapp.ui.scanner
+
+// TODO(ofy) Migrate to androidx.bluetooth.BluetoothGattService once in place
+import android.bluetooth.BluetoothGattService
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.bluetooth.integration.testapp.R
+import androidx.recyclerview.widget.RecyclerView
+
+class DeviceServicesAdapter(var services: List<BluetoothGattService>) :
+ RecyclerView.Adapter<DeviceServicesAdapter.ViewHolder>() {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_device_service, parent, false)
+ return ViewHolder(view)
+ }
+
+ override fun getItemCount(): Int {
+ return services.size
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val service = services[position]
+ holder.bind(service)
+ }
+
+ inner class ViewHolder(itemView: View) :
+ RecyclerView.ViewHolder(itemView) {
+
+ private val textViewUuid: TextView = itemView.findViewById(R.id.text_view_uuid)
+
+ fun bind(service: BluetoothGattService) {
+ textViewUuid.text = service.uuid.toString()
+ }
+ }
+}
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerAdapter.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerAdapter.kt
index c227ab5..7641f8a 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerAdapter.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerAdapter.kt
@@ -25,6 +25,7 @@
import android.widget.TextView
import androidx.bluetooth.integration.testapp.R
+import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@@ -62,8 +63,9 @@
@SuppressLint("MissingPermission")
fun bind(scanResult: ScanResult) {
currentScanResult = scanResult
- textViewDeviceName.text = scanResult.device.name
textViewDeviceAddress.text = scanResult.device.address
+ textViewDeviceName.text = scanResult.device.name
+ textViewDeviceName.isVisible = scanResult.device.name.isNullOrEmpty().not()
}
}
}
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 7411f0a..76d6330 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
@@ -23,26 +23,36 @@
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.TextView
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
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
-// TODO(ofy) Migrate to androidx.bluetooth.BluetoothLe once scan API is in place
-import androidx.bluetooth.integration.testapp.experimental.BluetoothLe
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProvider
+import com.google.android.material.tabs.TabLayout
+import com.google.android.material.tabs.TabLayout.Tab
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
+import java.lang.Exception
class ScannerFragment : Fragment() {
private companion object {
private const val TAG = "ScannerFragment"
+
+ private const val TAB_RESULTS_POSITION = 0
}
private lateinit var scannerViewModel: ScannerViewModel
@@ -52,20 +62,50 @@
private var scannerAdapter: ScannerAdapter? = null
+ private var deviceServicesAdapter: DeviceServicesAdapter? = null
+
private val scanScope = CoroutineScope(Dispatchers.Main + Job())
private var scanJob: Job? = null
+ private val connectScope = CoroutineScope(Dispatchers.Default + Job())
+ private var connectJob: Job? = null
+
private var isScanning: Boolean = false
set(value) {
field = value
if (value) {
_binding?.buttonScan?.text = getString(R.string.stop_scanning)
+ _binding?.buttonScan?.backgroundTintList = getColor(R.color.red_500)
} else {
_binding?.buttonScan?.text = getString(R.string.start_scanning)
+ _binding?.buttonScan?.backgroundTintList = getColor(R.color.indigo_500)
scanJob?.cancel()
+ scanJob = null
}
}
+ private var showingScanResults: Boolean = false
+ set(value) {
+ field = value
+ _binding?.relativeLayoutScanResults?.isVisible = value
+ _binding?.linearLayoutDevice?.isVisible = !value
+ }
+
+ private val onTabSelectedListener = object : TabLayout.OnTabSelectedListener {
+ override fun onTabSelected(tab: Tab) {
+ showingScanResults = tab.position == TAB_RESULTS_POSITION
+ if (tab.position != TAB_RESULTS_POSITION) {
+ updateDeviceUI(scannerViewModel.deviceConnection(tab.position))
+ }
+ }
+
+ override fun onTabUnselected(tab: Tab) {
+ }
+
+ override fun onTabReselected(tab: Tab) {
+ }
+ }
+
private var _binding: FragmentScannerBinding? = null
// This property is only valid between onCreateView and onDestroyView.
@@ -82,12 +122,20 @@
_binding = FragmentScannerBinding.inflate(inflater, container, false)
+ binding.tabLayout.addOnTabSelectedListener(onTabSelectedListener)
+
scannerAdapter = ScannerAdapter { scanResult -> onClickScanResult(scanResult) }
binding.recyclerViewScanResults.adapter = scannerAdapter
binding.recyclerViewScanResults.addItemDecoration(
DividerItemDecoration(context, LinearLayoutManager.VERTICAL)
)
+ deviceServicesAdapter = DeviceServicesAdapter(emptyList())
+ binding.recyclerViewDeviceServices.adapter = deviceServicesAdapter
+ binding.recyclerViewDeviceServices.addItemDecoration(
+ DividerItemDecoration(context, LinearLayoutManager.VERTICAL)
+ )
+
initData()
binding.buttonScan.setOnClickListener {
@@ -105,11 +153,15 @@
super.onDestroyView()
_binding = null
isScanning = false
+ scanJob?.cancel()
+ scanJob = null
}
private fun initData() {
scannerAdapter?.submitList(scannerViewModel.results)
scannerAdapter?.notifyItemRangeChanged(0, scannerViewModel.results.size)
+
+ scannerViewModel.devices.map { it.scanResult }.forEach(::addNewTab)
}
private fun startScan() {
@@ -133,6 +185,96 @@
}
private fun onClickScanResult(scanResult: ScanResult) {
- Log.d(TAG, "onClickScanResult() called with: scanResult = $scanResult")
+ isScanning = false
+
+ val index = scannerViewModel.addDeviceConnectionIfNew(scanResult)
+
+ val deviceTab = if (index == ScannerViewModel.NEW_DEVICE) {
+ addNewTab(scanResult)
+ } else {
+ binding.tabLayout.getTabAt(index)
+ }
+
+ // To prevent TabSelectedListener being triggered when a tab is promatically selected.
+ binding.tabLayout.removeOnTabSelectedListener(onTabSelectedListener)
+ binding.tabLayout.selectTab(deviceTab)
+ binding.tabLayout.addOnTabSelectedListener(onTabSelectedListener)
+
+ showingScanResults = false
+
+ connectTo(scannerViewModel.deviceConnection(binding.tabLayout.selectedTabPosition))
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun addNewTab(scanResult: ScanResult): Tab {
+ val deviceAddress = scanResult.device.address
+ val deviceName = scanResult.device.name
+
+ val newTab = binding.tabLayout.newTab()
+ newTab.setCustomView(R.layout.tab_item_device)
+
+ val customView = newTab.customView
+ customView?.findViewById<TextView>(R.id.text_view_address)?.text = deviceAddress
+ customView?.findViewById<TextView>(R.id.text_view_name)?.text = deviceName
+
+ binding.tabLayout.addTab(newTab)
+ return newTab
+ }
+
+ private fun connectTo(deviceConnection: DeviceConnection) {
+ Log.d(TAG, "connectTo() called with: deviceConnection = $deviceConnection")
+
+ connectJob = connectScope.launch {
+ deviceConnection.status = Status.CONNECTING
+ launch(Dispatchers.Main) {
+ updateDeviceUI(deviceConnection)
+ }
+
+ try {
+ bluetoothLe.connectGatt(requireContext(), deviceConnection.scanResult.device) {
+ Log.d(TAG, "connectGatt result. getServices() = ${getServices()}")
+
+ deviceConnection.status = Status.CONNECTED
+ deviceConnection.services = getServices()
+ launch(Dispatchers.Main) {
+ updateDeviceUI(deviceConnection)
+ }
+ }
+ } catch (exception: Exception) {
+ Log.e(TAG, "connectTo: exception", exception)
+
+ deviceConnection.status = Status.CONNECTION_FAILED
+ launch(Dispatchers.Main) {
+ updateDeviceUI(deviceConnection)
+ }
+ }
+ }
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private fun updateDeviceUI(deviceConnection: DeviceConnection) {
+ binding.progressIndicatorDeviceConnection.isVisible = false
+
+ when (deviceConnection.status) {
+ Status.NOT_CONNECTED -> {
+ binding.textViewDeviceConnectionStatus.text = getString(R.string.not_connected)
+ binding.textViewDeviceConnectionStatus.setTextColor(getColor(R.color.green_500))
+ }
+ Status.CONNECTING -> {
+ binding.progressIndicatorDeviceConnection.isVisible = true
+ binding.textViewDeviceConnectionStatus.text = getString(R.string.connecting)
+ binding.textViewDeviceConnectionStatus.setTextColor(getColor(R.color.indigo_500))
+ }
+ Status.CONNECTED -> {
+ binding.textViewDeviceConnectionStatus.text = getString(R.string.connected)
+ binding.textViewDeviceConnectionStatus.setTextColor(getColor(R.color.indigo_500))
+ }
+ Status.CONNECTION_FAILED -> {
+ binding.textViewDeviceConnectionStatus.text = getString(R.string.connection_failed)
+ binding.textViewDeviceConnectionStatus.setTextColor(getColor(R.color.red_500))
+ }
+ }
+ deviceServicesAdapter?.services = deviceConnection.services
+ deviceServicesAdapter?.notifyDataSetChanged()
}
}
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerViewModel.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerViewModel.kt
index 7d213fd..518fe42 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerViewModel.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerViewModel.kt
@@ -16,24 +16,60 @@
package androidx.bluetooth.integration.testapp.ui.scanner
+// TODO(ofy) Migrate to androidx.bluetooth.ScanResult once in place
+import android.bluetooth.BluetoothGattService
import android.bluetooth.le.ScanResult
import androidx.lifecycle.ViewModel
class ScannerViewModel : ViewModel() {
- private companion object {
+ internal companion object {
private const val TAG = "ScannerViewModel"
+
+ internal const val NEW_DEVICE = -1
}
internal val results: List<ScanResult> get() = _results.values.toList()
-
private val _results = mutableMapOf<String, ScanResult>()
+ internal val devices: Set<DeviceConnection> get() = _devices
+ private val _devices = mutableSetOf<DeviceConnection>()
+
fun addScanResultIfNew(scanResult: ScanResult): Boolean {
val deviceAddress = scanResult.device.address
- if (_results.containsKey(deviceAddress)) return false
+ if (_results.containsKey(deviceAddress)) {
+ return false
+ }
+
_results[deviceAddress] = scanResult
return true
}
+
+ fun addDeviceConnectionIfNew(scanResult: ScanResult): Int {
+ val deviceConnection = DeviceConnection(scanResult)
+
+ val indexOf = _devices.map { it.scanResult }.indexOf(scanResult)
+ if (indexOf != -1) {
+ // Index 0 is Results page; Tabs for devices start from 1.
+ return indexOf + 1
+ }
+
+ _devices.add(deviceConnection)
+ return NEW_DEVICE
+ }
+
+ fun deviceConnection(position: Int): DeviceConnection {
+ // Index 0 is Results page; Tabs for devices start from 1.
+ return devices.elementAt(position - 1)
+ }
+}
+
+class DeviceConnection(val scanResult: ScanResult) {
+ var status = Status.NOT_CONNECTED
+ var services = emptyList<BluetoothGattService>()
+}
+
+enum class Status {
+ NOT_CONNECTED, CONNECTING, CONNECTED, CONNECTION_FAILED
}
diff --git a/bluetooth/integration-tests/testapp/src/main/res/drawable/baseline_add_24.xml b/bluetooth/integration-tests/testapp/src/main/res/drawable/baseline_add_24.xml
new file mode 100644
index 0000000..89633bb
--- /dev/null
+++ b/bluetooth/integration-tests/testapp/src/main/res/drawable/baseline_add_24.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+ android:viewportHeight="24" android:viewportWidth="24"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_advertiser.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_advertiser.xml
index c6e37d3..014dc75 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_advertiser.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_advertiser.xml
@@ -88,12 +88,12 @@
<Button
android:id="@+id/button_add_data"
- style="@style/Widget.AppCompat.Button.Borderless"
+ style="@style/Widget.MaterialComponents.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/add_data"
- android:textColor="@color/purple_500"
+ app:icon="@drawable/baseline_add_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_view_advertising_data" />
@@ -108,12 +108,13 @@
tools:listitem="@layout/item_advertiser_data" />
<View
- android:id="@+id/view_overlay"
+ android:id="@+id/view_recycler_view_overlay"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@color/black_alpha_50"
+ android:layout_height="0dp"
android:clickable="true"
- android:visibility="gone" />
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/button_add_data" />
<Button
android:id="@+id/button_advertise"
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_scanner.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_scanner.xml
index e27e808..9cf7c3484 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_scanner.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_scanner.xml
@@ -21,21 +21,97 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/recycler_view_scan_results"
+ <com.google.android.material.tabs.TabLayout
+ android:id="@+id/tab_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:layoutManager="LinearLayoutManager"
- tools:itemCount="3"
- tools:listitem="@layout/item_scan_result" />
-
- <Button
- android:id="@+id/button_scan"
- android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:text="@string/start_scanning"
+ app:layout_constraintTop_toTopOf="parent"
+ app:tabGravity="start"
+ app:tabMode="scrollable">
+
+ <com.google.android.material.tabs.TabItem
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/scan_results" />
+
+ </com.google.android.material.tabs.TabLayout>
+
+ <RelativeLayout
+ android:id="@+id/relative_layout_scan_results"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent" />
+ app:layout_constraintTop_toBottomOf="@+id/tab_layout"
+ tools:visibility="gone">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/recycler_view_scan_results"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layoutManager="LinearLayoutManager"
+ tools:itemCount="3"
+ tools:listitem="@layout/item_scan_result" />
+
+ <Button
+ android:id="@+id/button_scan"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentBottom="true"
+ android:layout_margin="16dp"
+ android:text="@string/start_scanning" />
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/linear_layout_device"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="vertical"
+ android:padding="16dp"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/tab_layout"
+ tools:visibility="visible">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/text_view_device_connection_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:text="@string/not_connected"
+ android:textAllCaps="true"
+ android:textColor="@color/green_500"
+ android:textSize="16sp" />
+
+ <com.google.android.material.progressindicator.CircularProgressIndicator
+ android:id="@+id/progress_indicator_device_connection"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ android:visibility="gone"
+ app:indicatorSize="24dp" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/dividerVertical" />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/recycler_view_device_services"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layoutManager="LinearLayoutManager"
+ tools:itemCount="3"
+ tools:listitem="@layout/item_device_service" />
+
+ </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/item_device_service.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/item_device_service.xml
new file mode 100644
index 0000000..cb091804
--- /dev/null
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/item_device_service.xml
@@ -0,0 +1,42 @@
+<?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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="8dp">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/generic_attribute"
+ android:textColor="#000000" />
+
+ <TextView
+ android:id="@+id/text_view_uuid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:text="UUID: 0x1800" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/primary_service"
+ android:textAllCaps="true" />
+
+</LinearLayout>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/item_scan_result.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/item_scan_result.xml
index 649c4e4..fa2a9bc 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/layout/item_scan_result.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/item_scan_result.xml
@@ -1,4 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?><!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--
Copyright 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +15,7 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -38,24 +40,25 @@
android:paddingEnd="8dp">
<TextView
- android:id="@+id/text_view_device_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="#000000"
- tools:text="Omer S23 Ultra" />
-
- <TextView
android:id="@+id/text_view_device_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:textColor="@color/black"
tools:text="AB:CD:12:34:56:78" />
+ <TextView
+ android:id="@+id/text_view_device_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:text="Omer S23 Ultra" />
+
</LinearLayout>
<Button
android:id="@+id/button_connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/connect" />
+ android:text="@string/connect"
+ app:backgroundTint="@color/green_500" />
</LinearLayout>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/tab_item_device.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/tab_item_device.xml
new file mode 100644
index 0000000..23880a4
--- /dev/null
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/tab_item_device.xml
@@ -0,0 +1,36 @@
+<?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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/text_view_address"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black"
+ tools:text="70:04:13:03:98:B9" />
+
+ <TextView
+ android:id="@+id/text_view_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ tools:text="R33-0473" />
+
+</LinearLayout>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/values/colors.xml b/bluetooth/integration-tests/testapp/src/main/res/values/colors.xml
index a7e6ba3..2b48616 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/values/colors.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/values/colors.xml
@@ -15,12 +15,10 @@
limitations under the License.
-->
<resources>
- <color name="purple_200">#FFBB86FC</color>
- <color name="purple_500">#FF6200EE</color>
- <color name="purple_700">#FF3700B3</color>
- <color name="teal_200">#FF03DAC5</color>
- <color name="teal_700">#FF018786</color>
+ <color name="white">#FFFFFF</color>
+ <color name="indigo_500">#3F51B5</color>
+ <color name="indigo_700">#303F9F</color>
+ <color name="green_500">#4CAF50</color>
+ <color name="red_500">#F44336</color>
<color name="black">#FF000000</color>
- <color name="white">#FFFFFFFF</color>
- <color name="black_alpha_50">#80000000</color>
</resources>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
index 5707c81..b6a131a 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
@@ -26,6 +26,13 @@
<string name="stop_scanning">Stop scanning</string>
<string name="scan_result_icon">Scan Result Icon</string>
<string name="connect">Connect</string>
+ <string name="scan_results">Scan Results</string>
+ <string name="not_connected">Not Connected</string>
+ <string name="connecting">Connecting…</string>
+ <string name="connected">Connected</string>
+ <string name="connection_failed">Connection Failed</string>
+ <string name="generic_attribute">Generic Attribute</string>
+ <string name="primary_service">Primary Service</string>
<!-- Advertiser -->
<string name="configure_advertising_packet">Configure Advertising Packet</string>
diff --git a/bluetooth/integration-tests/testapp/src/main/res/values/themes.xml b/bluetooth/integration-tests/testapp/src/main/res/values/themes.xml
index 2373b57c..7ff92c0 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/values/themes.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/values/themes.xml
@@ -14,19 +14,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:tools="http://schemas.android.com/tools">
- <!-- Base application theme. -->
- <style name="Theme.TestApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
- <!-- Primary brand color. -->
- <item name="colorPrimary">@color/purple_500</item>
- <item name="colorPrimaryVariant">@color/purple_700</item>
- <item name="colorOnPrimary">@color/white</item>
- <!-- Secondary brand color. -->
- <item name="colorSecondary">@color/teal_200</item>
- <item name="colorSecondaryVariant">@color/teal_700</item>
- <item name="colorOnSecondary">@color/black</item>
- <!-- Status bar color. -->
+<resources>
+ <style name="Theme.TestApp" parent="Theme.MaterialComponents.Light">
+ <item name="colorPrimary">@color/indigo_500</item>
+ <item name="colorPrimaryVariant">@color/indigo_700</item>
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
- <!-- Customize your theme here. -->
+ <item name="actionBarTheme">@style/ThemeOverlay.MaterialComponents.Dark.ActionBar</item>
</style>
</resources>
diff --git a/browser/browser/src/main/stableAidlImports/android/net/Uri.aidl b/browser/browser/src/main/stableAidlImports/android/net/Uri.aidl
deleted file mode 100644
index 5ec5a66..0000000
--- a/browser/browser/src/main/stableAidlImports/android/net/Uri.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.net;
-
-@JavaOnlyStableParcelable parcelable Uri;
diff --git a/browser/browser/src/main/stableAidlImports/android/os/Bundle.aidl b/browser/browser/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/browser/browser/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index f6ed376..1640305 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -80,12 +80,6 @@
const val ENABLE_DOCUMENTATION = "androidx.enableDocumentation"
/**
- * Adjusts the set of projects participating in this build.
- * See settings.gradle for more information
- */
-const val PROJECT_SUBSET = "androidx.projects"
-
-/**
* Setting this property puts a summary of the relevant failure messages into standard error
*/
const val SUMMARIZE_STANDARD_ERROR = "androidx.summarizeStderr"
@@ -167,7 +161,6 @@
ENABLE_COMPOSE_COMPILER_REPORTS,
DISPLAY_TEST_OUTPUT,
ENABLE_DOCUMENTATION,
- PROJECT_SUBSET,
STUDIO_TYPE,
SUMMARIZE_STANDARD_ERROR,
USE_MAX_DEP_VERSIONS,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/ProjectResolver.kt b/buildSrc/private/src/main/kotlin/androidx/build/ProjectResolver.kt
index 4b207e8..65f0428 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/ProjectResolver.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/ProjectResolver.kt
@@ -45,13 +45,8 @@
* in the build, to make project configuration run more quickly.
*/
fun Project.getProjectSubset(): String? {
- val prop = project.providers.gradleProperty("androidx.projects")
- if (prop.isPresent()) {
- return prop.get().uppercase()
- }
-
val envProp = project.providers.environmentVariable("ANDROIDX_PROJECTS")
- if (envProp.isPresent()) {
+ if (envProp.isPresent) {
return envProp.get().uppercase()
}
return null
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
index d9587ea..0f22c64 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
@@ -56,7 +56,9 @@
// File where the library's public resources are recorded
val resourceFile: File,
// Directory where native API files are stored
- val nativeApiDirectory: File
+ val nativeApiDirectory: File,
+ // Directory where the library's stable AIDL surface is recorded
+ val aidlApiDirectory: File
) : Serializable {
/**
@@ -96,7 +98,8 @@
restrictedApiFile = File(apiFileDir, "$PREFIX_RESTRICTED$baseName$EXTENSION"),
experimentalApiFile = File(apiFileDir, "$PREFIX_EXPERIMENTAL$baseName$EXTENSION"),
resourceFile = File(apiFileDir, "$PREFIX_RESOURCE$baseName$EXTENSION"),
- nativeApiDirectory = File(apiFileDir, NATIVE_API_DIRECTORY_NAME).resolve(baseName)
+ nativeApiDirectory = File(apiFileDir, NATIVE_API_DIRECTORY_NAME).resolve(baseName),
+ aidlApiDirectory = File(apiFileDir, AIDL_API_DIRECTORY_NAME).resolve(baseName)
)
}
@@ -134,6 +137,11 @@
* Directory name for location of native API files
*/
private const val NATIVE_API_DIRECTORY_NAME = "native"
+
+ /**
+ * Directory name for location of AIDL API files
+ */
+ private const val AIDL_API_DIRECTORY_NAME = "aidl"
}
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
index 708cd58..84ef383 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
@@ -26,6 +26,7 @@
import androidx.build.libabigail.NativeApiTasks
import androidx.build.metalava.MetalavaTasks
import androidx.build.resources.ResourceTasks
+import androidx.build.stableaidl.setupWithStableAidlPlugin
import androidx.build.version
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.tasks.ProcessLibraryManifest
@@ -198,6 +199,8 @@
)
}
+ project.setupWithStableAidlPlugin()
+
if (config is LibraryApiTaskConfig) {
ResourceTasks.setupProject(
project, Release.DEFAULT_PUBLISH_CONFIG,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateApiTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateApiTask.kt
index 0286efc..b900386 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateApiTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateApiTask.kt
@@ -18,6 +18,9 @@
import androidx.build.checkapi.ApiLocation
import com.google.common.io.Files
+import java.io.File
+import java.security.MessageDigest
+import org.apache.commons.io.FileUtils
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.logging.Logger
@@ -25,14 +28,13 @@
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction
-import java.io.File
-import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.options.Option
/**
@@ -189,6 +191,54 @@
}
}
+fun copyDir(
+ source: File,
+ dest: File,
+ permitOverwriting: Boolean,
+ logger: Logger
+) {
+ val sourceHash = if (source.exists()) {
+ hashDir(source)
+ } else {
+ null
+ }
+ val overwriting = dest.exists() && !sourceHash.contentEquals(hashDir(dest))
+ val changing = overwriting || (dest.exists() != source.exists())
+ if (changing) {
+ if (overwriting && !permitOverwriting) {
+ val message = "Modifying the API definition for a previously released artifact " +
+ "having a final API version (version not ending in '-alpha') is not " +
+ "allowed.\n\n" +
+ "Previously declared definition is $dest\n" +
+ "Current generated definition is $source\n\n" +
+ "Did you mean to increment the library version first?\n\n" +
+ "If you have reason to overwrite the API files for the previous release " +
+ "anyway, you can run `./gradlew updateApi -Pforce` to ignore this message"
+ throw GradleException(message)
+ }
+ FileUtils.deleteDirectory(dest)
+ if (source.exists()) {
+ FileUtils.copyDirectory(source, dest)
+ logger.lifecycle("Copied $source to $dest")
+ } else {
+ logger.lifecycle("Deleted $dest because $source does not exist")
+ }
+ }
+}
+
+fun hashDir(dir: File): ByteArray {
+ val digest = MessageDigest.getInstance("SHA-256")
+ dir.listFiles()?.forEach { file ->
+ val fileBytes = if (file.isDirectory) {
+ hashDir(file)
+ } else {
+ file.readBytes()
+ }
+ digest.update(fileBytes)
+ }
+ return digest.digest()
+}
+
/**
* Returns -1 if [text] has fewer than [count] newline characters, 0 if equal, and 1 if greater
* than.
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/stableaidl/StableAidlApiTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/stableaidl/StableAidlApiTasks.kt
new file mode 100644
index 0000000..f700913
--- /dev/null
+++ b/buildSrc/private/src/main/kotlin/androidx/build/stableaidl/StableAidlApiTasks.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package androidx.build.stableaidl
+
+import androidx.build.BUILD_ON_SERVER_TASK
+import androidx.build.getSupportRootFolder
+import androidx.stableaidl.withStableAidlPlugin
+import java.io.File
+import org.gradle.api.Project
+
+fun Project.setupWithStableAidlPlugin() = this.withStableAidlPlugin { ext ->
+ ext.checkAction.apply {
+ before(project.tasks.named("check"))
+ before(project.tasks.named(BUILD_ON_SERVER_TASK))
+ before(project.tasks.register("checkAidlApi") { task ->
+ task.group = "API"
+ task.description = "Checks that the API surface generated Stable AIDL sources " +
+ "matches the checked in API surface"
+ })
+ }
+
+ ext.updateAction.apply {
+ before(project.tasks.named("updateApi"))
+ before(project.tasks.register("updateAidlApi") { task ->
+ task.group = "API"
+ task.description = "Updates the checked in API surface based on Stable AIDL sources"
+ })
+ }
+
+ // Don't show tasks added by the Stable AIDL plugin.
+ ext.taskGroup = null
+
+ // Use a single top-level directory for shadow framework definitions.
+ ext.addStaticImportDirs(
+ File(project.getSupportRootFolder(), "buildSrc/stableAidlImports")
+ )
+}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
index 4b9fe1c..8e922b1 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
@@ -22,8 +22,6 @@
import androidx.build.getSdkPath
import androidx.build.getSupportRootFolder
import androidx.build.getVersionByName
-import androidx.build.hasSupportRootFolder
-import androidx.build.setSupportRootFolder
import com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
import java.io.File
import java.nio.file.Files
@@ -125,6 +123,12 @@
open val vmOptions = File(project.getSupportRootFolder(), "development/studio/studio.vmoptions")
/**
+ * The path to the SDK directory used by Studio.
+ */
+ @get:Internal
+ open val localSdkPath = project.getSdkPath()
+
+ /**
* List of additional environment variables to pass into the Studio application.
*/
@get:Internal
@@ -179,18 +183,6 @@
*/
private fun setupSymlinksIfNeeded() {
val paths = listOf("system-images", "emulator")
-
- // The support root folder may not have been set yet, in which case we'll optimistically
- // assume that it matches the root project. This won't work for ui or Playground, but we'll
- // fail gracefully later.
- val hadSupportRootFolder = project.hasSupportRootFolder()
- if (!hadSupportRootFolder) {
- project.setSupportRootFolder(project.rootDir)
- }
- val localSdkPath = project.getSdkPath()
- if (!hadSupportRootFolder) {
- project.setSupportRootFolder(null)
- }
if (!localSdkPath.exists()) {
// We probably got the support root folder wrong. Fail gracefully.
return
@@ -229,11 +221,15 @@
*/
private fun launch() {
if (checkLicenseAgreement(services)) {
- if (requiresProjectList && !System.getenv().containsKey("ANDROIDX_PROJECTS")) {
+ if (requiresProjectList &&
+ !System.getenv().containsKey("ANDROIDX_PROJECTS") &&
+ !System.getenv().containsKey("PROJECT_PREFIX")
+ ) {
throw GradleException(
"""
Please specify which set of projects you'd like to open in studio
with ANDROIDX_PROJECTS=MAIN ./gradlew studio
+ or PROJECT_PREFIX=:room: ./gradlew studio
For possible options see settings.gradle
""".trimIndent()
diff --git a/core/core/src/main/stableAidlImports/android/app/Notification.aidl b/buildSrc/stableAidlImports/android/app/Notification.aidl
similarity index 100%
rename from core/core/src/main/stableAidlImports/android/app/Notification.aidl
rename to buildSrc/stableAidlImports/android/app/Notification.aidl
diff --git a/media/media/src/main/stableAidlImports/android/app/PendingIntent.aidl b/buildSrc/stableAidlImports/android/app/PendingIntent.aidl
similarity index 100%
rename from media/media/src/main/stableAidlImports/android/app/PendingIntent.aidl
rename to buildSrc/stableAidlImports/android/app/PendingIntent.aidl
diff --git a/browser/browser/src/main/stableAidlImports/android/content/ComponentName.aidl b/buildSrc/stableAidlImports/android/content/ComponentName.aidl
similarity index 100%
rename from browser/browser/src/main/stableAidlImports/android/content/ComponentName.aidl
rename to buildSrc/stableAidlImports/android/content/ComponentName.aidl
diff --git a/car/app/app/src/main/stableAidlImports/android/content/Intent.aidl b/buildSrc/stableAidlImports/android/content/Intent.aidl
similarity index 100%
rename from car/app/app/src/main/stableAidlImports/android/content/Intent.aidl
rename to buildSrc/stableAidlImports/android/content/Intent.aidl
diff --git a/javascriptengine/javascriptengine/src/main/stableAidlImports/android/content/res/AssetFileDescriptor.aidl b/buildSrc/stableAidlImports/android/content/res/AssetFileDescriptor.aidl
similarity index 100%
rename from javascriptengine/javascriptengine/src/main/stableAidlImports/android/content/res/AssetFileDescriptor.aidl
rename to buildSrc/stableAidlImports/android/content/res/AssetFileDescriptor.aidl
diff --git a/car/app/app/src/main/stableAidlImports/android/content/res/Configuration.aidl b/buildSrc/stableAidlImports/android/content/res/Configuration.aidl
similarity index 100%
rename from car/app/app/src/main/stableAidlImports/android/content/res/Configuration.aidl
rename to buildSrc/stableAidlImports/android/content/res/Configuration.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/graphics/Insets.aidl b/buildSrc/stableAidlImports/android/graphics/Insets.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/graphics/Insets.aidl
rename to buildSrc/stableAidlImports/android/graphics/Insets.aidl
diff --git a/car/app/app/src/main/stableAidlImports/android/graphics/Rect.aidl b/buildSrc/stableAidlImports/android/graphics/Rect.aidl
similarity index 100%
rename from car/app/app/src/main/stableAidlImports/android/graphics/Rect.aidl
rename to buildSrc/stableAidlImports/android/graphics/Rect.aidl
diff --git a/car/app/app/src/main/stableAidlImports/android/location/Location.aidl b/buildSrc/stableAidlImports/android/location/Location.aidl
similarity index 100%
rename from car/app/app/src/main/stableAidlImports/android/location/Location.aidl
rename to buildSrc/stableAidlImports/android/location/Location.aidl
diff --git a/media/media/src/main/stableAidlImports/android/net/Uri.aidl b/buildSrc/stableAidlImports/android/net/Uri.aidl
similarity index 100%
rename from media/media/src/main/stableAidlImports/android/net/Uri.aidl
rename to buildSrc/stableAidlImports/android/net/Uri.aidl
diff --git a/core/core/src/main/stableAidlImports/android/os/Bundle.aidl b/buildSrc/stableAidlImports/android/os/Bundle.aidl
similarity index 100%
rename from core/core/src/main/stableAidlImports/android/os/Bundle.aidl
rename to buildSrc/stableAidlImports/android/os/Bundle.aidl
diff --git a/car/app/app-projected/src/main/stableAidlImports/android/os/IBinder.aidl b/buildSrc/stableAidlImports/android/os/IBinder.aidl
similarity index 100%
rename from car/app/app-projected/src/main/stableAidlImports/android/os/IBinder.aidl
rename to buildSrc/stableAidlImports/android/os/IBinder.aidl
diff --git a/media/media/src/main/stableAidlImports/android/view/KeyEvent.aidl b/buildSrc/stableAidlImports/android/view/KeyEvent.aidl
similarity index 100%
rename from media/media/src/main/stableAidlImports/android/view/KeyEvent.aidl
rename to buildSrc/stableAidlImports/android/view/KeyEvent.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/MotionEvent.aidl b/buildSrc/stableAidlImports/android/view/MotionEvent.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/MotionEvent.aidl
rename to buildSrc/stableAidlImports/android/view/MotionEvent.aidl
diff --git a/media2/media2-session/src/main/stableAidlImports/android/view/Surface.aidl b/buildSrc/stableAidlImports/android/view/Surface.aidl
similarity index 100%
rename from media2/media2-session/src/main/stableAidlImports/android/view/Surface.aidl
rename to buildSrc/stableAidlImports/android/view/Surface.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/CompletionInfo.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/CompletionInfo.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/CompletionInfo.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/CompletionInfo.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/CorrectionInfo.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/CorrectionInfo.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/CorrectionInfo.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/CorrectionInfo.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/EditorInfo.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/EditorInfo.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/EditorInfo.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/EditorInfo.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/ExtractedText.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/ExtractedText.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/ExtractedText.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/ExtractedText.aidl
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/ExtractedTextRequest.aidl b/buildSrc/stableAidlImports/android/view/inputmethod/ExtractedTextRequest.aidl
similarity index 100%
rename from car/app/app-automotive/src/main/stableAidlImports/android/view/inputmethod/ExtractedTextRequest.aidl
rename to buildSrc/stableAidlImports/android/view/inputmethod/ExtractedTextRequest.aidl
diff --git a/busytown/androidx_multiplatform.sh b/busytown/androidx_multiplatform.sh
new file mode 120000
index 0000000..e2a6ac8
--- /dev/null
+++ b/busytown/androidx_multiplatform.sh
@@ -0,0 +1 @@
+./androidx_compose_multiplatform.sh
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
index d5795f9..5ed3f28 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
@@ -19,6 +19,7 @@
package androidx.camera.camera2.pipe.testing
import android.content.Context
+import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CaptureRequest
import androidx.annotation.RequiresApi
@@ -30,7 +31,10 @@
import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter
import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpInactiveSurfaceCloser
+import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
import androidx.camera.camera2.pipe.integration.config.CameraConfig
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
import androidx.camera.camera2.pipe.integration.impl.CameraCallbackMap
@@ -68,10 +72,22 @@
NoOpInactiveSurfaceCloser,
),
) : UseCaseCamera {
+ val cameraMetadata =
+ cameraPipe.cameras().awaitCameraMetadata(CameraId.fromCamera2Id(cameraId))!!
+ val streamConfigurationMap =
+ cameraMetadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]
+ val cameraQuirks = CameraQuirks(
+ cameraMetadata,
+ StreamConfigurationMapCompat(
+ streamConfigurationMap,
+ OutputSizesCorrector(cameraMetadata, streamConfigurationMap)
+ )
+ )
val useCaseCameraGraphConfig =
UseCaseCameraConfig(
useCases,
CameraStateAdapter(),
+ cameraQuirks,
CameraGraph.Flags()
).provideUseCaseGraphConfig(
callbackMap = callbackMap,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
index d80d27a..71ea477 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
@@ -62,9 +62,18 @@
if (CameraNoResponseWhenEnablingFlashQuirk.isEnabled(cameraMetadata)) {
quirks.add(CameraNoResponseWhenEnablingFlashQuirk())
}
+ if (CaptureSessionStuckQuirk.isEnabled()) {
+ quirks.add(CaptureSessionStuckQuirk())
+ }
+ if (CloseCaptureSessionOnVideoQuirk.isEnabled()) {
+ quirks.add(CloseCaptureSessionOnVideoQuirk())
+ }
if (ConfigureSurfaceToSecondarySessionFailQuirk.isEnabled(cameraMetadata)) {
quirks.add(ConfigureSurfaceToSecondarySessionFailQuirk())
}
+ if (FinalizeSessionOnCloseQuirk.isEnabled()) {
+ quirks.add(FinalizeSessionOnCloseQuirk())
+ }
if (FlashTooSlowQuirk.isEnabled(cameraMetadata)) {
quirks.add(FlashTooSlowQuirk())
}
@@ -92,13 +101,13 @@
if (YuvImageOnePixelShiftQuirk.isEnabled()) {
quirks.add(YuvImageOnePixelShiftQuirk())
}
- if (CaptureSessionStuckQuirk.isEnabled()) {
- quirks.add(CaptureSessionStuckQuirk())
- }
- if (FinalizeSessionOnCloseQuirk.isEnabled()) {
- quirks.add(FinalizeSessionOnCloseQuirk())
- }
Quirks(quirks)
}
+
+ companion object {
+ fun isImmediateSurfaceReleaseAllowed(): Boolean {
+ return Build.BRAND == "google"
+ }
+ }
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt
new file mode 100644
index 0000000..caad5f5
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import androidx.annotation.RequiresApi
+import androidx.camera.core.impl.Quirk
+
+/**
+ * A quirk that instructs the CameraGraph to close the capture session when it is stopped or closed.
+ *
+ * QuirkSummary
+ * - Bug Id: 277310425
+ * - Description: When we unbind and rebind a VideoCapture use case, the video setup process waits
+ * for the Surfaces to be released. This creates a potential deadlock, as CameraPipe
+ * may not release the Surfaces if the previous session or camera remains unclosed.
+ * This is a quirk added so that when we create a CameraGraph out of a set of use
+ * cases with VideoCapture, we make sure we explicitly instruct CameraGraph to close
+ * the capture session when the graph is closed.
+ * - Device(s): All devices.
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class CloseCaptureSessionOnVideoQuirk : Quirk {
+ companion object {
+ fun isEnabled(): Boolean = true
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt
index 95a6727..ed333d1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt
@@ -44,11 +44,19 @@
fun isEnabled() = true
fun getBehavior() =
- if (Build.BRAND == "google") {
+ if (CameraQuirks.isImmediateSurfaceReleaseAllowed()) {
// Finalize immediately for devices that allow immediate Surface reuse.
FinalizeSessionOnCloseBehavior.IMMEDIATE
- } else {
+ } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ // When CloseCaptureSessionOnVideoQuirk is enabled, we close the capture session
+ // in anticipation that the onClosed() callback would finalize the session. However,
+ // on API levels < M, it could be possible that onClosed() isn't invoked if a new
+ // capture session (or CameraGraph) is created too soon (read b/144817309 or
+ // CaptureSessionOnClosedNotCalledQuirk for more context). Therefore, we're enabling
+ // this quirk (on a timeout) for API levels < M, too.
FinalizeSessionOnCloseBehavior.TIMEOUT
+ } else {
+ FinalizeSessionOnCloseBehavior.OFF
}
}
}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
index 0a3c631..dac86b9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
@@ -28,12 +28,13 @@
* Quirk denoting the video profile list returns by [EncoderProfiles] is invalid.
*
* QuirkSummary
- * - Bug Id: 267727595
+ * - Bug Id: 267727595, 278860860
* - Description: When using [EncoderProfiles] on TP1A or TD1A builds of Android API 33,
* [EncoderProfiles.getVideoProfiles] returns a list with size one, but the single value in the
* list is null. This is not the expected behavior, and makes [EncoderProfiles] lack of video
* information.
- * - Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33).
+ * - Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33), Samsung devices
+ * with TP1A build (API 33).
*
* TODO: enable CameraXQuirksClassDetector lint check when kotlin is supported.
*/
@@ -41,7 +42,7 @@
class InvalidVideoProfilesQuirk : Quirk {
companion object {
- private val AFFECTED_MODELS: List<String> = listOf(
+ private val AFFECTED_PIXEL_MODELS: List<String> = listOf(
"pixel 4",
"pixel 4a",
"pixel 4a (5g)",
@@ -56,25 +57,33 @@
)
fun isEnabled(): Boolean {
- return isAffectedModel() && isAffectedBuild()
+ return isAffectedSamsungDevices() || isAffectedPixelDevices()
}
- private fun isAffectedModel(): Boolean {
- return AFFECTED_MODELS.contains(
+ private fun isAffectedSamsungDevices(): Boolean {
+ return "samsung".equals(Build.BRAND, true) && isTp1aBuild()
+ }
+
+ private fun isAffectedPixelDevices(): Boolean {
+ return isAffectedPixelModel() && isAffectedPixelBuild()
+ }
+
+ private fun isAffectedPixelModel(): Boolean {
+ return AFFECTED_PIXEL_MODELS.contains(
Build.MODEL.lowercase()
)
}
- private fun isAffectedBuild(): Boolean {
+ private fun isAffectedPixelBuild(): Boolean {
return isTp1aBuild() || isTd1aBuild()
}
private fun isTp1aBuild(): Boolean {
- return Build.ID.startsWith("TP1A")
+ return Build.ID.startsWith("TP1A", true)
}
private fun isTd1aBuild(): Boolean {
- return Build.ID.startsWith("TD1A")
+ return Build.ID.startsWith("TD1A", true)
}
}
}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
index d8e75a8..e05349a1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
@@ -18,6 +18,7 @@
package androidx.camera.camera2.pipe.integration.config
+import android.media.MediaCodec
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.CameraId
@@ -30,6 +31,8 @@
import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter.Companion.toCamera2ImplConfig
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.CloseCaptureSessionOnVideoQuirk
import androidx.camera.camera2.pipe.integration.compat.workaround.CapturePipelineTorchCorrection
import androidx.camera.camera2.pipe.integration.impl.CameraCallbackMap
import androidx.camera.camera2.pipe.integration.impl.CameraInteropStateCallbackRepository
@@ -82,6 +85,7 @@
class UseCaseCameraConfig(
private val useCases: List<UseCase>,
private val cameraStateAdapter: CameraStateAdapter,
+ private val cameraQuirks: CameraQuirks,
private val cameraGraphFlags: CameraGraph.Flags,
) {
@UseCaseCameraScope
@@ -106,6 +110,7 @@
): UseCaseGraphConfig {
val streamConfigMap = mutableMapOf<CameraStream.Config, DeferrableSurface>()
+ var containsVideo = false
// TODO: This may need to combine outputs that are (or will) share the same output
// imageReader or surface.
val sessionConfigAdapter = SessionConfigAdapter(useCases)
@@ -134,9 +139,26 @@
"Prepare config for: $deferrableSurface (${deferrableSurface.prescribedSize}," +
" ${deferrableSurface.prescribedStreamFormat})"
}
+ if (deferrableSurface.containerClass == MediaCodec::class.java) {
+ containsVideo = true
+ }
}
}
+ val shouldCloseCaptureSessionOnDisconnect =
+ if (CameraQuirks.isImmediateSurfaceReleaseAllowed()) {
+ // If we can release Surfaces immediately, we'll finalize the session when the
+ // camera graph is closed (through FinalizeSessionOnCloseQuirk), and thus we won't
+ // need to explicitly close the capture session.
+ false
+ } else {
+ cameraQuirks.quirks.contains(CloseCaptureSessionOnVideoQuirk::class.java) &&
+ containsVideo
+ }
+ val combinedFlags = cameraGraphFlags.copy(
+ quirkCloseCaptureSessionOnDisconnect = shouldCloseCaptureSessionOnDisconnect,
+ )
+
// Build up a config (using TEMPLATE_PREVIEW by default)
// TODO(b/277310425): Turn off CameraGraph.Flags.quirkFinalizeSessionOnCloseBehavior when
// it's not needed. This should be needed only when all use cases are detached (with
@@ -146,7 +168,7 @@
camera = cameraConfig.cameraId,
streams = streamConfigMap.keys.toList(),
defaultListeners = listOf(callbackMap, requestListener),
- flags = cameraGraphFlags,
+ flags = combinedFlags,
)
)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
index a19f296..6929c99 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
@@ -32,7 +32,6 @@
import androidx.camera.camera2.pipe.Request
import androidx.camera.camera2.pipe.RequestMetadata
import androidx.camera.camera2.pipe.StreamId
-import androidx.camera.camera2.pipe.core.Log
import androidx.camera.camera2.pipe.integration.adapter.CameraUseCaseAdapter
import androidx.camera.camera2.pipe.integration.adapter.CaptureResultAdapter
import androidx.camera.camera2.pipe.integration.config.CameraScope
@@ -88,8 +87,6 @@
)
}
}
- } else {
- Log.error { "Unhandled callback for onBufferLost()" }
}
}
}
@@ -209,8 +206,6 @@
)
}
}
- } else {
- Log.error { "Unhandled callback for onRequestSequenceCompleted()" }
}
}
}
@@ -232,8 +227,6 @@
)
}
}
- } else {
- Log.error { "Unhandled callback for onStarted()" }
}
}
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
index bfa1fc7..beb29c8 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
@@ -34,6 +34,7 @@
import androidx.camera.camera2.pipe.Result3A
import androidx.camera.camera2.pipe.StreamId
import androidx.camera.camera2.pipe.TorchState
+import androidx.camera.camera2.pipe.core.Log.debug
import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
@@ -179,6 +180,7 @@
tags: Map<String, Any>,
listeners: Set<Request.Listener>
): Deferred<Unit> = synchronized(lock) {
+ debug { "[$type] Add request option: $values" }
infoBundleMap.getOrPut(type) { InfoBundle() }.let {
it.options.addAllCaptureRequestOptionsWithPriority(values, optionPriority)
it.tags.putAll(tags)
@@ -195,6 +197,7 @@
template: RequestTemplate?,
listeners: Set<Request.Listener>
): Deferred<Unit> = synchronized(lock) {
+ debug { "[$type] Set config: ${config?.toParameters()}" }
infoBundleMap[type] = InfoBundle(
Camera2ImplConfig.Builder().apply {
config?.let {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
index 57cfef6..ddf68c2 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
@@ -29,6 +29,7 @@
import androidx.camera.camera2.pipe.RequestMetadata
import androidx.camera.camera2.pipe.RequestTemplate
import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.core.Log
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
import javax.inject.Inject
@@ -254,7 +255,7 @@
result?.let { result ->
updateSignals.add(RequestSignal(submittedRequestCounter.value, result))
}
-
+ Log.debug { "Update RepeatingRequest: $request" }
it.startRepeating(request)
}
}
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 222047d..9f39981 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
@@ -23,6 +23,7 @@
import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.core.Log
import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
import androidx.camera.camera2.pipe.integration.config.CameraConfig
import androidx.camera.camera2.pipe.integration.config.CameraScope
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraComponent
@@ -72,6 +73,7 @@
private val controls: java.util.Set<UseCaseCameraControl>,
private val camera2CameraControl: Camera2CameraControl,
private val cameraStateAdapter: CameraStateAdapter,
+ private val cameraQuirks: CameraQuirks,
private val cameraGraphFlags: CameraGraph.Flags,
cameraProperties: CameraProperties,
displayInfoManager: DisplayInfoManager,
@@ -257,7 +259,14 @@
// Create and configure the new camera component.
_activeComponent =
- builder.config(UseCaseCameraConfig(useCases, cameraStateAdapter, cameraGraphFlags))
+ builder.config(
+ UseCaseCameraConfig(
+ useCases,
+ cameraStateAdapter,
+ cameraQuirks,
+ cameraGraphFlags
+ )
+ )
.build()
for (control in allControls) {
control.useCaseCamera = camera
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 f0a9f17..58e25df 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
@@ -22,6 +22,9 @@
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
import androidx.camera.camera2.pipe.integration.config.CameraConfig
import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera.RunningUseCasesChangeListener
import androidx.camera.camera2.pipe.integration.interop.Camera2CameraControl
@@ -29,6 +32,7 @@
import androidx.camera.camera2.pipe.integration.testing.FakeCamera2CameraControlCompat
import androidx.camera.camera2.pipe.integration.testing.FakeCameraProperties
import androidx.camera.camera2.pipe.integration.testing.FakeUseCaseCameraComponentBuilder
+import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
import androidx.camera.core.UseCase
@@ -276,6 +280,10 @@
),
cameraStateAdapter = CameraStateAdapter(),
cameraGraphFlags = CameraGraph.Flags(),
+ cameraQuirks = CameraQuirks(
+ FakeCameraMetadata(),
+ StreamConfigurationMapCompat(null, OutputSizesCorrector(FakeCameraMetadata(), null))
+ ),
displayInfoManager = DisplayInfoManager(ApplicationProvider.getApplicationContext()),
).also {
useCaseManagerList.add(it)
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 5c3279f..ca33199 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
@@ -26,10 +26,14 @@
import androidx.camera.camera2.pipe.Result3A
import androidx.camera.camera2.pipe.StreamId
import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraComponent
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera
import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraRequestControl
+import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
import androidx.camera.core.UseCase
import androidx.camera.core.impl.CaptureConfig
import androidx.camera.core.impl.Config
@@ -40,8 +44,12 @@
import kotlinx.coroutines.withTimeoutOrNull
class FakeUseCaseCameraComponentBuilder : UseCaseCameraComponent.Builder {
+ private val cameraQuirks = CameraQuirks(
+ FakeCameraMetadata(),
+ StreamConfigurationMapCompat(null, OutputSizesCorrector(FakeCameraMetadata(), null))
+ )
private var config: UseCaseCameraConfig =
- UseCaseCameraConfig(emptyList(), CameraStateAdapter(), CameraGraph.Flags())
+ UseCaseCameraConfig(emptyList(), CameraStateAdapter(), cameraQuirks, CameraGraph.Flags())
override fun config(config: UseCaseCameraConfig): UseCaseCameraComponent.Builder {
this.config = config
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 7e5ce4b..ee57224 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
@@ -153,7 +153,30 @@
*/
val quirkWaitForRepeatingRequestOnDisconnect: Boolean? = null,
+ /**
+ * A quirk that finalizes [androidx.camera.camera2.pipe.compat.CaptureSessionState] when
+ * the CameraGraph is stopped or closed. When a CameraGraph is started, the app might
+ * wait for the Surfaces to be released before setting the new Surfaces. This creates a
+ * potential deadlock, and this quirk is aimed to mitigate such behavior by releasing the
+ * Surfaces (finalizing the session) when the graph is stopped or closed.
+ *
+ * - Bug(s): b/277310425
+ * - Device(s): All (but behaviors might differ across devices)
+ * - API levels: All
+ */
val quirkFinalizeSessionOnCloseBehavior: FinalizeSessionOnCloseBehavior = OFF,
+
+ /**
+ * A quirk that closes the camera capture session when the CameraGraph is stopped or closed.
+ * This is needed in cases where the app that do not wish to receive further frames, or
+ * in cases where not closing the capture session before closing the camera device might
+ * cause the camera close call itself to hang indefinitely.
+ *
+ * - Bug(s): b/277310425, b/277310425
+ * - Device(s): Depends on the situation and the use case.
+ * - API levels: All
+ */
+ val quirkCloseCaptureSessionOnDisconnect: Boolean = false,
) {
@JvmInline
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 35995c4..3d4ea8d 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
@@ -101,7 +101,7 @@
captureSequenceProcessorFactory,
cameraSurfaceManager,
timeSource,
- config.flags.quirkFinalizeSessionOnCloseBehavior,
+ config.flags,
scope
)
currentSession = session
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
index 0852ebf..7a07920 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
@@ -22,6 +22,7 @@
import android.view.Surface
import androidx.annotation.GuardedBy
import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.CameraGraph.Flags.FinalizeSessionOnCloseBehavior
import androidx.camera.camera2.pipe.CameraSurfaceManager
import androidx.camera.camera2.pipe.StreamId
@@ -65,7 +66,7 @@
private val captureSequenceProcessorFactory: Camera2CaptureSequenceProcessorFactory,
private val cameraSurfaceManager: CameraSurfaceManager,
private val timeSource: TimeSource,
- private val finalizeSessionOnCloseBehavior: FinalizeSessionOnCloseBehavior,
+ private val cameraGraphFlags: CameraGraph.Flags,
private val scope: CoroutineScope
) : CameraCaptureSessionWrapper.StateCallback {
private val debugId = captureSessionDebugIds.incrementAndGet()
@@ -270,7 +271,7 @@
val graphProcessor = configuredCaptureSession?.processor
if (graphProcessor != null) {
// WARNING:
- // This does NOT call close on the captureSession to avoid potentially slow
+ // This normally does NOT call close on the captureSession to avoid potentially slow
// reconfiguration during mode switch and shutdown. This avoids unintentional restarts
// by clearing the internal captureSession variable, clearing all repeating requests,
// and by aborting any pending single requests.
@@ -294,6 +295,23 @@
Debug.traceStop()
}
+ // There are rare, extraordinary circumstances where we might need to close the capture
+ // session. It is possible the app might explicitly wait for the captures to be
+ // completely stopped through signals from CameraSurfaceManager, and in which case
+ // closing the capture session would eventually release the Surfaces [1]. Additionally,
+ // on certain devices, we need to close the capture session, or else the camera device
+ // close call might stall indefinitely [2].
+ //
+ // [1] b/277310425
+ // [2] b/277675483
+ if (cameraGraphFlags.quirkCloseCaptureSessionOnDisconnect) {
+ val captureSession = configuredCaptureSession?.session
+ checkNotNull(captureSession)
+ Debug.trace("$this CameraCaptureSessionWrapper#close") {
+ Log.debug { "Closing capture session for $this" }
+ captureSession.close()
+ }
+ }
Debug.traceStop()
}
@@ -306,15 +324,17 @@
if (state != State.CLOSED) {
if (_cameraDevice == null || !hasAttemptedCaptureSession) {
shouldFinalizeSession = true
- } else if (finalizeSessionOnCloseBehavior ==
- FinalizeSessionOnCloseBehavior.IMMEDIATE
- ) {
- shouldFinalizeSession = true
- } else if (finalizeSessionOnCloseBehavior ==
- FinalizeSessionOnCloseBehavior.TIMEOUT
- ) {
- shouldFinalizeSession = true
- finalizeSessionDelayMs = 2000L
+ } else {
+ when (cameraGraphFlags.quirkFinalizeSessionOnCloseBehavior) {
+ FinalizeSessionOnCloseBehavior.IMMEDIATE -> {
+ shouldFinalizeSession = true
+ }
+
+ FinalizeSessionOnCloseBehavior.TIMEOUT -> {
+ shouldFinalizeSession = true
+ finalizeSessionDelayMs = 2000L
+ }
+ }
}
}
_cameraDevice = null
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
index 3635a7c..1236721 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
@@ -23,6 +23,7 @@
import android.util.Size
import android.view.Surface
import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraph.Flags.FinalizeSessionOnCloseBehavior
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.CameraMetadata
import androidx.camera.camera2.pipe.CameraPipe
@@ -128,7 +129,10 @@
},
CameraSurfaceManager(),
SystemTimeSource(),
- CameraGraph.Flags.FinalizeSessionOnCloseBehavior.OFF,
+ CameraGraph.Flags(
+ quirkFinalizeSessionOnCloseBehavior = FinalizeSessionOnCloseBehavior.OFF,
+ quirkCloseCaptureSessionOnDisconnect = false,
+ ),
this
)
)
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionStateTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionStateTest.kt
index 45f4731..eafc0ae 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionStateTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionStateTest.kt
@@ -19,6 +19,7 @@
import android.graphics.SurfaceTexture
import android.os.Build
import android.view.Surface
+import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.CameraGraph.Flags.FinalizeSessionOnCloseBehavior
import androidx.camera.camera2.pipe.CameraSurfaceManager
import androidx.camera.camera2.pipe.CaptureSequenceProcessor
@@ -61,6 +62,10 @@
): CaptureSequenceProcessor<Request, FakeCaptureSequence> = fakeCaptureSequenceProcessor
}
private val timeSource = SystemTimeSource()
+ private val cameraGraphFlags = CameraGraph.Flags(
+ quirkFinalizeSessionOnCloseBehavior = FinalizeSessionOnCloseBehavior.OFF,
+ quirkCloseCaptureSessionOnDisconnect = false,
+ )
private val surface1: Surface = Surface(SurfaceTexture(1))
private val surface2: Surface = Surface(SurfaceTexture(2))
@@ -91,7 +96,7 @@
captureSequenceProcessorFactory,
cameraSurfaceManager,
timeSource,
- FinalizeSessionOnCloseBehavior.OFF,
+ cameraGraphFlags,
this
)
// When disconnect is called first
@@ -114,7 +119,7 @@
captureSequenceProcessorFactory,
cameraSurfaceManager,
timeSource,
- FinalizeSessionOnCloseBehavior.OFF,
+ cameraGraphFlags,
this
)
@@ -142,7 +147,7 @@
captureSequenceProcessorFactory,
cameraSurfaceManager,
timeSource,
- FinalizeSessionOnCloseBehavior.OFF,
+ cameraGraphFlags,
this
)
@@ -176,7 +181,7 @@
captureSequenceProcessorFactory,
cameraSurfaceManager,
timeSource,
- FinalizeSessionOnCloseBehavior.OFF,
+ cameraGraphFlags,
this
)
// When surfaces are configured
@@ -200,7 +205,7 @@
captureSequenceProcessorFactory,
cameraSurfaceManager,
timeSource,
- FinalizeSessionOnCloseBehavior.OFF,
+ cameraGraphFlags,
this
)
// When surfaces are configured
@@ -224,7 +229,7 @@
captureSequenceProcessorFactory,
cameraSurfaceManager,
timeSource,
- FinalizeSessionOnCloseBehavior.OFF,
+ cameraGraphFlags,
this
)
// When surfaces are configured
@@ -238,4 +243,41 @@
verify(fakeSurfaceListener, times(1)).onSurfaceInactive(eq(surface1))
verify(fakeSurfaceListener, times(1)).onSurfaceInactive(eq(surface2))
}
+
+ @Test
+ fun captureSessionStateClosesCaptureSessionWhenQuirkIsEnabled() = runTest {
+ val state =
+ CaptureSessionState(
+ fakeGraphListener,
+ captureSessionFactory,
+ captureSequenceProcessorFactory,
+ cameraSurfaceManager,
+ timeSource,
+ CameraGraph.Flags(
+ quirkCloseCaptureSessionOnDisconnect = true,
+ ),
+ this
+ )
+
+ // When surfaces are configured
+ state.configureSurfaceMap(mapOf(stream1 to surface1, stream2 to surface2))
+ verify(fakeSurfaceListener, times(1)).onSurfaceActive(eq(surface1))
+ verify(fakeSurfaceListener, times(1)).onSurfaceActive(eq(surface2))
+
+ // And a device is set
+ state.cameraDevice = fakeCameraDevice
+
+ // Advance to make sure a capture session is created.
+ advanceUntilIdle()
+
+ // Feed a fake capture session
+ state.onConfigured(fakeCaptureSession)
+
+ // And the state is then disconnected
+ state.disconnect()
+
+ // Then make sure we do close the capture session.
+ advanceUntilIdle()
+ verify(fakeCaptureSession, times(1)).close()
+ }
}
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 c54cc1a..2f3e542 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
@@ -57,6 +57,8 @@
import androidx.camera.camera2.Camera2Config;
import androidx.camera.camera2.impl.Camera2ImplConfig;
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.core.CameraControl;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraXConfig;
@@ -68,6 +70,7 @@
import androidx.camera.core.impl.CameraCaptureResult;
import androidx.camera.core.impl.CameraControlInternal;
import androidx.camera.core.impl.CaptureConfig;
+import androidx.camera.core.impl.Quirks;
import androidx.camera.core.impl.SessionConfig;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.internal.CameraUseCaseAdapter;
@@ -125,6 +128,7 @@
private boolean mHasFlashUnit;
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private CameraUseCaseAdapter mCamera;
+ private Quirks mCameraQuirks;
@Before
public void setUp() throws InterruptedException {
@@ -151,6 +155,7 @@
mCameraCharacteristics, cameraId);
mCamera2CameraControlImpl = new Camera2CameraControlImpl(mCameraCharacteristicsCompat,
executorService, executorService, mControlUpdateCallback);
+ mCameraQuirks = CameraQuirks.get(cameraId, mCameraCharacteristicsCompat);
mCamera2CameraControlImpl.incrementUseCount();
mCamera2CameraControlImpl.setActive(true);
@@ -724,9 +729,12 @@
}
private void assertAeMode(Camera2ImplConfig config, int aeMode) {
- if (isAeModeSupported(aeMode)) {
+ AutoFlashAEModeDisabler aeModeCorrector = new AutoFlashAEModeDisabler(mCameraQuirks);
+ int aeModeCorrected = aeModeCorrector.getCorrectedAeMode(aeMode);
+
+ if (isAeModeSupported(aeModeCorrected)) {
assertThat(config.getCaptureRequestOption(
- CaptureRequest.CONTROL_AE_MODE, null)).isEqualTo(aeMode);
+ CaptureRequest.CONTROL_AE_MODE, null)).isEqualTo(aeModeCorrected);
} else {
int fallbackMode;
if (isAeModeSupported(CONTROL_AE_MODE_ON)) {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2RequestProcessorTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2RequestProcessorTest.kt
index 754b174..693f94e 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2RequestProcessorTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2RequestProcessorTest.kt
@@ -30,6 +30,7 @@
import androidx.camera.camera2.Camera2Config
import androidx.camera.camera2.impl.Camera2CameraCaptureResultConverter
import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat
import androidx.camera.camera2.internal.compat.quirk.CameraQuirks
import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks
import androidx.camera.camera2.internal.util.RequestProcessorRequest
@@ -79,6 +80,7 @@
private lateinit var cameraDeviceHolder: CameraUtil.CameraDeviceHolder
private lateinit var captureSessionRepository: CaptureSessionRepository
+ private lateinit var dynamicRangesCompat: DynamicRangesCompat
private lateinit var captureSessionOpenerBuilder: SynchronizedCaptureSessionOpener.Builder
private lateinit var mainThreadExecutor: Executor
private lateinit var previewSurface: SessionProcessorSurface
@@ -103,12 +105,14 @@
val handler = Handler(Looper.getMainLooper())
mainThreadExecutor = CameraXExecutors.newHandlerExecutor(handler)
captureSessionRepository = CaptureSessionRepository(mainThreadExecutor)
+ val cameraCharacteristics = getCameraCharacteristic(CAMERA_ID)
+ dynamicRangesCompat = DynamicRangesCompat.fromCameraCharacteristics(cameraCharacteristics)
captureSessionOpenerBuilder = SynchronizedCaptureSessionOpener.Builder(
mainThreadExecutor,
mainThreadExecutor as ScheduledExecutorService,
handler,
captureSessionRepository,
- CameraQuirks.get(CAMERA_ID, getCameraCharacteristic(CAMERA_ID)),
+ CameraQuirks.get(CAMERA_ID, cameraCharacteristics),
DeviceQuirks.getAll()
)
@@ -171,7 +175,7 @@
@Test
fun canSubmit(): Unit = runBlocking {
// Arrange
- val captureSession = CaptureSession()
+ val captureSession = CaptureSession(dynamicRangesCompat)
val cameraDevice = cameraDeviceHolder.get()!!
captureSession.open(
getSessionConfig(),
@@ -220,7 +224,7 @@
@Test
fun canSubmitMultipleThreads() {
// Arrange
- val captureSession = CaptureSession()
+ val captureSession = CaptureSession(dynamicRangesCompat)
val cameraDevice = cameraDeviceHolder.get()!!
captureSession.open(
getSessionConfig(),
@@ -286,7 +290,7 @@
@Test
fun canSubmitList(): Unit = runBlocking {
// Arrange
- val captureSession = CaptureSession()
+ val captureSession = CaptureSession(dynamicRangesCompat)
val cameraDevice = cameraDeviceHolder.get()!!
captureSession.open(
getSessionConfig(),
@@ -364,7 +368,7 @@
@Test
fun canSetRepeating(): Unit = runBlocking {
// Arrange
- val captureSession = CaptureSession()
+ val captureSession = CaptureSession(dynamicRangesCompat)
val cameraDevice = cameraDeviceHolder.get()!!
captureSession.open(
getSessionConfig(),
@@ -416,7 +420,7 @@
@Test
fun closeRequestProcessor(): Unit = runBlocking {
// Arrange
- val captureSession = CaptureSession()
+ val captureSession = CaptureSession(dynamicRangesCompat)
val cameraDevice = cameraDeviceHolder.get()!!
captureSession.open(
getSessionConfig(),
@@ -459,7 +463,7 @@
@Test
fun invalidRequest(): Unit = runBlocking {
// Arrange
- val captureSession = CaptureSession()
+ val captureSession = CaptureSession(dynamicRangesCompat)
val cameraDevice = cameraDeviceHolder.get()!!
captureSession.open(
getSessionConfig(),
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 75e0fe5..fdd7bae 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
@@ -19,6 +19,7 @@
import static android.os.Build.VERSION.SDK_INT;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static junit.framework.TestCase.assertTrue;
import static junit.framework.TestCase.fail;
@@ -39,11 +40,11 @@
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
+import android.hardware.DataSpace;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
@@ -58,17 +59,23 @@
import android.view.Surface;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.camera2.Camera2Config;
import androidx.camera.camera2.impl.Camera2ImplConfig;
import androidx.camera.camera2.impl.CameraEventCallback;
import androidx.camera.camera2.impl.CameraEventCallbacks;
import androidx.camera.camera2.internal.CaptureSession.State;
+import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
+import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
+import androidx.camera.camera2.internal.compat.CameraManagerCompat;
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat;
import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat;
import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat;
import androidx.camera.camera2.internal.compat.quirk.ConfigureSurfaceToSecondarySessionFailQuirk;
import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks;
import androidx.camera.camera2.internal.compat.quirk.PreviewOrientationIncorrectQuirk;
+import androidx.camera.core.DynamicRange;
import androidx.camera.core.impl.CameraCaptureCallback;
import androidx.camera.core.impl.CameraCaptureCallbacks;
import androidx.camera.core.impl.CameraCaptureResult;
@@ -82,8 +89,10 @@
import androidx.camera.core.impl.utils.futures.FutureCallback;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.SurfaceTextureProvider;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.core.os.HandlerCompat;
+import androidx.core.util.Preconditions;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
@@ -119,6 +128,7 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Tests for {@link CaptureSession}. This requires an environment where a valid {@link
@@ -131,6 +141,9 @@
@SdkSuppress(minSdkVersion = 21)
@RequiresApi(21)
public final class CaptureSessionTest {
+ private static final DynamicRange DYNAMIC_RANGE_HLG10 =
+ new DynamicRange(DynamicRange.FORMAT_HLG, DynamicRange.BIT_DEPTH_10_BIT);
+
/** Thread for all asynchronous calls. */
private static HandlerThread sHandlerThread;
/** Handler for all asynchronous calls. */
@@ -151,6 +164,10 @@
private final List<CaptureSession> mCaptureSessions = new ArrayList<>();
private final List<DeferrableSurface> mDeferrableSurfaces = new ArrayList<>();
+ private CameraCharacteristicsCompat mCameraCharacteristics;
+
+ private DynamicRangesCompat mDynamicRangesCompat;
+
@Rule
public TestRule getUseCameraRule() {
if (SDK_INT >= 19) {
@@ -204,7 +221,20 @@
mScheduledExecutor, mHandler, mCaptureSessionRepository,
new Quirks(new ArrayList<>()), DeviceQuirks.getAll());
- mCameraDeviceHolder = CameraUtil.getCameraDevice(
+ String cameraId = CameraUtil.getBackwardCompatibleCameraIdListOrThrow().get(0);
+ Context context = ApplicationProvider.getApplicationContext();
+ CameraManagerCompat cameraManager = CameraManagerCompat.from(context, mHandler);
+ try {
+ mCameraCharacteristics =
+ cameraManager.getCameraCharacteristicsCompat(cameraId);
+ } catch (CameraAccessExceptionCompat e) {
+ throw new AssumptionViolatedException("Could not retrieve camera characteristics", e);
+ }
+
+ mDynamicRangesCompat =
+ DynamicRangesCompat.fromCameraCharacteristics(mCameraCharacteristics);
+
+ mCameraDeviceHolder = CameraUtil.getCameraDevice(cameraId,
mCaptureSessionRepository.getCameraStateCallback());
}
@@ -279,17 +309,9 @@
}
private boolean isLegacyCamera() {
- String cameraId = CameraUtil.getBackwardCompatibleCameraIdListOrThrow().get(0);
- Context context = ApplicationProvider.getApplicationContext();
- CameraManager cameraManager =
- (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
- try {
- return cameraManager.getCameraCharacteristics(cameraId)
- .get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
- == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
- } catch (CameraAccessException e) {
- }
- return false;
+ return Preconditions.checkNotNull(mCameraCharacteristics
+ .get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL))
+ == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
}
// Set stream use case is not supported before API 33
@@ -320,6 +342,26 @@
== CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW);
}
+ @SdkSuppress(minSdkVersion = 33) // Can only verify data space on API 33+
+ @Test
+ public void openCaptureSessionWithDefault_usesSdrDynamicRange()
+ throws ExecutionException, InterruptedException, TimeoutException {
+ openCaptureSessionAndVerifyDynamicRangeApplied(
+ /*inputDynamicRange=*/null, // Should default to SDR
+ DataSpace.STANDARD_BT709,
+ DataSpace.TRANSFER_SRGB);
+ }
+
+ @SdkSuppress(minSdkVersion = 33) // HLG dynamic range only supported since API 33
+ @Test
+ public void openCaptureSessionWithHlgDynamicRange()
+ throws ExecutionException, InterruptedException, TimeoutException {
+ openCaptureSessionAndVerifyDynamicRangeApplied(
+ DYNAMIC_RANGE_HLG10,
+ DataSpace.STANDARD_BT2020,
+ DataSpace.TRANSFER_HLG);
+ }
+
// Sharing surface of YUV format is supported since API 28
@SdkSuppress(minSdkVersion = 28)
@Test
@@ -1103,7 +1145,7 @@
}
private CaptureSession createCaptureSession() {
- CaptureSession captureSession = new CaptureSession();
+ CaptureSession captureSession = new CaptureSession(mDynamicRangesCompat);
mCaptureSessions.add(captureSession);
return captureSession;
}
@@ -1365,7 +1407,7 @@
SynchronizedCaptureSessionOpener opener = new SynchronizedCaptureSessionOpener(fakeOpener);
// Don't use #createCaptureSession since FakeOpenerImpl won't create CameraCaptureSession
// so no need to be released.
- CaptureSession captureSession = new CaptureSession();
+ CaptureSession captureSession = new CaptureSession(mDynamicRangesCompat);
captureSession.open(sessionConfigBuilder.build(), mCameraDeviceHolder.get(), opener);
ArgumentCaptor<SessionConfigurationCompat> captor =
@@ -1512,6 +1554,94 @@
verify(stateCallback, timeout(3000L)).onReady(any());
}
+ @RequiresApi(33) // SurfaceTexture.getDataSpace() was added in API 33
+ private void openCaptureSessionAndVerifyDynamicRangeApplied(
+ @Nullable DynamicRange inputDynamicRange,
+ int expectedColorStandard,
+ int expectedTransferFn)
+ throws ExecutionException, InterruptedException, TimeoutException {
+ // 1. Arrange
+ if (inputDynamicRange != null) {
+ // Only run test on devices that support the
+ assumeTrue(
+ mDynamicRangesCompat.getSupportedDynamicRanges().contains(inputDynamicRange));
+ }
+
+ assumeFalse("Cuttlefish does not set the data space correctly for camera targets.",
+ Build.MODEL.contains("Cuttlefish"));
+
+ CountDownLatch latch0 = new CountDownLatch(1);
+ AtomicInteger dataSpace = new AtomicInteger(DataSpace.DATASPACE_UNKNOWN);
+ ListenableFuture<SurfaceTextureProvider.SurfaceTextureHolder> surfaceTextureHolderFuture =
+ SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync(mExecutor, 640, 480,
+ surfaceTexture -> {
+ dataSpace.set(surfaceTexture.getDataSpace());
+ latch0.countDown();
+ }, /* onClosed= */null);
+
+ DeferrableSurface deferrableSurface = new DeferrableSurface() {
+ @NonNull
+ @Override
+ protected ListenableFuture<Surface> provideSurface() {
+ return Futures.transform(surfaceTextureHolderFuture,
+ surfaceTextureHolder -> {
+ Surface surface = new Surface(surfaceTextureHolder.getSurfaceTexture());
+ getTerminationFuture().addListener(surface::release, mExecutor);
+ return surface;
+ },
+ CameraXExecutors.directExecutor());
+ }
+ };
+
+ deferrableSurface.getTerminationFuture().addListener(
+ () -> Futures.addCallback(surfaceTextureHolderFuture,
+ new FutureCallback<SurfaceTextureProvider.SurfaceTextureHolder>() {
+ @Override
+ public void onSuccess(
+ @Nullable SurfaceTextureProvider.SurfaceTextureHolder result) {
+ try {
+ Preconditions.checkNotNull(result).close();
+ } catch (Exception e) {
+ throw new AssertionError("Unable to release SurfaceTexture", e);
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Throwable t) {
+ throw new AssertionError("Unable to retrieve SurfaceTexture", t);
+ }
+ }, mExecutor), CameraXExecutors.directExecutor());
+
+ mDeferrableSurfaces.add(deferrableSurface);
+
+ SessionConfig.OutputConfig.Builder outputConfigBuilder =
+ SessionConfig.OutputConfig.builder(deferrableSurface);
+ if (inputDynamicRange != null) {
+ outputConfigBuilder.setDynamicRange(inputDynamicRange);
+ }
+ SessionConfig sessionConfig =
+ new SessionConfig.Builder()
+ .addOutputConfig(outputConfigBuilder.build())
+ .setTemplateType(CameraDevice.TEMPLATE_PREVIEW)
+ .build();
+
+ // 2. Act
+ CaptureSession captureSession = createCaptureSession();
+ captureSession.setSessionConfig(sessionConfig); // set repeating request
+ ListenableFuture<Void> future = captureSession.open(sessionConfig,
+ mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build());
+ future.get(2, TimeUnit.SECONDS);
+
+ // 3. Assert
+ assertWithMessage("Timed out while waiting for frame to be produced.")
+ .that(latch0.await(2, TimeUnit.SECONDS))
+ .isTrue();
+
+ // Ensure the dataspace matches what is expected
+ assertThat(DataSpace.getStandard(dataSpace.get())).isEqualTo(expectedColorStandard);
+ assertThat(DataSpace.getTransfer(dataSpace.get())).isEqualTo(expectedTransferFn);
+ }
+
/**
* A implementation to test {@link CameraEventCallback} on CaptureSession.
*/
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt
index 057cc38..0a26bfb 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt
@@ -34,6 +34,7 @@
import android.view.Surface
import androidx.camera.camera2.Camera2Config
import androidx.camera.camera2.internal.compat.CameraManagerCompat
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat
import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks
import androidx.camera.camera2.interop.CaptureRequestOptions
import androidx.camera.core.CameraSelector
@@ -195,9 +196,16 @@
val cameraId = CameraUtil.getCameraIdWithLensFacing(lensFacing)!!
val camera2Info = Camera2CameraInfoImpl(cameraId, cameraManagerCompat)
+ val dynamicRangesCompat = cameraManagerCompat.getCameraCharacteristicsCompat(cameraId).let {
+ DynamicRangesCompat.fromCameraCharacteristics(it)
+ }
return ProcessingCaptureSession(
- sessionProcessor, camera2Info, executor, executor as ScheduledExecutorService
+ sessionProcessor,
+ camera2Info,
+ dynamicRangesCompat,
+ executor,
+ executor as ScheduledExecutorService
)
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 088aa5d..1612a15 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -37,13 +37,13 @@
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.camera.camera2.internal.annotation.CameraExecutor;
import androidx.camera.camera2.internal.compat.ApiCompat;
import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
import androidx.camera.camera2.internal.compat.CameraManagerCompat;
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat;
import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraState;
@@ -201,6 +201,9 @@
@NonNull
private final CameraCharacteristicsCompat mCameraCharacteristicsCompat;
+ @NonNull
+ private final DynamicRangesCompat mDynamicRangesCompat;
+
/**
* Constructor for a camera.
*
@@ -234,7 +237,6 @@
mCameraStateMachine = new CameraStateMachine(cameraStateRegistry);
mCaptureSessionRepository = new CaptureSessionRepository(mExecutor);
mDisplayInfoManager = displayInfoManager;
- mCaptureSession = newCaptureSession();
try {
mCameraCharacteristicsCompat =
@@ -248,6 +250,10 @@
} catch (CameraAccessExceptionCompat e) {
throw CameraUnavailableExceptionHelper.createFrom(e);
}
+ mDynamicRangesCompat =
+ DynamicRangesCompat.fromCameraCharacteristics(mCameraCharacteristicsCompat);
+ mCaptureSession = newCaptureSession();
+
mCaptureSessionOpenerBuilder = new SynchronizedCaptureSessionOpener.Builder(mExecutor,
mScheduledExecutorService, schedulerHandler, mCaptureSessionRepository,
cameraInfoImpl.getCameraQuirks(), DeviceQuirks.getAll());
@@ -268,10 +274,11 @@
private CaptureSessionInterface newCaptureSession() {
synchronized (mLock) {
if (mSessionProcessor == null) {
- return new CaptureSession();
+ return new CaptureSession(mDynamicRangesCompat);
} else {
return new ProcessingCaptureSession(mSessionProcessor,
- mCameraInfoInternal, mExecutor, mScheduledExecutorService);
+ mCameraInfoInternal, mDynamicRangesCompat, mExecutor,
+ mScheduledExecutorService);
}
}
}
@@ -359,7 +366,7 @@
@ExecutedBy("mExecutor")
private void configAndClose(boolean abortInFlightCaptures) {
- final CaptureSession noOpSession = new CaptureSession();
+ final CaptureSession noOpSession = new CaptureSession(mDynamicRangesCompat);
mConfiguringForClose.add(noOpSession); // Make mCameraDevice is not closed and existed.
resetCaptureSession(abortInFlightCaptures);
@@ -671,7 +678,7 @@
* block until completion.
*
*/
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
boolean isUseCaseAttached(@NonNull UseCase useCase) {
try {
String useCaseId = getUseCaseId(useCase);
@@ -1023,7 +1030,7 @@
}
@NonNull
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
public CameraAvailability getCameraAvailability() {
return mCameraAvailability;
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
index 4894b28..95dda70 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
@@ -22,6 +22,8 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.DynamicRangeProfiles;
+import android.os.Build;
import android.view.Surface;
import androidx.annotation.GuardedBy;
@@ -31,12 +33,15 @@
import androidx.annotation.RequiresApi;
import androidx.camera.camera2.impl.Camera2ImplConfig;
import androidx.camera.camera2.impl.CameraEventCallbacks;
+import androidx.camera.camera2.internal.compat.params.DynamicRangeConversions;
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat;
import androidx.camera.camera2.internal.compat.params.InputConfigurationCompat;
import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat;
import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat;
import androidx.camera.camera2.internal.compat.workaround.StillCaptureFlow;
import androidx.camera.camera2.internal.compat.workaround.TorchStateReset;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
+import androidx.camera.core.DynamicRange;
import androidx.camera.core.Logger;
import androidx.camera.core.impl.CameraCaptureCallback;
import androidx.camera.core.impl.CaptureConfig;
@@ -135,11 +140,14 @@
final StillCaptureFlow mStillCaptureFlow = new StillCaptureFlow();
final TorchStateReset mTorchStateReset = new TorchStateReset();
+ private final DynamicRangesCompat mDynamicRangesCompat;
+
/**
* Constructor for CaptureSession.
*/
- CaptureSession() {
+ CaptureSession(@NonNull DynamicRangesCompat dynamicRangesCompat) {
mState = State.INITIALIZED;
+ mDynamicRangesCompat = dynamicRangesCompat;
mCaptureSessionStateCallback = new StateCallback();
}
@@ -416,6 +424,27 @@
outputConfiguration.addSurface(sharedSurface);
}
}
+
+ long dynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ DynamicRangeProfiles dynamicRangeProfiles =
+ mDynamicRangesCompat.toDynamicRangeProfiles();
+ if (dynamicRangeProfiles != null) {
+ DynamicRange requestedDynamicRange = outputConfig.getDynamicRange();
+ Long dynamicRangeProfileOrNull =
+ DynamicRangeConversions.dynamicRangeToFirstSupportedProfile(
+ requestedDynamicRange, dynamicRangeProfiles);
+ if (dynamicRangeProfileOrNull == null) {
+ Logger.e(TAG,
+ "Requested dynamic range is not supported. Defaulting to STANDARD "
+ + "dynamic range profile.\nRequested dynamic range:\n "
+ + requestedDynamicRange);
+ } else {
+ dynamicRangeProfile = dynamicRangeProfileOrNull;
+ }
+ }
+ }
+ outputConfiguration.setDynamicRangeProfile(dynamicRangeProfile);
return outputConfiguration;
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
index cdab65e..da5e717 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
@@ -26,6 +26,7 @@
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
import androidx.camera.camera2.impl.Camera2ImplConfig;
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat;
import androidx.camera.camera2.interop.CaptureRequestOptions;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.ImageAnalysis;
@@ -123,9 +124,10 @@
private int mInstanceId = 0;
ProcessingCaptureSession(@NonNull SessionProcessor sessionProcessor,
- @NonNull Camera2CameraInfoImpl camera2CameraInfoImpl, @NonNull Executor executor,
+ @NonNull Camera2CameraInfoImpl camera2CameraInfoImpl,
+ @NonNull DynamicRangesCompat dynamicRangesCompat, @NonNull Executor executor,
@NonNull ScheduledExecutorService scheduledExecutorService) {
- mCaptureSession = new CaptureSession();
+ mCaptureSession = new CaptureSession(dynamicRangesCompat);
mSessionProcessor = sessionProcessor;
mCamera2CameraInfoImpl = camera2CameraInfoImpl;
mExecutor = executor;
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java
index 0ab747c..1ca1a561 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java
@@ -29,7 +29,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.camera.core.impl.utils.MainThreadAsyncHandler;
import java.util.Map;
@@ -77,7 +77,7 @@
* Get a {@link CameraManagerCompat} instance from a provided {@link CameraManagerCompatImpl}.
*
*/
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
@NonNull
public static CameraManagerCompat from(@NonNull final CameraManagerCompatImpl impl) {
return new CameraManagerCompat(impl);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompat.java
index e7d3d64..0af3554 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompat.java
@@ -119,7 +119,7 @@
* Creates an instance from a framework android.hardware.camera2.params.DynamicRangeProfiles
* object.
*
- * @param dynamicRangeProfiles a {@link android.hardware.camera2.params.DynamicRangeProfiles).
+ * @param dynamicRangeProfiles a {@link android.hardware.camera2.params.DynamicRangeProfiles}.
* @return an equivalent {@link DynamicRangesCompat} object.
*/
@Nullable
@@ -138,16 +138,17 @@
/**
* Returns the underlying framework
- * {@link android.hardware.camera2.params.DynamicRangeProfiles).
+ * {@link android.hardware.camera2.params.DynamicRangeProfiles}.
*
- * @return the underlying {@link android.hardware.camera2.params.DynamicRangeProfiles).
+ * @return the underlying {@link android.hardware.camera2.params.DynamicRangeProfiles} or
+ * {@code null} if the device doesn't support 10 bit dynamic range.
*/
- @NonNull
+ @Nullable
@RequiresApi(33)
public DynamicRangeProfiles toDynamicRangeProfiles() {
Preconditions.checkState(Build.VERSION.SDK_INT >= 33, "DynamicRangesCompat can only be "
+ "converted to DynamicRangeProfiles on API 33 or higher.");
- return Preconditions.checkNotNull(mImpl.unwrap());
+ return mImpl.unwrap();
}
interface DynamicRangeProfilesCompatImpl {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java
index 5c0a513..ba916eb 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java
@@ -30,17 +30,18 @@
* Quirk denoting the video profile list returns by {@link EncoderProfiles} is invalid.
*
* <p>QuirkSummary
- * Bug Id: 267727595
+ * Bug Id: 267727595, 278860860
* Description: When using {@link EncoderProfiles} on TP1A or TD1A builds of Android API 33,
* {@link EncoderProfiles#getVideoProfiles()} returns a list with size one, but
* the single value in the list is null. This is not the expected behavior, and
* makes {@link EncoderProfiles} lack of video information.
- * Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33).
+ * Device(s): Pixel 4 and above pixel devices with TP1A or TD1A builds (API 33), Samsung devices
+ * with TP1A build (API 33).
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class InvalidVideoProfilesQuirk implements Quirk {
- static final List<String> AFFECTED_MODELS = Arrays.asList(
+ static final List<String> AFFECTED_PIXEL_MODELS = Arrays.asList(
"pixel 4",
"pixel 4a",
"pixel 4a (5g)",
@@ -55,22 +56,30 @@
);
static boolean load() {
- return isAffectedModel() && isAffectedBuild();
+ return isAffectedSamsungDevices() || isAffectedPixelDevices();
}
- private static boolean isAffectedModel() {
- return AFFECTED_MODELS.contains(Build.MODEL.toLowerCase(Locale.US));
+ private static boolean isAffectedSamsungDevices() {
+ return "samsung".equalsIgnoreCase(Build.BRAND) && isTp1aBuild();
}
- private static boolean isAffectedBuild() {
+ private static boolean isAffectedPixelDevices() {
+ return isAffectedPixelModel() && isAffectedPixelBuild();
+ }
+
+ private static boolean isAffectedPixelModel() {
+ return AFFECTED_PIXEL_MODELS.contains(Build.MODEL.toLowerCase(Locale.ROOT));
+ }
+
+ private static boolean isAffectedPixelBuild() {
return isTp1aBuild() || isTd1aBuild();
}
private static boolean isTp1aBuild() {
- return Build.ID.startsWith("TP1A");
+ return Build.ID.toLowerCase(Locale.ROOT).startsWith("tp1a");
}
private static boolean isTd1aBuild() {
- return Build.ID.startsWith("TD1A");
+ return Build.ID.toLowerCase(Locale.ROOT).startsWith("td1a");
}
}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompatTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompatTest.kt
index 76fdf54..56d280e 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompatTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompatTest.kt
@@ -177,6 +177,16 @@
@Config(minSdk = Build.VERSION_CODES.TIRAMISU)
@Test
+ fun producesNullDynamicRangeProfilesFromNullCharacteristics() {
+ val characteristics = newCameraCharacteristicsCompat()
+
+ val dynamicRangesCompat = DynamicRangesCompat.fromCameraCharacteristics(characteristics)
+
+ assertThat(dynamicRangesCompat.toDynamicRangeProfiles()).isNull()
+ }
+
+ @Config(minSdk = Build.VERSION_CODES.TIRAMISU)
+ @Test
fun canProduceDynamicRangesCompatFromCharacteristics() {
val characteristics = newCameraCharacteristicsCompat()
Shadow.extract<ShadowCameraCharacteristics>(
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 27c35c0..8eea5df 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
@@ -1697,11 +1697,6 @@
private boolean isNodeEnabled() {
checkMainThread();
ImageCaptureConfig config = (ImageCaptureConfig) getCurrentConfig();
- if (config.getImageReaderProxyProvider() != null) {
- // Use old pipeline for custom ImageReader.
- return false;
- }
-
if (config.getBufferFormat(ImageFormat.JPEG) != ImageFormat.JPEG) {
// Use old pipeline for non-JPEG output format.
return false;
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 e0f9eda..a96506f 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
@@ -36,6 +36,7 @@
import androidx.camera.core.ForwardingImageProxy;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.ImageProxy;
+import androidx.camera.core.ImageReaderProxyProvider;
import androidx.camera.core.ImageReaderProxys;
import androidx.camera.core.Logger;
import androidx.camera.core.MetadataImageReader;
@@ -101,7 +102,15 @@
Consumer<ProcessingRequest> requestConsumer;
ImageReaderProxy wrappedImageReader;
boolean hasMetadata = !inputEdge.isVirtualCamera();
- if (hasMetadata) {
+ if (inputEdge.getImageReaderProxyProvider() != null) {
+ wrappedImageReader = inputEdge.getImageReaderProxyProvider().newInstance(
+ size.getWidth(), size.getHeight(), format, MAX_IMAGES, 0);
+ requestConsumer = request -> {
+ // Ignore incoming requests. ImageReaderProxyProvider is only for unit tests. If
+ // we make it public later, we need to also drop the TakePictureRequest in the
+ // TakePictureManager.
+ };
+ } else if (hasMetadata) {
// Use MetadataImageReader if the input edge expects metadata.
MetadataImageReader metadataImageReader = new MetadataImageReader(size.getWidth(),
size.getHeight(), format, MAX_IMAGES);
@@ -292,6 +301,12 @@
abstract boolean isVirtualCamera();
/**
+ * Whether the pipeline is connected to a virtual camera.
+ */
+ @Nullable
+ abstract ImageReaderProxyProvider getImageReaderProxyProvider();
+
+ /**
* Edge that accepts {@link ProcessingRequest}.
*/
@NonNull
@@ -333,9 +348,10 @@
}
@NonNull
- static In of(Size size, int format, boolean isVirtualCamera) {
+ static In of(Size size, int format, boolean isVirtualCamera,
+ @Nullable ImageReaderProxyProvider imageReaderProxyProvider) {
return new AutoValue_CaptureNode_In(size, format, isVirtualCamera,
- new Edge<>(), new Edge<>());
+ imageReaderProxyProvider, new Edge<>(), new Edge<>());
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java
index 13b05cf..b4f0b28 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java
@@ -111,8 +111,11 @@
cameraEffect != null ? new InternalImageProcessor(cameraEffect) : null);
// Connect nodes
- mPipelineIn = CaptureNode.In.of(cameraSurfaceSize, mUseCaseConfig.getInputFormat(),
- isVirtualCamera);
+ mPipelineIn = CaptureNode.In.of(
+ cameraSurfaceSize,
+ mUseCaseConfig.getInputFormat(),
+ isVirtualCamera,
+ mUseCaseConfig.getImageReaderProxyProvider());
CaptureNode.Out captureOut = mCaptureNode.transform(mPipelineIn);
ProcessingNode.In processingIn = mBundlingNode.transform(captureOut);
mProcessingNode.transform(processingIn);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/DeferrableSurface.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/DeferrableSurface.java
index 803bfed..58deea4 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/DeferrableSurface.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/DeferrableSurface.java
@@ -27,6 +27,7 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.VisibleForTesting;
import androidx.camera.core.Logger;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.Futures;
@@ -331,7 +332,7 @@
return mPrescribedStreamFormat;
}
- @RestrictTo(Scope.TESTS)
+ @VisibleForTesting
public int getUseCount() {
synchronized (mLock) {
return mUseCount;
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
index 8662d08b..91b520a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
@@ -27,6 +27,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.camera.core.DynamicRange;
import androidx.camera.core.Logger;
import androidx.camera.core.internal.compat.workaround.SurfaceSorter;
@@ -110,6 +111,17 @@
public abstract int getSurfaceGroupId();
/**
+ * Returns the dynamic range for this output configuration.
+ *
+ * <p>The dynamic range will determine the dynamic range format and profile of pixels in
+ * the surfaces associated with this output configuration.
+ *
+ * <p>If not set, this defaults to {@link DynamicRange#SDR}.
+ */
+ @NonNull
+ public abstract DynamicRange getDynamicRange();
+
+ /**
* Creates the {@link Builder} instance with specified {@link DeferrableSurface}.
*/
@NonNull
@@ -118,7 +130,8 @@
.setSurface(surface)
.setSharedSurfaces(Collections.emptyList())
.setPhysicalCameraId(null)
- .setSurfaceGroupId(SURFACE_GROUP_ID_NONE);
+ .setSurfaceGroupId(SURFACE_GROUP_ID_NONE)
+ .setDynamicRange(DynamicRange.SDR);
}
/**
@@ -157,6 +170,15 @@
public abstract Builder setSurfaceGroupId(int surfaceGroupId);
/**
+ * Returns the dynamic range for this output configuration.
+ *
+ * <p>The dynamic range will determine the dynamic range format and profile of pixels in
+ * the surfaces associated with this output configuration.
+ */
+ @NonNull
+ public abstract Builder setDynamicRange(@NonNull DynamicRange dynamicRange);
+
+ /**
* Creates the instance.
*/
@NonNull
@@ -558,10 +580,27 @@
}
- /** Add a surface to the set that the session repeatedly writes data to. */
+ /**
+ * Add a surface to the set that the session repeatedly writes data to.
+ *
+ * <p>The dynamic range of this surface will default to {@link DynamicRange#SDR}. To
+ * manually set the dynamic range, use {@link #addSurface(DeferrableSurface, DynamicRange)}.
+ */
@NonNull
public Builder addSurface(@NonNull DeferrableSurface surface) {
- OutputConfig outputConfig = OutputConfig.builder(surface).build();
+ return addSurface(surface, DynamicRange.SDR);
+ }
+
+ /**
+ * Add a surface with the provided dynamic range to the set that the session repeatedly
+ * writes data to.
+ */
+ @NonNull
+ public Builder addSurface(@NonNull DeferrableSurface surface,
+ @NonNull DynamicRange dynamicRange) {
+ OutputConfig outputConfig = OutputConfig.builder(surface)
+ .setDynamicRange(dynamicRange)
+ .build();
mOutputConfigs.add(outputConfig);
mCaptureConfigBuilder.addSurface(surface);
return this;
@@ -581,10 +620,28 @@
return this;
}
- /** Add a surface for the session which only used for single captures. */
+ /**
+ * Add a surface for the session which only used for single captures.
+ *
+ * <p>The dynamic range of this surface will default to {@link DynamicRange#SDR}. To
+ * manually set the dynamic range, use
+ * {@link #addNonRepeatingSurface(DeferrableSurface, DynamicRange)}.
+ */
@NonNull
public Builder addNonRepeatingSurface(@NonNull DeferrableSurface surface) {
- OutputConfig outputConfig = OutputConfig.builder(surface).build();
+ return addNonRepeatingSurface(surface, DynamicRange.SDR);
+ }
+
+ /**
+ * Add a surface with the provided dynamic range for the session which only used for
+ * single captures.
+ */
+ @NonNull
+ public Builder addNonRepeatingSurface(@NonNull DeferrableSurface surface,
+ @NonNull DynamicRange dynamicRange) {
+ OutputConfig outputConfig = OutputConfig.builder(surface)
+ .setDynamicRange(dynamicRange)
+ .build();
mOutputConfigs.add(outputConfig);
return this;
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java
index 74389f1..d9c51b0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java
@@ -34,7 +34,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.CameraEffect;
import androidx.camera.core.Logger;
@@ -239,7 +238,7 @@
/**
* Returns the close state.
*/
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
public boolean isClosed() {
synchronized (mLock) {
return mIsClosed;
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
index 5581da2..75b44d2 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
@@ -306,14 +306,14 @@
}
@Test
- fun useImageReaderProvider_pipelineDisabled() {
+ fun useImageReaderProvider_pipelineEnabled() {
assertThat(
bindImageCapture(
useProcessingPipeline = true,
bufferFormat = ImageFormat.JPEG,
imageReaderProxyProvider = getImageReaderProxyProvider(),
).isProcessingPipelineEnabled
- ).isFalse()
+ ).isTrue()
}
@Test
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt
index acc1781..bf80cb5 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt
@@ -21,9 +21,11 @@
import android.os.Looper.getMainLooper
import android.util.Size
import androidx.camera.core.ImageProxy
+import androidx.camera.core.ImageReaderProxyProvider
import androidx.camera.core.imagecapture.Utils.createCaptureBundle
import androidx.camera.core.imagecapture.Utils.createFakeImage
import androidx.camera.core.impl.utils.futures.Futures
+import androidx.camera.testing.fakes.FakeImageReaderProxy
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -52,7 +54,7 @@
@Before
fun setUp() {
- captureNodeIn = CaptureNode.In.of(Size(10, 10), ImageFormat.JPEG, false)
+ captureNodeIn = CaptureNode.In.of(Size(10, 10), ImageFormat.JPEG, false, null)
captureNodeOut = captureNode.transform(captureNodeIn)
captureNodeOut.imageEdge.setListener {
imagePropagated.add(it)
@@ -68,6 +70,22 @@
}
@Test
+ fun hasImageReaderProxyProvider_useTheProvidedImageReader() {
+ // Arrange: create a fake ImageReaderProxyProvider.
+ val imageReader = FakeImageReaderProxy(CaptureNode.MAX_IMAGES)
+ val imageReaderProvider = ImageReaderProxyProvider { _, _, _, _, _ ->
+ imageReader
+ }
+ val input = CaptureNode.In.of(Size(10, 10), ImageFormat.JPEG, false, imageReaderProvider)
+ // Act: transform.
+ val node = CaptureNode()
+ node.transform(input)
+ // Assert: ImageReaderProxyProvider is used.
+ assertThat(input.surface.surface.get()).isEqualTo(imageReader.surface)
+ node.release()
+ }
+
+ @Test
fun release_imageReaderNotClosedUntilTermination() {
// Arrange: increment the DeferrableSurface's use count to prevent it from being terminated.
captureNode.inputEdge.surface.incrementUseCount()
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 de40c35..5b96555 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
@@ -28,6 +28,7 @@
import androidx.camera.core.ImageCapture.CaptureMode
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
+import androidx.camera.core.ImageReaderProxyProvider
import androidx.camera.core.SafeCloseImageReaderProxy
import androidx.camera.core.imagecapture.CaptureNode.MAX_IMAGES
import androidx.camera.core.imagecapture.ImagePipeline.JPEG_QUALITY_MAX_QUALITY
@@ -89,9 +90,10 @@
@Before
fun setUp() {
// Create ImageCaptureConfig.
- val builder = ImageCapture.Builder().setCaptureOptionUnpacker { _, builder ->
- builder.templateType = TEMPLATE_TYPE
- }
+ val builder = ImageCapture.Builder()
+ .setCaptureOptionUnpacker { _, builder ->
+ builder.templateType = TEMPLATE_TYPE
+ }
builder.mutableConfig.insertOption(OPTION_IO_EXECUTOR, mainThreadExecutor())
builder.mutableConfig.insertOption(ImageInputConfig.OPTION_INPUT_FORMAT, ImageFormat.JPEG)
imageCaptureConfig = builder.useCaseConfig
@@ -104,6 +106,31 @@
}
@Test
+ fun createPipeline_captureNodeHasImageReaderProxyProvider() {
+ // Arrange.
+ val imageReaderProxyProvider = ImageReaderProxyProvider { _, _, _, _, _ ->
+ FakeImageReaderProxy(MAX_IMAGES)
+ }
+ val builder = ImageCapture.Builder()
+ .setImageReaderProxyProvider(imageReaderProxyProvider)
+ .setCaptureOptionUnpacker { _, builder ->
+ builder.templateType = TEMPLATE_TYPE
+ }
+ builder.mutableConfig.insertOption(ImageInputConfig.OPTION_INPUT_FORMAT, ImageFormat.JPEG)
+ // Act.
+ val pipeline = ImagePipeline(builder.useCaseConfig, SIZE)
+ // Assert.
+ assertThat(pipeline.captureNode.inputEdge.imageReaderProxyProvider).isEqualTo(
+ imageReaderProxyProvider
+ )
+ }
+
+ @Test
+ fun createPipelineWithoutImageReaderProxyProvider_isNull() {
+ assertThat(imagePipeline.captureNode.inputEdge.imageReaderProxyProvider).isNull()
+ }
+
+ @Test
fun createPipelineWithVirtualCamera_receivesImageProxy() {
// Arrange: close the pipeline and create a new one not expecting metadata.
imagePipeline.close()
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 942e44b..1627bec 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -58,6 +58,7 @@
androidTestImplementation(project(":camera:camera-video"))
androidTestImplementation(project(":internal-testutils-truth"))
androidTestImplementation(project(":camera:camera-testlib-extensions"))
+ androidTestImplementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
// To use the testlib to have the implementation of the extensions-stub interface.
}
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 78fa363..f9e1b5a 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
@@ -72,6 +72,7 @@
import androidx.camera.testing.CameraUtil
import androidx.camera.testing.SurfaceTextureProvider
import androidx.camera.testing.fakes.FakeLifecycleOwner
+import androidx.concurrent.futures.await
import androidx.lifecycle.Observer
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
@@ -549,7 +550,7 @@
}
}
- private fun initBasicExtenderSessionProcessor(): AutoCloseable {
+ private suspend fun initBasicExtenderSessionProcessor(): AutoCloseable {
val width = 640
val height = 480
val maxImages = 2
@@ -557,9 +558,9 @@
val handlerThread = HandlerThread("CameraX-AutoDrainThread")
handlerThread.start()
val handler = Handler(handlerThread.looper)
- val surfaceTextureHolder = SurfaceTextureProvider.createAutoDrainingSurfaceTexture(
- CameraXExecutors.newHandlerExecutor(handler), width, height
- ) {}
+ val surfaceTextureHolder = SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync(
+ CameraXExecutors.newHandlerExecutor(handler), width, height, null
+ ) { handlerThread.quitSafely() }.await()
val previewOutputSurface = OutputSurface.create(
Surface(surfaceTextureHolder.surfaceTexture),
Size(width, height),
@@ -583,7 +584,6 @@
return AutoCloseable {
jpegImageReader.close()
surfaceTextureHolder.close()
- handlerThread.quitSafely()
}
}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
index 3672fcd..b6c7ced 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
@@ -24,7 +24,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.CameraProvider;
import androidx.camera.core.CameraSelector;
@@ -277,7 +276,7 @@
*/
// TODO: Will need to be rewritten to be threadsafe with use in conjunction with
// ExtensionsManager.init(...) if this is to be released for use outside of testing.
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
@NonNull
public ListenableFuture<Void> shutdown() {
synchronized (EXTENSIONS_LOCK) {
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
index 286acb9..7909211 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
@@ -36,8 +36,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
+import androidx.annotation.VisibleForTesting;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraEffect;
import androidx.camera.core.CameraFilter;
@@ -283,7 +282,7 @@
* future is a no-op.
* @hide
*/
- @RestrictTo(Scope.TESTS)
+ @VisibleForTesting
@NonNull
public ListenableFuture<Void> shutdown() {
runOnMainSync(this::unbindAll);
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
index 198e8c24..832c091 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
@@ -50,7 +50,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraX;
import androidx.camera.core.CameraXConfig;
@@ -556,7 +556,7 @@
* @param cameraCoordinator The camera coordinator for concurrent cameras.
* @param cameraSelector The selector to select cameras with.
*/
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
@NonNull
public static CameraUseCaseAdapter createCameraUseCaseAdapter(
@NonNull Context context,
@@ -591,7 +591,7 @@
* @param context The context used to initialize CameraX
* @param cameraSelector The selector to select cameras with.
*/
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
@NonNull
public static CameraUseCaseAdapter createCameraUseCaseAdapter(
@NonNull Context context,
@@ -616,7 +616,7 @@
* @param cameraSelector The selector to select cameras with.
* @param useCases The UseCases to attach to the CameraUseCaseAdapter.
*/
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
@NonNull
public static CameraUseCaseAdapter createCameraAndAttachUseCase(
@NonNull Context context,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java b/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java
index 8387b97..72c6e2f 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/SurfaceTextureProvider.java
@@ -35,8 +35,11 @@
import androidx.camera.core.Logger;
import androidx.camera.core.Preview;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.core.os.HandlerCompat;
+import com.google.common.util.concurrent.ListenableFuture;
+
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -107,7 +110,6 @@
* a surface for preview.
*
* <p> The {@link SurfaceTexture} will be released when it is no longer needed.
- *
*/
@NonNull
public static Preview.SurfaceProvider createSurfaceTextureProvider() {
@@ -154,72 +156,81 @@
handlerThread.start();
Handler handler = HandlerCompat.createAsync(handlerThread.getLooper());
Executor glContextExecutor = CameraXExecutors.newHandlerExecutor(handler);
- glContextExecutor.execute(() -> {
+ ListenableFuture<SurfaceTextureHolder> surfaceTextureFuture =
+ createAutoDrainingSurfaceTextureAsync(glContextExecutor,
+ surfaceRequest.getResolution().getWidth(),
+ surfaceRequest.getResolution().getHeight(), frameAvailableListener,
+ handlerThread::quitSafely);
+
+ surfaceTextureFuture.addListener(() -> {
+ try {
+ SurfaceTextureHolder holder = surfaceTextureFuture.get();
+ surfaceRequest.provideSurface(new Surface(holder.getSurfaceTexture()),
+ glContextExecutor,
+ (surfaceResponse) -> {
+ try {
+ holder.close();
+ } catch (Exception e) {
+ throw new AssertionError("SurfaceTextureHolder failed"
+ + " to close", e);
+ }
+ });
+ } catch (Exception e) {
+ // Should never happen
+ throw new AssertionError("Failed to create auto-draining surface "
+ + "texture",
+ e);
+ }
+ }, glContextExecutor);
+ };
+ }
+
+ /**
+ * Creates a {@link SurfaceTextureHolder} asynchronously that contains a {@link SurfaceTexture}
+ * which will automatically drain frames as new frames arrive.
+ *
+ * @param glExecutor the executor where the GL codes will run.
+ * @param width the width of the SurfaceTexture size
+ * @param height the height of the SurfaceTexture size.
+ * @param frameAvailableListener listener to be invoked when there are new frames.
+ * @param onClosed runnable which will be called after all resources managed by
+ * the SurfaceTextureHolder have been released.
+ */
+ @NonNull
+ public static ListenableFuture<SurfaceTextureHolder> createAutoDrainingSurfaceTextureAsync(
+ @NonNull Executor glExecutor,
+ int width,
+ int height,
+ @Nullable SurfaceTexture.OnFrameAvailableListener frameAvailableListener,
+ @Nullable Runnable onClosed) {
+ return CallbackToFutureAdapter.getFuture((completer) -> {
+ glExecutor.execute(() -> {
EGLContextParams contextParams = createDummyEGLContext();
EGL14.eglMakeCurrent(contextParams.display, contextParams.outputSurface,
contextParams.outputSurface, contextParams.context);
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
SurfaceTexture surfaceTexture = new SurfaceTexture(textureIds[0]);
- surfaceTexture.setDefaultBufferSize(surfaceRequest.getResolution().getWidth(),
- surfaceRequest.getResolution().getHeight());
- surfaceTexture.setOnFrameAvailableListener((st) -> {
- st.updateTexImage();
- if (frameAvailableListener != null) {
- frameAvailableListener.onFrameAvailable(st);
- }
- }, handler);
+ surfaceTexture.setDefaultBufferSize(width, height);
+ surfaceTexture.setOnFrameAvailableListener(it ->
+ glExecutor.execute(() -> {
+ it.updateTexImage();
+ if (frameAvailableListener != null) {
+ frameAvailableListener.onFrameAvailable(surfaceTexture);
+ }
+ }));
- Surface surface = new Surface(surfaceTexture);
- surfaceRequest.provideSurface(surface,
- glContextExecutor,
- (surfaceResponse) -> {
- surface.release();
+ completer.set(
+ new SurfaceTextureHolder(surfaceTexture, () -> glExecutor.execute(() -> {
surfaceTexture.release();
GLES20.glDeleteTextures(1, textureIds, 0);
terminateEGLContext(contextParams);
- handlerThread.quitSafely();
- });
+ if (onClosed != null) {
+ onClosed.run();
+ }
+ })));
});
- };
- }
-
- /**
- * Creates a {@link SurfaceTextureHolder} that contains a {@link SurfaceTexture} which will
- * automatically drain frames as new frames arrive.
- *
- * @param glExecutor the executor where the GL codes will run.
- * @param width the width of the SurfaceTexture size
- * @param height the height of the SurfaceTexture size.
- * @param frameAvailableListener listener to be invoked when there are new frames.
- */
- @NonNull
- public static SurfaceTextureHolder createAutoDrainingSurfaceTexture(
- @NonNull Executor glExecutor,
- int width,
- int height,
- @NonNull SurfaceTexture.OnFrameAvailableListener frameAvailableListener) {
- int[] textureIds = new int[1];
- SurfaceTexture surfaceTexture = new SurfaceTexture(textureIds[0]);
- EGLContextParams contextParams = createDummyEGLContext();
- glExecutor.execute(() -> {
- EGL14.eglMakeCurrent(contextParams.display, contextParams.outputSurface,
- contextParams.outputSurface, contextParams.context);
- GLES20.glGenTextures(1, textureIds, 0);
- surfaceTexture.setDefaultBufferSize(width, height);
- surfaceTexture.setOnFrameAvailableListener(it ->
- glExecutor.execute(() -> {
- it.updateTexImage();
- frameAvailableListener.onFrameAvailable(surfaceTexture);
- }));
- });
-
- return new SurfaceTextureHolder(surfaceTexture, () -> {
- glExecutor.execute(() -> {
- surfaceTexture.release();
- GLES20.glDeleteTextures(1, textureIds, 0);
- terminateEGLContext(contextParams);
- });
+ return "createAutoDrainingSurfaceTexture";
});
}
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/DeviceCompatibilityTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/DeviceCompatibilityTest.kt
index 7ee2e0b..9cfc292 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/DeviceCompatibilityTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/DeviceCompatibilityTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.media.MediaCodec
import android.media.MediaCodecInfo
+import android.os.Build
import androidx.camera.camera2.Camera2Config
import androidx.camera.camera2.pipe.integration.CameraPipeConfig
import androidx.camera.core.CameraSelector
@@ -118,13 +119,19 @@
// Act.
val (width, height) = videoProfile.width to videoProfile.height
+ // Pass if VideoCapabilities.isSizeSupported() is true
+ if (capabilities.isSizeSupported(width, height)) {
+ return@forEach
+ }
+
val supportedWidths = capabilities.supportedWidths
val supportedHeights = capabilities.supportedHeights
val supportedWidthsForHeight = capabilities.getWidthsForHeightQuietly(height)
val supportedHeightForWidth = capabilities.getHeightsForWidthQuietly(width)
// Assert.
- val msg = "mime: $mime, size: ${width}x$height is not in " +
+ val msg = "Build.BRAND: ${Build.BRAND}, Build.MODEL: ${Build.MODEL} " +
+ "mime: $mime, size: ${width}x$height is not in " +
"supported widths $supportedWidths/$supportedWidthsForHeight " +
"or heights $supportedHeights/$supportedHeightForWidth, " +
"the width/height alignment is " +
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSettingsAudioProfileResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSettingsAudioProfileResolverTest.kt
index 4424a86..553e0a2 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSettingsAudioProfileResolverTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSettingsAudioProfileResolverTest.kt
@@ -40,6 +40,7 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
+import androidx.test.rule.GrantPermissionRule
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
@@ -79,6 +80,11 @@
}
@get:Rule
+ val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
+ android.Manifest.permission.RECORD_AUDIO
+ )
+
+ @get:Rule
val cameraPipeConfigTestRule = CameraPipeConfigTestRule(
active = implName == CameraPipeConfig::class.simpleName,
)
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 c7a7b9b..8d1e292 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
@@ -741,7 +741,7 @@
*
*/
@Nullable
- @RestrictTo(Scope.TESTS)
+ @VisibleForTesting
SurfaceEdge getCameraEdge() {
return mCameraEdge;
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/MediaCodecInfoReportIncorrectInfoQuirk.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/MediaCodecInfoReportIncorrectInfoQuirk.java
index 9f31996..557a2a7 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/MediaCodecInfoReportIncorrectInfoQuirk.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/MediaCodecInfoReportIncorrectInfoQuirk.java
@@ -16,6 +16,9 @@
package androidx.camera.video.internal.compat.quirk;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_MPEG4;
+
import android.media.CamcorderProfile;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
@@ -23,12 +26,17 @@
import android.os.Build;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.core.impl.Quirk;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
/**
* <p>QuirkSummary
- * Bug Id: 192431846, 199582287, 218841498, 203481899, 216583006
+ * Bug Id: 192431846, 199582287, 218841498, 203481899, 216583006, 278843124, 278855948
* Description: Quirk which denotes {@link MediaCodecInfo} queried by {@link MediaCodecList}
* returns incorrect info.
* On Nokia 1, {@link CamcorderProfile} indicates it can support resolutions
@@ -55,19 +63,18 @@
* experimental result, H.264 + 3840x2160 can be used to record video on this
* device. Hence use quirk to workaround this case. See b/203481899#comment2.
* @link MediaCodecInfo} searched by {@link MediaCodecList#getCodecInfos()}
- * shows the maximum supported resolution of the AVC encoder is 1920x1072 on
- * Redmi note 4 and LG K10 LTE K430. However, the 1920x1080 option can be
- * successfully configured properly. See b/216583006.
- *
+ * shows the maximum supported resolution of the AVC encoder is 1920x1072.
+ * However, the 1920x1080 option can be successfully configured properly.
+ * See b/216583006, b/278843124, b/278855948.
* Device(s): Nokia 1, Motc C, X650, LG-X230, Positivo Twist 2 Pro, Huawei Mate9, Redmi note 4
- * , LG K10 LTE K430
+ * , LG K10 LTE K430, Samsung Galaxy A03 Core, Vivo Y75, Realme C11 2021
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class MediaCodecInfoReportIncorrectInfoQuirk implements Quirk {
static boolean load() {
return isNokia1() || isMotoC() || isX650() || isX230() || isHuaweiMate9()
- || isPositivoTwist2Pro() || isRedmiNote4() || isLGK430();
+ || isPositivoTwist2Pro() || isFHDProblematicDevice();
}
private static boolean isNokia1() {
@@ -96,39 +103,62 @@
Build.MODEL);
}
- private static boolean isRedmiNote4() {
- return "Xiaomi".equalsIgnoreCase(Build.BRAND) && "redmi note 4".equalsIgnoreCase(
- Build.MODEL);
- }
-
- private static boolean isLGK430() {
- return "lge".equalsIgnoreCase(Build.BRAND) && "lg-k430".equalsIgnoreCase(Build.MODEL);
- }
+ public static final List<String> INCORRECT_FHD_PROFILE_MODEL_LIST = Arrays.asList(
+ "lg-k430",
+ "redmi note 4",
+ "rmx3231",
+ "v2117",
+ "sm-a032f",
+ "moto g(20)",
+ "sm-a035m"
+ );
/** Check if problematic MediaFormat info for these candidate devices. */
public boolean isUnSupportMediaCodecInfo(@NonNull MediaFormat mediaFormat) {
+ MediaFormatResolver formatResolver = new MediaFormatResolver(mediaFormat);
if (isNokia1() || isMotoC() || isX650() || isX230() || isPositivoTwist2Pro()) {
- /** Checks if the given mime type is a problematic mime type. */
- String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
- return MediaFormat.MIMETYPE_VIDEO_MPEG4.equals(mimeType);
- } else if (isHuaweiMate9() && isVideoFormat(mediaFormat)) {
- /** Checks if this is an unsupported resolution for avc. */
- int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
- int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
- return (width == 3840 && height == 2160);
- } else if (isRedmiNote4() || isLGK430()) {
- if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(
- mediaFormat.getString(MediaFormat.KEY_MIME))) {
- int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
- int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
- return width == 1920 && height == 1080;
- }
+ return formatResolver.isMpeg4();
+ } else if (isHuaweiMate9()) {
+ return formatResolver.isVideo() && formatResolver.isSize(3840, 2160);
+ } else if (isFHDProblematicDevice()) {
+ return formatResolver.isAvc() && formatResolver.isSize(1920, 1080);
}
return false;
}
- private boolean isVideoFormat(@NonNull MediaFormat mediaFormat) {
- String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
- return mimeType.contains("video/");
+ private static boolean isFHDProblematicDevice() {
+ return INCORRECT_FHD_PROFILE_MODEL_LIST.contains(Build.MODEL.toLowerCase(Locale.US));
+ }
+
+ private static class MediaFormatResolver {
+ private final MediaFormat mMediaFormat;
+
+ MediaFormatResolver(@NonNull MediaFormat mediaFormat) {
+ mMediaFormat = mediaFormat;
+ }
+
+ boolean isVideo() {
+ String mimeType = getMime();
+ return mimeType != null && mimeType.contains("video/");
+ }
+
+ boolean isAvc() {
+ return MIMETYPE_VIDEO_AVC.equalsIgnoreCase(getMime());
+ }
+
+ boolean isMpeg4() {
+ return MIMETYPE_VIDEO_MPEG4.equalsIgnoreCase(getMime());
+ }
+
+ boolean isSize(int width, int height) {
+ int formatWidth = mMediaFormat.getInteger(MediaFormat.KEY_WIDTH);
+ int formatHeight = mMediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
+ return formatWidth == width && formatHeight == height;
+ }
+
+ @Nullable
+ private String getMime() {
+ return mMediaFormat.getString(MediaFormat.KEY_MIME);
+ }
}
}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/LifecycleCameraController.java b/camera/camera-view/src/main/java/androidx/camera/view/LifecycleCameraController.java
index 4a91e89..2f29672 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/LifecycleCameraController.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/LifecycleCameraController.java
@@ -16,8 +16,6 @@
package androidx.camera.view;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
-
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -28,7 +26,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
-import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraEffect;
@@ -154,7 +151,7 @@
/**
*/
- @RestrictTo(TESTS)
+ @VisibleForTesting
@SuppressWarnings("FutureReturnValueIgnored")
void shutDownForTests() {
if (mCameraProvider != null) {
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapper.java b/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapper.java
index 3247c70..b42e391 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapper.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapper.java
@@ -18,7 +18,7 @@
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraSelector;
@@ -68,6 +68,6 @@
*
*/
@NonNull
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
ListenableFuture<Void> shutdown();
}
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapperImpl.java b/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapperImpl.java
index fb13889..30b38fc 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapperImpl.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/ProcessCameraProviderWrapperImpl.java
@@ -20,6 +20,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.annotation.VisibleForTesting;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraSelector;
@@ -66,6 +67,7 @@
return mProcessCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);
}
+ @VisibleForTesting
@NonNull
@Override
public ListenableFuture<Void> shutdown() {
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 ce1894b2..8154dd9 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
@@ -458,10 +458,10 @@
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 12c9de0..2c3dd80 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
@@ -61,6 +61,8 @@
private boolean mIsPreviewCropRectPrecalculated = false;
private Size mPreviewSize;
private int mTextureRotationDegrees;
+ private int mSurfaceRequestRotationDegrees;
+ private boolean mHasCameraTransform;
// Transform retrieved by SurfaceTexture.getTransformMatrix
private final float[] mTextureTransform = new float[16];
@@ -128,6 +130,9 @@
mExecutor,
transformationInfo -> {
mMvpDirty = true;
+ mHasCameraTransform = transformationInfo.hasCameraTransform();
+ mSurfaceRequestRotationDegrees =
+ transformationInfo.getRotationDegrees();
if (!isCropRectFullTexture(transformationInfo.getCropRect())) {
// Crop rect is pre-calculated. Use it directly.
mPreviewCropRect = new RectF(
@@ -495,7 +500,15 @@
// device is rotated in a counter-clockwise direction and our world-space coordinates
// define positive angles in the clockwise direction, we add the two together to get the
// total angle required.
- return (mTextureRotationDegrees + mSurfaceRotationDegrees) % 360;
+ if (mHasCameraTransform) {
+ // If the Surface is connected to the camera, there is surface rotation encoded in
+ // the SurfaceTexture.
+ return (mTextureRotationDegrees + mSurfaceRotationDegrees) % 360;
+ } else {
+ // When the Surface is connected to an internal OpenGl renderer, the texture rotation
+ // is always 0. Use the rotation provided by SurfaceRequest instead.
+ return mSurfaceRequestRotationDegrees;
+ }
}
/**
@@ -540,7 +553,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, 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/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
index 429f3c8..2b39b56 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
@@ -42,6 +42,7 @@
import androidx.camera.testing.CameraUtil
import androidx.camera.testing.LabTestRule
import androidx.camera.testing.SurfaceTextureProvider
+import androidx.concurrent.futures.await
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executors
import kotlinx.coroutines.CompletableDeferred
@@ -100,15 +101,14 @@
val executorForGL = Executors.newSingleThreadExecutor()
// Some OEM requires frames drain (updateTexImage being invoked) in SurfaceTexture,
// otherwise it might cause still capture to fail.
- val surfaceTextureHolder = SurfaceTextureProvider.createAutoDrainingSurfaceTexture(
+ val surfaceTextureHolder = SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync(
executorForGL,
previewSize.width,
- previewSize.height
- ) {
- if (!deferredPreviewFrame.isCompleted) {
- deferredPreviewFrame.complete(it)
- }
- }
+ previewSize.height, {
+ if (!deferredPreviewFrame.isCompleted) {
+ deferredPreviewFrame.complete(it)
+ }
+ }) { executorForGL.shutdown() }.await()
val previewSurface = Surface(surfaceTextureHolder.surfaceTexture)
// Still capture surface
@@ -182,7 +182,6 @@
previewSurface.release()
captureSurface.release()
surfaceTextureHolder.close()
- executorForGL.shutdown()
}
/**
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
index edecc85..1d771cc 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
@@ -58,7 +58,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresPermission;
-import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
@@ -610,30 +609,22 @@
// For testing
// -----------------
- /**
- */
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
LifecycleCameraController getCameraController() {
return mCameraController;
}
- /**
- */
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
void setWrappedAnalyzer(@Nullable ImageAnalysis.Analyzer analyzer) {
mWrappedAnalyzer = analyzer;
}
- /**
- */
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
PreviewView getPreviewView() {
return mPreviewView;
}
- /**
- */
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
int getSensorRotation() {
return mRotation;
}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/ICarAppActivity.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/ICarAppActivity.aidl
new file mode 100644
index 0000000..26e7e22
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/ICarAppActivity.aidl
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface ICarAppActivity {
+ oneway void setSurfacePackage(in androidx.car.app.serialization.Bundleable surfacePackage) = 1;
+ oneway void setSurfaceListener(androidx.car.app.activity.renderer.surface.ISurfaceListener listener) = 2;
+ oneway void registerRendererCallback(androidx.car.app.activity.renderer.IRendererCallback callback) = 3;
+ oneway void onStartInput() = 4;
+ oneway void onStopInput() = 5;
+ oneway void startCarApp(in android.content.Intent intent) = 6;
+ oneway void finishCarApp() = 7;
+ oneway void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd) = 8;
+ oneway void setInsetsListener(androidx.car.app.activity.renderer.IInsetsListener listener) = 9;
+ oneway void showAssist(in android.os.Bundle args) = 10;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IInsetsListener.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IInsetsListener.aidl
new file mode 100644
index 0000000..1f3159d
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IInsetsListener.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface IInsetsListener {
+ /**
+ * @deprecated Use onWindowInsetsChanged(Insets, Insets) instead.
+ */
+ void onInsetsChanged(in android.graphics.Insets insets) = 1;
+ void onWindowInsetsChanged(in android.graphics.Insets insets, in android.graphics.Insets safeInsets) = 2;
+}
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
new file mode 100644
index 0000000..a0e60d4
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IProxyInputConnection.aidl
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface IProxyInputConnection {
+ CharSequence getTextBeforeCursor(int length, int flags) = 1;
+ CharSequence getTextAfterCursor(int length, int flags) = 2;
+ CharSequence getSelectedText(int flags) = 3;
+ int getCursorCapsMode(int reqModes) = 4;
+ boolean deleteSurroundingText(int beforeLength, int afterLength) = 5;
+ boolean setComposingText(CharSequence text, int newCursorPosition) = 6;
+ boolean setComposingRegion(int start, int end) = 7;
+ boolean finishComposingText() = 8;
+ boolean commitText(CharSequence text, int newCursorPosition) = 9;
+ boolean setSelection(int start, int end) = 10;
+ boolean performEditorAction(int editorAction) = 11;
+ boolean performContextMenuAction(int id) = 12;
+ boolean beginBatchEdit() = 13;
+ boolean endBatchEdit() = 14;
+ boolean sendKeyEvent(in android.view.KeyEvent event) = 15;
+ boolean clearMetaKeyStates(int states) = 16;
+ boolean reportFullscreenMode(boolean enabled) = 17;
+ boolean performPrivateCommand(String action, in android.os.Bundle data) = 18;
+ boolean requestCursorUpdates(int cursorUpdateMode) = 19;
+ boolean commitCorrection(in android.view.inputmethod.CorrectionInfo correctionInfo) = 20;
+ boolean commitCompletion(in android.view.inputmethod.CompletionInfo text) = 21;
+ android.view.inputmethod.ExtractedText getExtractedText(in android.view.inputmethod.ExtractedTextRequest request, int flags) = 22;
+ void closeConnection() = 23;
+ android.view.inputmethod.EditorInfo getEditorInfo() = 24;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererCallback.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererCallback.aidl
new file mode 100644
index 0000000..8233bd1
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererCallback.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface IRendererCallback {
+ void onBackPressed() = 1;
+ void onCreate() = 2;
+ void onStart() = 3;
+ void onResume() = 4;
+ void onPause() = 5;
+ void onStop() = 6;
+ void onDestroyed() = 7;
+ androidx.car.app.activity.renderer.IProxyInputConnection onCreateInputConnection(in android.view.inputmethod.EditorInfo editorInfo) = 8;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererService.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererService.aidl
new file mode 100644
index 0000000..f2b84b8
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/IRendererService.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer;
+/* @hide */
+interface IRendererService {
+ boolean initialize(androidx.car.app.activity.renderer.ICarAppActivity carActivity, in android.content.ComponentName serviceName, int displayId) = 1;
+ boolean onNewIntent(in android.content.Intent intent, in android.content.ComponentName serviceName, int displayId) = 2;
+ void terminate(in android.content.ComponentName serviceName) = 3;
+ androidx.car.app.serialization.Bundleable performHandshake(in android.content.ComponentName serviceName, int appLatestApiLevel) = 4;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceControl.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceControl.aidl
new file mode 100644
index 0000000..700162d
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceControl.aidl
@@ -0,0 +1,41 @@
+/**
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer.surface;
+/* @hide */
+interface ISurfaceControl {
+ oneway void setSurfaceWrapper(in androidx.car.app.serialization.Bundleable surfaceWrapper) = 1;
+ oneway void onTouchEvent(in android.view.MotionEvent event) = 2;
+ oneway void onWindowFocusChanged(boolean hasFocus, boolean isInTouchMode) = 3;
+ oneway void onKeyEvent(in android.view.KeyEvent event) = 4;
+}
diff --git a/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceListener.aidl b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceListener.aidl
new file mode 100644
index 0000000..582319c1
--- /dev/null
+++ b/car/app/app-automotive/api/aidlRelease/current/androidx/car/app/activity/renderer/surface/ISurfaceListener.aidl
@@ -0,0 +1,40 @@
+/**
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package androidx.car.app.activity.renderer.surface;
+/* @hide */
+interface ISurfaceListener {
+ oneway void onSurfaceAvailable(in androidx.car.app.serialization.Bundleable surfaceWrapper) = 1;
+ oneway void onSurfaceChanged(in androidx.car.app.serialization.Bundleable surfaceWrapper) = 2;
+ oneway void onSurfaceDestroyed(in androidx.car.app.serialization.Bundleable surfaceWrapper) = 3;
+}
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/content/ComponentName.aidl b/car/app/app-automotive/src/main/stableAidlImports/android/content/ComponentName.aidl
deleted file mode 100644
index f1529b1..0000000
--- a/car/app/app-automotive/src/main/stableAidlImports/android/content/ComponentName.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.content;
-
-@JavaOnlyStableParcelable parcelable ComponentName;
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/content/Intent.aidl b/car/app/app-automotive/src/main/stableAidlImports/android/content/Intent.aidl
deleted file mode 100644
index 0c8c241..0000000
--- a/car/app/app-automotive/src/main/stableAidlImports/android/content/Intent.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.content;
-
-@JavaOnlyStableParcelable parcelable Intent;
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/os/Bundle.aidl b/car/app/app-automotive/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/car/app/app-automotive/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/car/app/app-automotive/src/main/stableAidlImports/android/view/KeyEvent.aidl b/car/app/app-automotive/src/main/stableAidlImports/android/view/KeyEvent.aidl
deleted file mode 100644
index cfaff66..0000000
--- a/car/app/app-automotive/src/main/stableAidlImports/android/view/KeyEvent.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.view;
-
-@JavaOnlyStableParcelable parcelable KeyEvent;
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
index fb180a2..e724acf 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
@@ -125,12 +125,12 @@
<string name="package_not_found_error_msg" msgid="7525619456883627939">"ጥቅል አልተገኘም።"</string>
<string name="permissions_granted_msg" msgid="2348556088141992714">"ሁሉም ፈቃዶች ተሰጥተዋል። እባክዎን ከቅንብሮች ፈቃዶችን ይሻሩ።"</string>
<string name="needs_access_msg_prefix" msgid="2204136858798832382">"መተግበሪያው ለሚከተሉት ፈቃዶች መዳረሻ ያስፈልገዋል፦\n"</string>
- <string name="phone_screen_permission_msg" msgid="3599815596923367256">"በስልኩ ማያ ገጽ ላይ ፈቃድ ይስጡ"</string>
+ <string name="phone_screen_permission_msg" msgid="3599815596923367256">"በስልኩ ማያ ገፅ ላይ ፈቃድ ይስጡ"</string>
<string name="enable_location_permission_on_device_msg" msgid="472752487966156897">"በመሣሪያ ላይ የአካባቢ ፈቃዶችን አንቃ"</string>
- <string name="enable_location_permission_on_phone_msg" msgid="5082615523959139121">"በስልኩ ማያ ገጽ ላይ አካባቢን አንቃ"</string>
+ <string name="enable_location_permission_on_phone_msg" msgid="5082615523959139121">"በስልኩ ማያ ገፅ ላይ አካባቢን አንቃ"</string>
<string name="required_permissions_title" msgid="5351791879153568211">"የሚያስፈልጉ ፈቃዶች"</string>
<string name="request_permissions_title" msgid="7456426341142412300">"የፈቃድ ቅንጭብ ማሳያን ጠይቅ"</string>
- <string name="cancel_reservation_title" msgid="1374986823057959608">"የቦታ ማስያዣ ማይ ገጽ ይቅር"</string>
+ <string name="cancel_reservation_title" msgid="1374986823057959608">"የቦታ ማስያዣ ማይ ገፅ ይቅር"</string>
<string name="reservation_cancelled_msg" msgid="6334213670275547875">"ቦታ ማስያዣ ተሰርዟል"</string>
<string name="result_demo_title" msgid="3900525190662148290">"የውጤት ቅንጭብ ማሳያ"</string>
<string name="not_started_for_result_msg" msgid="7498800528148447270">"ይህ መተግበሪያ ለውጤት አልተጀመረም"</string>
@@ -298,7 +298,7 @@
<string name="some_additional_text" msgid="4009872495806318260">"አንዳንድ ተጨማሪ ጽሁፍ"</string>
<string name="sample_additional_list" msgid="5085372891301576306">"የናሙና ሊመረጥ የሚችል ዝርዝር"</string>
<string name="task_restriction_demo_title" msgid="2212084350718766941">"የተግባር ገደብ ቅንጭብ ማሳያ"</string>
- <string name="task_limit_reached_msg" msgid="7162842196382260992">"ይህ የእርምጃ ቁጥር ፍሰቱ ከመጠን በላይ እንዲሆን ያደርገዋል እና ወደ አዲስ ማያ ገጽ ይመራዎታል።"</string>
+ <string name="task_limit_reached_msg" msgid="7162842196382260992">"ይህ የእርምጃ ቁጥር ፍሰቱ ከመጠን በላይ እንዲሆን ያደርገዋል እና ወደ አዲስ ማያ ገፅ ይመራዎታል።"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"የተግባር እርምጃ %1$d ከ%2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ወደፊት ለመሄድ ጠቅ ያድርጉ"</string>
<string name="task_content_allowed" msgid="545061986612431190">"እባክዎ የተለያዩ የቅንብር ደንቦችን ይጎብኙ እና መኪናው የማሽከርከር ሁነታ ላይ እንደሆነ ያረጋግጡ"</string>
@@ -333,7 +333,7 @@
<string name="showcase_demos_title" msgid="1542092687878113304">"የመሳያ ቅንጭብ ማሳያ"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"የቅንብር ደንብ አቀማመጥ ቅንጭብ ማሳያዎች"</string>
<string name="grid_template_menu_demo_title" msgid="7096285873490705119">"የፍርግርግ ቅንብር ደንብ ቅንጭብ ማሳያዎች"</string>
- <string name="voice_access_demo_title" msgid="3825223890895361496">"የድምጽ መዳረሻ ቅንጭብ ማሳያ ማያ ገጽ"</string>
+ <string name="voice_access_demo_title" msgid="3825223890895361496">"የድምጽ መዳረሻ ቅንጭብ ማሳያ ማያ ገፅ"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"የተጠቃሚ መስተጋብሮች"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"የቅንጭብ ማሳያ ፈቃዶችን ይጠይቁ"</string>
<string name="application_overflow_title" msgid="396427940886169325">"የመተግበሪያ ትርፍ አረጋጋጭ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
index 6a4c7b2a..5408e2f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
@@ -226,7 +226,7 @@
<string name="title_prefix" msgid="3991742709199357049">"標題"</string>
<string name="list_template_demo_title" msgid="1740208242737246151">"「清單範本」示範"</string>
<string name="long_msg_template_demo_title" msgid="1793748562161438131">"「長訊息範本」示範"</string>
- <string name="long_msg_template_not_supported_text" msgid="3641559637317672505">"您的主機不支援「長訊息」範本"</string>
+ <string name="long_msg_template_not_supported_text" msgid="3641559637317672505">"你的主機不支援「長訊息」範本"</string>
<string name="long_msg_template_not_supported_title" msgid="8600719470226274925">"主機不兼容"</string>
<string name="msg_template_demo_title" msgid="3895210951340409473">"「訊息範本」示範"</string>
<string name="msg_template_demo_text" msgid="2275291617716161409">"訊息會在這裡顯示。\n其他文字會在第二行顯示。"</string>
@@ -240,7 +240,7 @@
<string name="google_sign_in" msgid="6556259799319701727">"Google 登入"</string>
<string name="use_pin" msgid="7850893299484337431">"使用 PIN"</string>
<string name="qr_code" msgid="5487041647280777397">"QR 碼"</string>
- <string name="sign_in_template_not_supported_text" msgid="7184733753948837646">"您的主機不支援登入範本"</string>
+ <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>
@@ -254,7 +254,7 @@
<string name="qr_code_sign_in_title" msgid="8137070561006464518">"掃瞄 QR 碼即登入"</string>
<string name="sign_in_with_google_title" msgid="8043752000786977249">"使用 Google 帳戶登入"</string>
<string name="provider_sign_in_instruction" msgid="7586815688292506743">"使用這個按鈕可完成 Google 登入程序"</string>
- <string name="sign_in_complete_text" msgid="8423984266325680606">"您已登入!"</string>
+ <string name="sign_in_complete_text" msgid="8423984266325680606">"你已登入!"</string>
<string name="sign_in_complete_title" msgid="8919868148773983428">"已完成登入"</string>
<string name="sign_in_template_demo_title" msgid="6052035424941410249">"「登入範本」示範"</string>
<string name="tab_template_layouts_demo_title" msgid="6529681462424538165">"分頁範本示範"</string>
@@ -298,7 +298,7 @@
<string name="some_additional_text" msgid="4009872495806318260">"一些其他文字"</string>
<string name="sample_additional_list" msgid="5085372891301576306">"可選取的清單範本"</string>
<string name="task_restriction_demo_title" msgid="2212084350718766941">"「工作限制」示範"</string>
- <string name="task_limit_reached_msg" msgid="7162842196382260992">"這會導致步數超過上限,並將您向到新畫面。"</string>
+ <string name="task_limit_reached_msg" msgid="7162842196382260992">"這會導致步數超過上限,並將你向到新畫面。"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"工作步驟 %1$d,共 %2$d 個步驟"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"點擊即可繼續使用"</string>
<string name="task_content_allowed" msgid="545061986612431190">"請查看各種範本,並確保汽車處於駕駛模式"</string>
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index c8ce9a4..ebb7251 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -660,6 +660,10 @@
method public androidx.car.app.model.OnClickDelegate getOnClickDelegate();
}
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
+ method public String getContentId();
+ }
+
@androidx.car.app.annotations.CarProtocol public final class DateTimeWithZone {
method public static androidx.car.app.model.DateTimeWithZone create(long, @IntRange(from=0xffff02e0, to=64800) int, String);
method public static androidx.car.app.model.DateTimeWithZone create(long, java.util.TimeZone);
@@ -1064,6 +1068,60 @@
method public androidx.car.app.model.ItemList getItemList();
}
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
+ method public String getContentId();
+ method public androidx.car.app.model.CarIcon getIcon();
+ method public androidx.car.app.model.CarText getTitle();
+ }
+
+ public static final class Tab.Builder {
+ ctor public Tab.Builder();
+ ctor public Tab.Builder(androidx.car.app.model.Tab);
+ method public androidx.car.app.model.Tab build();
+ method public androidx.car.app.model.Tab.Builder setContentId(String);
+ method public androidx.car.app.model.Tab.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Tab.Builder setTitle(CharSequence);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
+ method public void sendTabSelected(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
+ method public String getContentId();
+ method public androidx.car.app.model.Template getTemplate();
+ field public static final String CONTENT_ID = "TAB_CONTENTS_CONTENT_ID";
+ }
+
+ public static final class TabContents.Builder {
+ ctor public TabContents.Builder(androidx.car.app.model.Template);
+ method public androidx.car.app.model.TabContents build();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
+ method public String getActiveTabContentId();
+ method public androidx.car.app.model.Action getHeaderAction();
+ method public androidx.car.app.model.TabCallbackDelegate getTabCallbackDelegate();
+ method public androidx.car.app.model.TabContents getTabContents();
+ method public java.util.List<androidx.car.app.model.Tab!> getTabs();
+ method public boolean isLoading();
+ }
+
+ public static final class TabTemplate.Builder {
+ ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate.TabCallback);
+ ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate);
+ method public androidx.car.app.model.TabTemplate.Builder addTab(androidx.car.app.model.Tab);
+ method public androidx.car.app.model.TabTemplate build();
+ method public androidx.car.app.model.TabTemplate.Builder setActiveTabContentId(String);
+ method public androidx.car.app.model.TabTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.TabTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.TabTemplate.Builder setTabContents(androidx.car.app.model.TabContents);
+ }
+
+ public static interface TabTemplate.TabCallback {
+ method public default void onTabSelected(String);
+ }
+
@androidx.car.app.annotations.CarProtocol public interface Template {
}
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 02f26c1..1e53eca 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -1097,7 +1097,7 @@
method public androidx.car.app.model.OnClickDelegate getOnClickDelegate();
}
- @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
method public String getContentId();
}
@@ -1525,7 +1525,7 @@
method public androidx.car.app.model.ItemList getItemList();
}
- @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
method public String getContentId();
method public androidx.car.app.model.CarIcon getIcon();
method public androidx.car.app.model.CarText getTitle();
@@ -1540,11 +1540,11 @@
method public androidx.car.app.model.Tab.Builder setTitle(CharSequence);
}
- @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
method public void sendTabSelected(String, androidx.car.app.OnDoneCallback);
}
- @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
method public String getContentId();
method public androidx.car.app.model.Template getTemplate();
field public static final String CONTENT_ID = "TAB_CONTENTS_CONTENT_ID";
@@ -1555,7 +1555,7 @@
method public androidx.car.app.model.TabContents build();
}
- @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
method public String getActiveTabContentId();
method public androidx.car.app.model.Action getHeaderAction();
method public androidx.car.app.model.TabCallbackDelegate getTabCallbackDelegate();
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index c8ce9a4..ebb7251 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -660,6 +660,10 @@
method public androidx.car.app.model.OnClickDelegate getOnClickDelegate();
}
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
+ method public String getContentId();
+ }
+
@androidx.car.app.annotations.CarProtocol public final class DateTimeWithZone {
method public static androidx.car.app.model.DateTimeWithZone create(long, @IntRange(from=0xffff02e0, to=64800) int, String);
method public static androidx.car.app.model.DateTimeWithZone create(long, java.util.TimeZone);
@@ -1064,6 +1068,60 @@
method public androidx.car.app.model.ItemList getItemList();
}
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
+ method public String getContentId();
+ method public androidx.car.app.model.CarIcon getIcon();
+ method public androidx.car.app.model.CarText getTitle();
+ }
+
+ public static final class Tab.Builder {
+ ctor public Tab.Builder();
+ ctor public Tab.Builder(androidx.car.app.model.Tab);
+ method public androidx.car.app.model.Tab build();
+ method public androidx.car.app.model.Tab.Builder setContentId(String);
+ method public androidx.car.app.model.Tab.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Tab.Builder setTitle(CharSequence);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
+ method public void sendTabSelected(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
+ method public String getContentId();
+ method public androidx.car.app.model.Template getTemplate();
+ field public static final String CONTENT_ID = "TAB_CONTENTS_CONTENT_ID";
+ }
+
+ public static final class TabContents.Builder {
+ ctor public TabContents.Builder(androidx.car.app.model.Template);
+ method public androidx.car.app.model.TabContents build();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
+ method public String getActiveTabContentId();
+ method public androidx.car.app.model.Action getHeaderAction();
+ method public androidx.car.app.model.TabCallbackDelegate getTabCallbackDelegate();
+ method public androidx.car.app.model.TabContents getTabContents();
+ method public java.util.List<androidx.car.app.model.Tab!> getTabs();
+ method public boolean isLoading();
+ }
+
+ public static final class TabTemplate.Builder {
+ ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate.TabCallback);
+ ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate);
+ method public androidx.car.app.model.TabTemplate.Builder addTab(androidx.car.app.model.Tab);
+ method public androidx.car.app.model.TabTemplate build();
+ method public androidx.car.app.model.TabTemplate.Builder setActiveTabContentId(String);
+ method public androidx.car.app.model.TabTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.TabTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.TabTemplate.Builder setTabContents(androidx.car.app.model.TabContents);
+ }
+
+ public static interface TabTemplate.TabCallback {
+ method public default void onTabSelected(String);
+ }
+
@androidx.car.app.annotations.CarProtocol public interface Template {
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Content.java b/car/app/app/src/main/java/androidx/car/app/model/Content.java
index 5023c429..c493b66 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Content.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Content.java
@@ -18,12 +18,10 @@
import androidx.annotation.NonNull;
import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.RequiresCarApi;
/** Interface implemented by models that can be invalidated and refreshed individually. */
@CarProtocol
-@ExperimentalCarApi
@RequiresCarApi(6)
public interface Content {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Tab.java b/car/app/app/src/main/java/androidx/car/app/model/Tab.java
index d99fd60..f0292db 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Tab.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Tab.java
@@ -21,7 +21,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.KeepFields;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.model.constraints.CarIconConstraints;
@@ -34,7 +33,6 @@
* display tab headers.
*/
@CarProtocol
-@ExperimentalCarApi
@RequiresCarApi(6)
@KeepFields
public final class Tab implements Content {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegate.java
index ddb229b..6cf0bcb 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegate.java
@@ -21,7 +21,6 @@
import androidx.annotation.NonNull;
import androidx.car.app.OnDoneCallback;
import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.RequiresCarApi;
/**
@@ -29,7 +28,6 @@
* {@link androidx.car.app.model.TabTemplate.TabCallback} events to the car app.
*/
@CarProtocol
-@ExperimentalCarApi
@RequiresCarApi(6)
public interface TabCallbackDelegate {
/**
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java
index 4e559ca..ed7a988 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java
@@ -23,16 +23,14 @@
import android.annotation.SuppressLint;
import android.os.RemoteException;
-import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.car.app.IOnDoneCallback;
import androidx.car.app.OnDoneCallback;
import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
-import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.annotations.KeepFields;
+import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.utils.RemoteUtils;
/**
@@ -42,7 +40,6 @@
*/
@RestrictTo(LIBRARY)
@CarProtocol
-@ExperimentalCarApi
@RequiresCarApi(6)
@KeepFields
public class TabCallbackDelegateImpl implements TabCallbackDelegate {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabContents.java b/car/app/app/src/main/java/androidx/car/app/model/TabContents.java
index d27dc30..b16cbd3 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabContents.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabContents.java
@@ -23,10 +23,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
+import androidx.car.app.annotations.KeepFields;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.model.constraints.TabContentsConstraints;
-import androidx.car.app.annotations.KeepFields;
import java.util.Objects;
@@ -34,7 +33,6 @@
* Represents the contents to display for a selected tab in a {@link TabTemplate}.
*/
@CarProtocol
-@ExperimentalCarApi
@RequiresCarApi(6)
@KeepFields
public class TabContents implements Content {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java
index 1a55793..e216793 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java
@@ -26,7 +26,6 @@
import androidx.annotation.Nullable;
import androidx.car.app.Screen;
import androidx.car.app.annotations.CarProtocol;
-import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.KeepFields;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.model.constraints.TabsConstraints;
@@ -55,7 +54,6 @@
* </ul>
*/
@CarProtocol
-@ExperimentalCarApi
@RequiresCarApi(6)
@KeepFields
public class TabTemplate implements Template {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index edf1329..a269ea2 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -25,7 +25,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
-import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.model.Action;
import androidx.car.app.model.Action.ActionType;
@@ -161,7 +160,6 @@
/** Constraints for TabTemplate. */
@NonNull
- @ExperimentalCarApi
@RequiresCarApi(6)
public static final ActionsConstraints ACTIONS_CONSTRAINTS_TABS =
new ActionsConstraints.Builder(ACTIONS_CONSTRAINTS_HEADER)
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java
index b1d917901..664eaa0 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java
@@ -18,7 +18,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
-import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.model.CarText;
import androidx.car.app.model.GridTemplate;
@@ -37,7 +36,6 @@
*
* @hide
*/
-@ExperimentalCarApi
@RequiresCarApi(6)
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class TabContentsConstraints {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabsConstraints.java
index d80ab48..83bcf62 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabsConstraints.java
@@ -18,7 +18,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
-import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.model.Tab;
@@ -31,7 +30,6 @@
*
* @hide
*/
-@ExperimentalCarApi
@RequiresCarApi(6)
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class TabsConstraints {
diff --git a/compose/compiler/compiler-hosted/integration-tests/build.gradle b/compose/compiler/compiler-hosted/integration-tests/build.gradle
index 149e956..0ac6b78 100644
--- a/compose/compiler/compiler-hosted/integration-tests/build.gradle
+++ b/compose/compiler/compiler-hosted/integration-tests/build.gradle
@@ -32,9 +32,7 @@
testCompileOnly(libs.kotlinCompiler)
testRuntimeOnly(
- project(
- ":compose:compiler:compiler-hosted:integration-tests:kotlin-compiler-repackaged"
- )
+ project(":compose:compiler:compiler-hosted:integration-tests:kotlin-compiler-repackaged")
)
// tools.jar required for com.sun.jdi
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
index 9eb33b2..c991670 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
@@ -112,6 +112,7 @@
9701 to "1.5.0-alpha01",
9801 to "1.5.0-alpha02",
9901 to "1.5.0-alpha03",
+ 10001 to "1.5.0-alpha04",
)
/**
@@ -124,7 +125,7 @@
* The maven version string of this compiler. This string should be updated before/after every
* release.
*/
- const val compilerVersion: String = "1.4.6"
+ const val compilerVersion: String = "1.5.0-alpha04"
private val minimumRuntimeVersion: String
get() = runtimeVersionToMavenVersionTable[minimumRuntimeVersionInt] ?: "unknown"
}
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 2a1b29f..337b887 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -494,6 +494,11 @@
method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
}
+ public static final class GridCells.FixedSize implements androidx.compose.foundation.lazy.grid.GridCells {
+ ctor public GridCells.FixedSize(float size);
+ method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class GridItemSpan {
method public int getCurrentLineSpan();
}
@@ -631,7 +636,7 @@
property public abstract long size;
}
- public sealed interface LazyStaggeredGridItemScope {
+ @androidx.compose.runtime.Stable public sealed interface LazyStaggeredGridItemScope {
}
public sealed interface LazyStaggeredGridLayoutInfo {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 0ce3cfef..0a56a3a 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -614,7 +614,7 @@
method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
}
- @androidx.compose.foundation.ExperimentalFoundationApi public static final class GridCells.FixedSize implements androidx.compose.foundation.lazy.grid.GridCells {
+ public static final class GridCells.FixedSize implements androidx.compose.foundation.lazy.grid.GridCells {
ctor public GridCells.FixedSize(float size);
method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
}
@@ -865,7 +865,8 @@
property public abstract long size;
}
- public sealed interface LazyStaggeredGridItemScope {
+ @androidx.compose.runtime.Stable public sealed interface LazyStaggeredGridItemScope {
+ method @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
}
public sealed interface LazyStaggeredGridLayoutInfo {
@@ -981,10 +982,13 @@
}
public final class PagerKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
- method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
- method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
+ }
+
+ @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PagerScope {
}
@androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PagerSnapDistance {
@@ -1007,6 +1011,7 @@
method public final int getInitialPage();
method public final float getInitialPageOffsetFraction();
method public final androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
+ method public final float getOffsetFractionForPage(int page);
method public abstract int getPageCount();
method public final int getSettledPage();
method public final int getTargetPage();
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 2a1b29f..337b887 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -494,6 +494,11 @@
method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
}
+ public static final class GridCells.FixedSize implements androidx.compose.foundation.lazy.grid.GridCells {
+ ctor public GridCells.FixedSize(float size);
+ method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+ }
+
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class GridItemSpan {
method public int getCurrentLineSpan();
}
@@ -631,7 +636,7 @@
property public abstract long size;
}
- public sealed interface LazyStaggeredGridItemScope {
+ @androidx.compose.runtime.Stable public sealed interface LazyStaggeredGridItemScope {
}
public sealed interface LazyStaggeredGridLayoutInfo {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
index b80442a..c7d9378 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
@@ -1004,13 +1004,15 @@
StaggeredGridItemSpan.FullLine
else
StaggeredGridItemSpan.SingleLane
- }
+ },
+ key = { indices.value[it % indices.value.size] }
) {
var expanded by remember { mutableStateOf(false) }
val index = indices.value[it % indices.value.size]
val color = colors[index]
Box(
modifier = Modifier
+ .animateItemPlacement()
.height(if (!expanded) heights[index] else heights[index] * 2)
.border(2.dp, color, RoundedCornerShape(5.dp))
.clickable {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
index 4d8b7f9..9cb4e69 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
@@ -35,6 +35,7 @@
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.ExperimentalTestApi
@@ -359,19 +360,14 @@
@Test
fun draggable_callsDragStop_whenNewState() {
- var total = 0f
var dragStopped = 0f
- val state = mutableStateOf(
- DraggableState { total += it }
- )
+ val state = mutableStateOf(DraggableState { })
setDraggableContent {
- if (total < 20f) {
- Modifier.draggable(
- orientation = Orientation.Horizontal,
- onDragStopped = { dragStopped += 1 },
- state = state.value
- )
- } else Modifier
+ Modifier.draggable(
+ orientation = Orientation.Horizontal,
+ onDragStopped = { dragStopped += 1 },
+ state = state.value
+ )
}
rule.onNodeWithTag(draggableBoxTag).performTouchInput {
down(center)
@@ -387,6 +383,180 @@
}
@Test
+ fun draggable_callsDragStop_whenNewOrientation() {
+ var dragStopped = 0f
+ var orientation by mutableStateOf(Orientation.Horizontal)
+ setDraggableContent {
+ Modifier.draggable(
+ orientation = orientation,
+ onDragStopped = { dragStopped += 1 },
+ onDrag = {}
+ )
+ }
+ rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+ down(center)
+ moveBy(Offset(100f, 100f))
+ }
+ rule.runOnIdle {
+ assertThat(dragStopped).isEqualTo(0f)
+ orientation = Orientation.Vertical
+ }
+ rule.runOnIdle {
+ assertThat(dragStopped).isEqualTo(1f)
+ }
+ }
+
+ @Test
+ fun draggable_callsDragStop_whenDisabled() {
+ var dragStopped = 0f
+ var enabled by mutableStateOf(true)
+ setDraggableContent {
+ Modifier.draggable(
+ orientation = Orientation.Horizontal,
+ onDragStopped = { dragStopped += 1 },
+ enabled = enabled,
+ onDrag = {}
+ )
+ }
+ rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+ down(center)
+ moveBy(Offset(100f, 100f))
+ }
+ rule.runOnIdle {
+ assertThat(dragStopped).isEqualTo(0f)
+ enabled = false
+ }
+ rule.runOnIdle {
+ assertThat(dragStopped).isEqualTo(1f)
+ }
+ }
+
+ @Test
+ fun draggable_callsDragStop_whenNewReverseDirection() {
+ var dragStopped = 0f
+ var reverseDirection by mutableStateOf(false)
+ setDraggableContent {
+ Modifier.draggable(
+ orientation = Orientation.Horizontal,
+ onDragStopped = { dragStopped += 1 },
+ onDrag = {},
+ reverseDirection = reverseDirection
+ )
+ }
+ rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+ down(center)
+ moveBy(Offset(100f, 100f))
+ }
+ rule.runOnIdle {
+ assertThat(dragStopped).isEqualTo(0f)
+ reverseDirection = true
+ }
+ rule.runOnIdle {
+ assertThat(dragStopped).isEqualTo(1f)
+ }
+ }
+
+ @Test
+ fun draggable_updates_startDragImmediately() {
+ var total = 0f
+ var startDragImmediately by mutableStateOf(false)
+ var touchSlop: Float? = null
+ setDraggableContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ Modifier.draggable(
+ orientation = Orientation.Horizontal,
+ onDrag = { total += it },
+ startDragImmediately = startDragImmediately
+ )
+ }
+ val delta = touchSlop!! / 2f
+ rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+ down(center)
+ moveBy(Offset(delta, 0f))
+ up()
+ }
+ rule.runOnIdle {
+ assertThat(total).isEqualTo(0f)
+ startDragImmediately = true
+ }
+ rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+ down(center)
+ moveBy(Offset(delta, 0f))
+ up()
+ }
+ rule.runOnIdle {
+ assertThat(total).isEqualTo(delta)
+ }
+ }
+
+ @Test
+ fun draggable_updates_onDragStarted() {
+ var total = 0f
+ var onDragStarted1Calls = 0
+ var onDragStarted2Calls = 0
+ var onDragStarted: (Offset) -> Unit by mutableStateOf({ onDragStarted1Calls += 1 })
+ setDraggableContent {
+ Modifier.draggable(
+ orientation = Orientation.Horizontal,
+ onDrag = { total += it },
+ onDragStarted = onDragStarted
+ )
+ }
+ rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+ down(center)
+ moveBy(Offset(100f, 100f))
+ up()
+ }
+ rule.runOnIdle {
+ assertThat(onDragStarted1Calls).isEqualTo(1)
+ assertThat(onDragStarted2Calls).isEqualTo(0)
+ onDragStarted = { onDragStarted2Calls += 1 }
+ }
+ rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+ down(center)
+ moveBy(Offset(100f, 100f))
+ up()
+ }
+ rule.runOnIdle {
+ assertThat(onDragStarted1Calls).isEqualTo(1)
+ assertThat(onDragStarted2Calls).isEqualTo(1)
+ }
+ }
+
+ @Test
+ fun draggable_updates_onDragStopped() {
+ var total = 0f
+ var onDragStopped1Calls = 0
+ var onDragStopped2Calls = 0
+ var onDragStopped: (Float) -> Unit by mutableStateOf({ onDragStopped1Calls += 1 })
+ setDraggableContent {
+ Modifier.draggable(
+ orientation = Orientation.Horizontal,
+ onDrag = { total += it },
+ onDragStopped = onDragStopped
+ )
+ }
+ rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+ down(center)
+ moveBy(Offset(100f, 100f))
+ }
+ rule.runOnIdle {
+ assertThat(onDragStopped1Calls).isEqualTo(0)
+ assertThat(onDragStopped2Calls).isEqualTo(0)
+ onDragStopped = { onDragStopped2Calls += 1 }
+ }
+ rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+ up()
+ }
+ rule.runOnIdle {
+ // We changed the lambda before we ever stopped dragging, so only the new one should be
+ // called
+ assertThat(onDragStopped1Calls).isEqualTo(0)
+ assertThat(onDragStopped2Calls).isEqualTo(1)
+ }
+ }
+
+ @Test
fun draggable_resumesNormally_whenInterruptedWithHigherPriority() = runBlocking {
var total = 0f
var dragStopped = 0f
@@ -576,6 +746,112 @@
}
}
+ @Test
+ fun draggable_interactionSource_resetWhenEnabledChanged() {
+ val interactionSource = MutableInteractionSource()
+ val enabledState = mutableStateOf(true)
+
+ var scope: CoroutineScope? = null
+
+ setDraggableContent {
+ scope = rememberCoroutineScope()
+ Modifier.draggable(
+ Orientation.Horizontal,
+ enabled = enabledState.value,
+ interactionSource = interactionSource
+ ) {}
+ }
+
+ val interactions = mutableListOf<Interaction>()
+
+ scope!!.launch {
+ interactionSource.interactions.collect { interactions.add(it) }
+ }
+
+ rule.runOnIdle {
+ assertThat(interactions).isEmpty()
+ }
+
+ rule.onNodeWithTag(draggableBoxTag)
+ .performTouchInput {
+ down(Offset(visibleSize.width / 4f, visibleSize.height / 2f))
+ moveBy(Offset(visibleSize.width / 2f, 0f))
+ }
+
+ rule.runOnIdle {
+ assertThat(interactions).hasSize(1)
+ assertThat(interactions.first()).isInstanceOf(DragInteraction.Start::class.java)
+ }
+
+ rule.runOnIdle {
+ enabledState.value = false
+ }
+
+ rule.runOnIdle {
+ assertThat(interactions).hasSize(2)
+ assertThat(interactions.first()).isInstanceOf(DragInteraction.Start::class.java)
+ assertThat(interactions[1]).isInstanceOf(DragInteraction.Cancel::class.java)
+ assertThat((interactions[1] as DragInteraction.Cancel).start)
+ .isEqualTo(interactions[0])
+ }
+ }
+
+ @Test
+ fun draggable_interactionSource_resetWhenInteractionSourceChanged() {
+ val interactionSource1 = MutableInteractionSource()
+ val interactionSource2 = MutableInteractionSource()
+ val interactionSourceState = mutableStateOf(interactionSource1)
+
+ var scope: CoroutineScope? = null
+
+ setDraggableContent {
+ scope = rememberCoroutineScope()
+ Modifier.draggable(
+ Orientation.Horizontal,
+ interactionSource = interactionSourceState.value
+ ) {}
+ }
+
+ val interactions1 = mutableListOf<Interaction>()
+ val interactions2 = mutableListOf<Interaction>()
+
+ scope!!.launch {
+ interactionSource1.interactions.collect { interactions1.add(it) }
+ }
+
+ rule.runOnIdle {
+ assertThat(interactions1).isEmpty()
+ assertThat(interactions2).isEmpty()
+ }
+
+ rule.onNodeWithTag(draggableBoxTag)
+ .performTouchInput {
+ down(Offset(visibleSize.width / 4f, visibleSize.height / 2f))
+ moveBy(Offset(visibleSize.width / 2f, 0f))
+ }
+
+ rule.runOnIdle {
+ assertThat(interactions1).hasSize(1)
+ assertThat(interactions1.first()).isInstanceOf(DragInteraction.Start::class.java)
+ assertThat(interactions2).isEmpty()
+ }
+
+ rule.runOnIdle {
+ interactionSourceState.value = interactionSource2
+ }
+
+ rule.runOnIdle {
+ assertThat(interactions1).hasSize(2)
+ assertThat(interactions1.first()).isInstanceOf(DragInteraction.Start::class.java)
+ assertThat(interactions1[1]).isInstanceOf(DragInteraction.Cancel::class.java)
+ assertThat((interactions1[1] as DragInteraction.Cancel).start)
+ .isEqualTo(interactions1[0])
+ // Currently we don't emit drag start for an in progress drag, but this might change
+ // in the future.
+ assertThat(interactions2).isEmpty()
+ }
+ }
+
@OptIn(ExperimentalTestApi::class)
@Test
fun draggable_cancelMidDown_shouldContinueWithNextDown() {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
index 61ce9f7..030e1d7 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
@@ -760,7 +760,7 @@
rule.onNodeWithTag("4").assertDoesNotExist()
rule.onNodeWithTag("5").assertDoesNotExist()
val item2Size = itemSize3 /* the real size of the item 2 */
- // item 2 moves from and item 4 moves to `0 - item size`, right before the start edge
+ // item 2 moves from `0 - item size`, right before the start edge
val startItem2Offset = -item2Size
val item2Offset =
startItem2Offset + (itemSize2 - startItem2Offset) * fraction
@@ -833,7 +833,7 @@
}
onAnimationFrame { fraction ->
- // item 1 moves from and item 8 moves to `gridSize`, right after the end edge
+ // item 8 moves from and item 2 moves to `gridSize`, right after the end edge
val startItem8Offset = gridSize
val endItem2Offset = gridSize
val line4Size = itemSize3
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsTest.kt
new file mode 100644
index 0000000..f0315ad
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsTest.kt
@@ -0,0 +1,639 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.foundation.lazy.grid
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.BeyondBoundsLayout
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Above
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.After
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Before
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Below
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Left
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Right
+import androidx.compose.ui.layout.ModifierLocalBeyondBoundsLayout
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.modifier.modifierLocalConsumer
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.LayoutDirection.Ltr
+import androidx.compose.ui.unit.LayoutDirection.Rtl
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalComposeUiApi::class)
+@MediumTest
+@RunWith(Parameterized::class)
+class LazyGridBeyondBoundsTest(param: Param) {
+
+ @get:Rule
+ val rule = createComposeRule()
+
+ // We need to wrap the inline class parameter in another class because Java can't instantiate
+ // the inline class.
+ class Param(
+ val beyondBoundsLayoutDirection: BeyondBoundsLayout.LayoutDirection,
+ val reverseLayout: Boolean,
+ val layoutDirection: LayoutDirection,
+ ) {
+ override fun toString() = "beyondBoundsLayoutDirection=$beyondBoundsLayoutDirection " +
+ "reverseLayout=$reverseLayout " +
+ "layoutDirection=$layoutDirection"
+ }
+
+ private val beyondBoundsLayoutDirection = param.beyondBoundsLayoutDirection
+ private val reverseLayout = param.reverseLayout
+ private val layoutDirection = param.layoutDirection
+ private val placedItems = mutableSetOf<Int>()
+ private var beyondBoundsLayout: BeyondBoundsLayout? = null
+ private lateinit var lazyGridState: LazyGridState
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun initParameters() = buildList {
+ for (beyondBoundsLayoutDirection in listOf(Left, Right, Above, Below, Before, After)) {
+ for (reverseLayout in listOf(false, true)) {
+ for (layoutDirection in listOf(Ltr, Rtl)) {
+ add(Param(beyondBoundsLayoutDirection, reverseLayout, layoutDirection))
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun onlyOneVisibleItemIsPlaced() {
+ // Arrange.
+ rule.setLazyContent(size = 10.toDp(), firstVisibleItem = 0) {
+ items(100) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(0)
+ assertThat(visibleItems).containsExactly(0)
+ }
+ }
+
+ @Test
+ fun onlyTwoVisibleItemsArePlaced() {
+ // Arrange.
+ rule.setLazyContent(size = 20.toDp(), firstVisibleItem = 0) {
+ items(100) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(0, 1)
+ assertThat(visibleItems).containsExactly(0, 1)
+ }
+ }
+
+ @Test
+ fun onlyThreeVisibleItemsArePlaced() {
+ // Arrange.
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 0) {
+ items(100) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(0, 1, 2)
+ assertThat(visibleItems).containsExactly(0, 1, 2)
+ }
+ }
+
+ @Test
+ fun emptyLazyList_doesNotCrash() {
+ // Arrange.
+ var addItems by mutableStateOf(true)
+ lateinit var beyondBoundsLayoutRef: BeyondBoundsLayout
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 0) {
+ if (addItems) {
+ item {
+ Box(
+ Modifier.modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ }
+ }
+ rule.runOnIdle {
+ beyondBoundsLayoutRef = beyondBoundsLayout!!
+ addItems = false
+ }
+
+ // Act.
+ val hasMoreContent = rule.runOnIdle {
+ beyondBoundsLayoutRef.layout(beyondBoundsLayoutDirection) {
+ hasMoreContent
+ }
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(hasMoreContent).isFalse()
+ }
+ }
+
+ @Test
+ fun oneExtraItemBeyondVisibleBounds() {
+ // Arrange.
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += 5 }
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index + 6 }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ // Assert that the beyond bounds items are present.
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ } else {
+ assertThat(placedItems).containsExactly(5, 6, 7, 8)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ placedItems.clear()
+ // Just return true so that we stop as soon as we run this once.
+ // This should result in one extra item being added.
+ true
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ }
+
+ @Test
+ fun oneExtraItemBeyondVisibleBounds_multipleCells() {
+ val itemSize = 50
+ val itemSizeDp = itemSize.toDp()
+ // Arrange.
+ rule.setLazyContent(cells = 2, size = itemSizeDp * 3, firstVisibleItem = 10) {
+ // item | item | x5
+ // item | local | x1
+ // item | item | x5
+ items(11) { index ->
+ Box(
+ Modifier
+ .size(itemSizeDp)
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(Modifier
+ .size(itemSizeDp)
+ .onPlaced { placedItems += 11 }
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ items(10) { index ->
+ Box(
+ Modifier
+ .size(itemSizeDp)
+ .onPlaced { placedItems += index + 12 }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ // Assert that the beyond bounds items are present.
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(9, 10, 11, 12, 13, 14, 15)
+ assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+ } else {
+ assertThat(placedItems).containsExactly(10, 11, 12, 13, 14, 15, 16)
+ assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+ }
+ placedItems.clear()
+ // Just return true so that we stop as soon as we run this once.
+ // This should result in one extra item being added.
+ true
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(10, 11, 12, 13, 14, 15)
+ assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+ }
+ }
+
+ @Test
+ fun twoExtraItemsBeyondVisibleBounds() {
+ // Arrange.
+ var extraItemCount = 2
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += 5 }
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index + 6 }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ if (--extraItemCount > 0) {
+ placedItems.clear()
+ // Return null to continue the search.
+ null
+ } else {
+ // Assert that the beyond bounds items are present.
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(3, 4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ } else {
+ assertThat(placedItems).containsExactly(5, 6, 7, 8, 9)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ placedItems.clear()
+ // Return true to stop the search.
+ true
+ }
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ }
+
+ @Test
+ fun allBeyondBoundsItemsInSpecifiedDirection() {
+ // Arrange.
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(
+ Modifier
+ .size(10.toDp())
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ .onPlaced { placedItems += 5 }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced {
+ placedItems += index + 6
+ }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ if (hasMoreContent) {
+ placedItems.clear()
+ // Just return null so that we keep adding more items till we reach the end.
+ null
+ } else {
+ // Assert that the beyond bounds items are present.
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(0, 1, 2, 3, 4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ } else {
+ assertThat(placedItems).containsExactly(5, 6, 7, 8, 9, 10)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ placedItems.clear()
+ // Return true to end the search.
+ true
+ }
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ }
+ }
+
+ @Test
+ fun beyondBoundsLayoutRequest_inDirectionPerpendicularToLazyListOrientation() {
+ // Arrange.
+ var beyondBoundsLayoutCount = 0
+ rule.setLazyContentInPerpendicularDirection(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += 5 }
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index + 6 }
+ )
+ }
+ }
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ placedItems.clear()
+ }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ beyondBoundsLayoutCount++
+ when (beyondBoundsLayoutDirection) {
+ Left, Right, Above, Below -> {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ Before, After -> {
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ } else {
+ assertThat(placedItems).containsExactly(5, 6, 7, 8)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ }
+ }
+ placedItems.clear()
+ // Just return true so that we stop as soon as we run this once.
+ // This should result in one extra item being added.
+ true
+ }
+ }
+
+ rule.runOnIdle {
+ when (beyondBoundsLayoutDirection) {
+ Left, Right, Above, Below -> {
+ assertThat(beyondBoundsLayoutCount).isEqualTo(0)
+ }
+ Before, After -> {
+ assertThat(beyondBoundsLayoutCount).isEqualTo(1)
+
+ // Assert that the beyond bounds items are removed.
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ else -> error("Unsupported BeyondBoundsLayoutDirection")
+ }
+ }
+ }
+
+ @Test
+ fun returningNullDoesNotCauseInfiniteLoop() {
+ // Arrange.
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced {
+ placedItems += index
+ }
+ )
+ }
+ item {
+ Box(
+ Modifier
+ .size(10.toDp())
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ .onPlaced { placedItems += 5 }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced {
+ placedItems += index + 6
+ }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ var count = 0
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ // Assert that we don't keep iterating when there is no ending condition.
+ assertThat(count++).isLessThan(lazyGridState.layoutInfo.totalItemsCount)
+ placedItems.clear()
+ // Always return null to continue the search.
+ null
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ }
+
+ private fun ComposeContentTestRule.setLazyContent(
+ size: Dp,
+ firstVisibleItem: Int,
+ cells: Int = 1,
+ content: LazyGridScope.() -> Unit
+ ) {
+ setContent {
+ CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+ lazyGridState = rememberLazyGridState(firstVisibleItem)
+ when (beyondBoundsLayoutDirection) {
+ Left, Right, Before, After ->
+ LazyHorizontalGrid(
+ rows = GridCells.Fixed(cells),
+ modifier = Modifier.size(size),
+ state = lazyGridState,
+ reverseLayout = reverseLayout,
+ content = content
+ )
+ Above, Below ->
+ LazyVerticalGrid(
+ columns = GridCells.Fixed(cells),
+ modifier = Modifier.size(size),
+ state = lazyGridState,
+ reverseLayout = reverseLayout,
+ content = content
+ )
+ else -> unsupportedDirection()
+ }
+ }
+ }
+ }
+
+ private fun ComposeContentTestRule.setLazyContentInPerpendicularDirection(
+ size: Dp,
+ firstVisibleItem: Int,
+ content: LazyGridScope.() -> Unit
+ ) {
+ setContent {
+ CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+ lazyGridState = rememberLazyGridState(firstVisibleItem)
+ when (beyondBoundsLayoutDirection) {
+ Left, Right, Before, After ->
+ LazyVerticalGrid(
+ columns = GridCells.Fixed(1),
+ modifier = Modifier.size(size),
+ state = lazyGridState,
+ reverseLayout = reverseLayout,
+ content = content
+ )
+ Above, Below ->
+ LazyHorizontalGrid(
+ rows = GridCells.Fixed(1),
+ modifier = Modifier.size(size),
+ state = lazyGridState,
+ reverseLayout = reverseLayout,
+ content = content
+ )
+ else -> unsupportedDirection()
+ }
+ }
+ }
+ }
+
+ private fun Int.toDp(): Dp = with(rule.density) { toDp() }
+
+ private val visibleItems: List<Int>
+ get() = lazyGridState.layoutInfo.visibleItemsInfo.map { it.index }
+
+ private fun expectedExtraItemsBeforeVisibleBounds() = when (beyondBoundsLayoutDirection) {
+ Right -> if (layoutDirection == Ltr) reverseLayout else !reverseLayout
+ Left -> if (layoutDirection == Ltr) !reverseLayout else reverseLayout
+ Above -> !reverseLayout
+ Below -> reverseLayout
+ After -> false
+ Before -> true
+ else -> error("Unsupported BeyondBoundsDirection")
+ }
+
+ private fun unsupportedDirection(): Nothing = error(
+ "Lazy list does not support beyond bounds layout for the specified direction"
+ )
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt
index 3bf22a4..c868e03 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt
@@ -19,6 +19,7 @@
import androidx.compose.animation.core.FloatSpringSpec
import androidx.compose.foundation.AutoTestFrameClock
import androidx.compose.foundation.gestures.animateScrollBy
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
@@ -65,22 +66,28 @@
itemSizeDp = itemSizePx.toDp()
containerSizeDp = itemSizeDp * 3
}
+ }
+
+ private fun testScroll(spacingPx: Int = 0, assertBlock: suspend () -> Unit) {
rule.setContent {
state = rememberLazyGridState()
scope = rememberCoroutineScope()
- TestContent()
+ TestContent(with(rule.density) { spacingPx.toDp() })
+ }
+ runBlocking {
+ assertBlock()
}
}
@Test
- fun setupWorks() {
+ fun setupWorks() = testScroll {
assertThat(state.firstVisibleItemIndex).isEqualTo(0)
assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
assertThat(state.firstVisibleItemIndex).isEqualTo(0)
}
@Test
- fun scrollToItem() = runBlocking {
+ fun scrollToItem() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(2)
}
@@ -95,7 +102,7 @@
}
@Test
- fun scrollToItemWithOffset() = runBlocking {
+ fun scrollToItemWithOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(6, 10)
}
@@ -104,7 +111,7 @@
}
@Test
- fun scrollToItemWithNegativeOffset() = runBlocking {
+ fun scrollToItemWithNegativeOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(6, -10)
}
@@ -114,7 +121,7 @@
}
@Test
- fun scrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+ fun scrollToItemWithPositiveOffsetLargerThanAvailableSize() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(itemsCount - 6, 10)
}
@@ -123,7 +130,7 @@
}
@Test
- fun scrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+ fun scrollToItemWithNegativeOffsetLargerThanAvailableSize() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(1, -(itemSizePx + 10))
}
@@ -132,7 +139,7 @@
}
@Test
- fun scrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+ fun scrollToItemWithIndexLargerThanItemsCount() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(itemsCount + 4)
}
@@ -140,7 +147,7 @@
}
@Test
- fun animateScrollBy() = runBlocking {
+ fun animateScrollBy() = testScroll {
val scrollDistance = 320
val expectedLine = scrollDistance / itemSizePx // resolves to 3
@@ -155,7 +162,7 @@
}
@Test
- fun animateScrollToItem() = runBlocking {
+ fun animateScrollToItem() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(10, 10)
}
@@ -164,7 +171,7 @@
}
@Test
- fun animateScrollToItemWithOffset() = runBlocking {
+ fun animateScrollToItemWithOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(6, 10)
}
@@ -173,7 +180,7 @@
}
@Test
- fun animateScrollToItemWithNegativeOffset() = runBlocking {
+ fun animateScrollToItemWithNegativeOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(6, -10)
}
@@ -183,7 +190,7 @@
}
@Test
- fun animateScrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+ fun animateScrollToItemWithPositiveOffsetLargerThanAvailableSize() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(itemsCount - 6, 10)
}
@@ -192,7 +199,7 @@
}
@Test
- fun animateScrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+ fun animateScrollToItemWithNegativeOffsetLargerThanAvailableSize() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(2, -(itemSizePx + 10))
}
@@ -201,7 +208,7 @@
}
@Test
- fun animateScrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+ fun animateScrollToItemWithIndexLargerThanItemsCount() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(itemsCount + 2)
}
@@ -209,42 +216,42 @@
}
@Test
- fun animatePerFrameForwardToVisibleItem() {
+ fun animatePerFrameForwardToVisibleItem() = testScroll {
assertSpringAnimation(toIndex = 4)
}
@Test
- fun animatePerFrameForwardToVisibleItemWithOffset() {
+ fun animatePerFrameForwardToVisibleItemWithOffset() = testScroll {
assertSpringAnimation(toIndex = 4, toOffset = 35)
}
@Test
- fun animatePerFrameForwardToNotVisibleItem() {
+ fun animatePerFrameForwardToNotVisibleItem() = testScroll {
assertSpringAnimation(toIndex = 16)
}
@Test
- fun animatePerFrameForwardToNotVisibleItemWithOffset() {
+ fun animatePerFrameForwardToNotVisibleItemWithOffset() = testScroll {
assertSpringAnimation(toIndex = 20, toOffset = 35)
}
@Test
- fun animatePerFrameBackward() {
+ fun animatePerFrameBackward() = testScroll {
assertSpringAnimation(toIndex = 2, fromIndex = 12)
}
@Test
- fun animatePerFrameBackwardWithOffset() {
+ fun animatePerFrameBackwardWithOffset() = testScroll {
assertSpringAnimation(toIndex = 2, fromIndex = 10, fromOffset = 58)
}
@Test
- fun animatePerFrameBackwardWithInitialOffset() {
+ fun animatePerFrameBackwardWithInitialOffset() = testScroll {
assertSpringAnimation(toIndex = 0, toOffset = 40, fromIndex = 8)
}
@Test
- fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = runBlocking {
+ fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(10, -itemSizePx * 3)
}
@@ -253,7 +260,7 @@
}
@Test
- fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = runBlocking {
+ fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(10)
state.animateScrollToItem(0, itemSizePx * 3)
@@ -263,14 +270,14 @@
}
@Test
- fun canScrollForward() = runBlocking {
+ fun canScrollForward() = testScroll {
assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
assertThat(state.canScrollForward).isTrue()
assertThat(state.canScrollBackward).isFalse()
}
@Test
- fun canScrollBackward() = runBlocking {
+ fun canScrollBackward() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(itemsCount)
}
@@ -280,7 +287,7 @@
}
@Test
- fun canScrollForwardAndBackward() = runBlocking {
+ fun canScrollForwardAndBackward() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(10)
}
@@ -289,11 +296,32 @@
assertThat(state.canScrollBackward).isTrue()
}
+ @Test
+ fun animatePerFrameForwardWithSpacing() = testScroll(spacingPx = 10) {
+ assertSpringAnimation(toIndex = 16, spacingPx = 10)
+ }
+
+ @Test
+ fun animatePerFrameForwardWithNegativeSpacing() = testScroll(spacingPx = -10) {
+ assertSpringAnimation(toIndex = 16, spacingPx = -10)
+ }
+
+ @Test
+ fun animatePerFrameBackwardWithSpacing() = testScroll(spacingPx = 10) {
+ assertSpringAnimation(toIndex = 2, fromIndex = 12, spacingPx = 10)
+ }
+
+ @Test
+ fun animatePerFrameBackwardWithNegativeSpacing() = testScroll(spacingPx = -10) {
+ assertSpringAnimation(toIndex = 2, fromIndex = 12, spacingPx = -10)
+ }
+
private fun assertSpringAnimation(
toIndex: Int,
toOffset: Int = 0,
fromIndex: Int = 0,
- fromOffset: Int = 0
+ fromOffset: Int = 0,
+ spacingPx: Int = 0
) {
if (fromIndex != 0 || fromOffset != 0) {
rule.runOnIdle {
@@ -317,8 +345,9 @@
Thread.sleep(5)
}
- val startOffset = (fromIndex / 2 * itemSizePx + fromOffset).toFloat()
- val endOffset = (toIndex / 2 * itemSizePx + toOffset).toFloat()
+ val itemWSpacing = itemSizePx + spacingPx
+ val startOffset = (fromIndex / 2 * itemWSpacing + fromOffset).toFloat()
+ val endOffset = (toIndex / 2 * itemWSpacing + toOffset).toFloat()
val spec = FloatSpringSpec()
val duration =
@@ -330,7 +359,7 @@
val expectedValue =
spec.getValueFromNanos(nanosTime, startOffset, endOffset, 0f)
val actualValue =
- (state.firstVisibleItemIndex / 2 * itemSizePx + state.firstVisibleItemScrollOffset)
+ state.firstVisibleItemIndex / 2 * itemWSpacing + state.firstVisibleItemScrollOffset
assertWithMessage(
"On animation frame at $i index=${state.firstVisibleItemIndex} " +
"offset=${state.firstVisibleItemScrollOffset} expectedValue=$expectedValue"
@@ -346,9 +375,14 @@
}
@Composable
- private fun TestContent() {
+ private fun TestContent(spacingDp: Dp) {
if (vertical) {
- LazyVerticalGrid(GridCells.Fixed(2), Modifier.height(containerSizeDp), state) {
+ LazyVerticalGrid(
+ GridCells.Fixed(2),
+ Modifier.height(containerSizeDp),
+ state,
+ verticalArrangement = Arrangement.spacedBy(spacingDp)
+ ) {
items(itemsCount) {
ItemContent()
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
index 8d2a237..bca1dc3 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
@@ -26,6 +26,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
@@ -73,21 +74,27 @@
itemSizeDp = itemSizePx.toDp()
containerSizeDp = itemSizeDp * 3
}
+ }
+
+ private fun testScroll(spacingPx: Int = 0, assertBlock: suspend () -> Unit) {
rule.setContent {
state = rememberLazyListState()
scope = rememberCoroutineScope()
- TestContent()
+ TestContent(with(rule.density) { spacingPx.toDp() })
+ }
+ runBlocking {
+ assertBlock()
}
}
@Test
- fun setupWorks() {
+ fun setupWorks() = testScroll {
assertThat(state.firstVisibleItemIndex).isEqualTo(0)
assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
}
@Test
- fun scrollToItem() = runBlocking {
+ fun scrollToItem() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(3)
}
@@ -96,7 +103,7 @@
}
@Test
- fun scrollToItemWithOffset() = runBlocking {
+ fun scrollToItemWithOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(3, 10)
}
@@ -105,7 +112,7 @@
}
@Test
- fun scrollToItemWithNegativeOffset() = runBlocking {
+ fun scrollToItemWithNegativeOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(3, -10)
}
@@ -115,7 +122,7 @@
}
@Test
- fun scrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+ fun scrollToItemWithPositiveOffsetLargerThanAvailableSize() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(itemsCount - 3, 10)
}
@@ -124,7 +131,7 @@
}
@Test
- fun scrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+ fun scrollToItemWithNegativeOffsetLargerThanAvailableSize() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(1, -(itemSizePx + 10))
}
@@ -133,7 +140,7 @@
}
@Test
- fun scrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+ fun scrollToItemWithIndexLargerThanItemsCount() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(itemsCount + 2)
}
@@ -141,7 +148,7 @@
}
@Test
- fun animateScrollBy() = runBlocking {
+ fun animateScrollBy() = testScroll {
val scrollDistance = 320
val expectedIndex = scrollDistance / itemSizePx // resolves to 3
@@ -155,7 +162,7 @@
}
@Test
- fun animateScrollToItem() = runBlocking {
+ fun animateScrollToItem() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(5, 10)
}
@@ -164,7 +171,7 @@
}
@Test
- fun animateScrollToItemWithOffset() = runBlocking {
+ fun animateScrollToItemWithOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(3, 10)
}
@@ -173,7 +180,7 @@
}
@Test
- fun animateScrollToItemWithNegativeOffset() = runBlocking {
+ fun animateScrollToItemWithNegativeOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(3, -10)
}
@@ -183,7 +190,7 @@
}
@Test
- fun animateScrollToItemWithPositiveOffsetLargerThanAvailableSize() = runBlocking {
+ fun animateScrollToItemWithPositiveOffsetLargerThanAvailableSize() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(itemsCount - 3, 10)
}
@@ -192,7 +199,7 @@
}
@Test
- fun animateScrollToItemWithNegativeOffsetLargerThanAvailableSize() = runBlocking {
+ fun animateScrollToItemWithNegativeOffsetLargerThanAvailableSize() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(1, -(itemSizePx + 10))
}
@@ -201,7 +208,7 @@
}
@Test
- fun animateScrollToItemWithIndexLargerThanItemsCount() = runBlocking {
+ fun animateScrollToItemWithIndexLargerThanItemsCount() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(itemsCount + 2)
}
@@ -209,42 +216,42 @@
}
@Test
- fun animatePerFrameForwardToVisibleItem() {
+ fun animatePerFrameForwardToVisibleItem() = testScroll {
assertSpringAnimation(toIndex = 2)
}
@Test
- fun animatePerFrameForwardToVisibleItemWithOffset() {
+ fun animatePerFrameForwardToVisibleItemWithOffset() = testScroll {
assertSpringAnimation(toIndex = 2, toOffset = 35)
}
@Test
- fun animatePerFrameForwardToNotVisibleItem() {
+ fun animatePerFrameForwardToNotVisibleItem() = testScroll {
assertSpringAnimation(toIndex = 8)
}
@Test
- fun animatePerFrameForwardToNotVisibleItemWithOffset() {
+ fun animatePerFrameForwardToNotVisibleItemWithOffset() = testScroll {
assertSpringAnimation(toIndex = 10, toOffset = 35)
}
@Test
- fun animatePerFrameBackward() {
+ fun animatePerFrameBackward() = testScroll {
assertSpringAnimation(toIndex = 1, fromIndex = 6)
}
@Test
- fun animatePerFrameBackwardWithOffset() {
+ fun animatePerFrameBackwardWithOffset() = testScroll {
assertSpringAnimation(toIndex = 1, fromIndex = 5, fromOffset = 58)
}
@Test
- fun animatePerFrameBackwardWithInitialOffset() {
+ fun animatePerFrameBackwardWithInitialOffset() = testScroll {
assertSpringAnimation(toIndex = 0, toOffset = 20, fromIndex = 8)
}
@Test
- fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = runBlocking {
+ fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(10, -itemSizePx * 3)
}
@@ -253,7 +260,7 @@
}
@Test
- fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = runBlocking {
+ fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(10)
state.animateScrollToItem(0, itemSizePx * 3)
@@ -263,14 +270,14 @@
}
@Test
- fun canScrollForward() = runBlocking {
+ fun canScrollForward() = testScroll {
assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
assertThat(state.canScrollForward).isTrue()
assertThat(state.canScrollBackward).isFalse()
}
@Test
- fun canScrollBackward() = runBlocking {
+ fun canScrollBackward() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(itemsCount)
}
@@ -280,7 +287,7 @@
}
@Test
- fun canScrollForwardAndBackward() = runBlocking {
+ fun canScrollForwardAndBackward() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(1)
}
@@ -289,11 +296,32 @@
assertThat(state.canScrollBackward).isTrue()
}
+ @Test
+ fun animatePerFrameWithSpacing() = testScroll(spacingPx = 10) {
+ assertSpringAnimation(toIndex = 8, spacingPx = 10)
+ }
+
+ @Test
+ fun animatePerFrameWithNegativeSpacing() = testScroll(spacingPx = -10) {
+ assertSpringAnimation(toIndex = 8, spacingPx = -10)
+ }
+
+ @Test
+ fun animatePerFrameBackwardWithSpacing() = testScroll(spacingPx = 10) {
+ assertSpringAnimation(toIndex = 1, fromIndex = 6, spacingPx = 10)
+ }
+
+ @Test
+ fun animatePerFrameBackwardWithNegativeSpacing() = testScroll(spacingPx = -10) {
+ assertSpringAnimation(toIndex = 1, fromIndex = 6, spacingPx = -10)
+ }
+
private fun assertSpringAnimation(
toIndex: Int,
toOffset: Int = 0,
fromIndex: Int = 0,
- fromOffset: Int = 0
+ fromOffset: Int = 0,
+ spacingPx: Int = 0
) {
if (fromIndex != 0 || fromOffset != 0) {
rule.runOnIdle {
@@ -317,8 +345,9 @@
Thread.sleep(5)
}
- val startOffset = (fromIndex * itemSizePx + fromOffset).toFloat()
- val endOffset = (toIndex * itemSizePx + toOffset).toFloat()
+ val itemSizeWSpacing = itemSizePx + spacingPx
+ val startOffset = (fromIndex * itemSizeWSpacing + fromOffset).toFloat()
+ val endOffset = (toIndex * itemSizeWSpacing + toOffset).toFloat()
val spec = FloatSpringSpec()
val duration =
@@ -329,8 +358,9 @@
val nanosTime = TimeUnit.MILLISECONDS.toNanos(i)
val expectedValue =
spec.getValueFromNanos(nanosTime, startOffset, endOffset, 0f)
- val actualValue =
- (state.firstVisibleItemIndex * itemSizePx + state.firstVisibleItemScrollOffset)
+ val actualValue = (
+ state.firstVisibleItemIndex * itemSizeWSpacing + state.firstVisibleItemScrollOffset
+ )
assertWithMessage(
"On animation frame at $i index=${state.firstVisibleItemIndex} " +
"offset=${state.firstVisibleItemScrollOffset} expectedValue=$expectedValue"
@@ -346,15 +376,22 @@
}
@Composable
- private fun TestContent() {
+ private fun TestContent(spacingDp: Dp) {
if (vertical) {
- LazyColumn(Modifier.height(containerSizeDp), state) {
+ LazyColumn(
+ Modifier.height(containerSizeDp),
+ state,
+ verticalArrangement = Arrangement.spacedBy(spacingDp)
+ ) {
items(itemsCount) {
ItemContent()
}
}
} else {
- LazyRow(Modifier.width(containerSizeDp), state) {
+ LazyRow(
+ Modifier.width(containerSizeDp), state,
+ horizontalArrangement = Arrangement.spacedBy(spacingDp)
+ ) {
items(itemsCount) {
ItemContent()
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateItemPlacementTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateItemPlacementTest.kt
new file mode 100644
index 0000000..984c7d4
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateItemPlacementTest.kt
@@ -0,0 +1,2181 @@
+/*
+ * 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.lazy.staggeredgrid
+
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.layout.requiredHeightIn
+import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.layout.requiredWidthIn
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.round
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+class LazyStaggeredGridAnimateItemPlacementTest(private val config: Config) {
+
+ private val isVertical: Boolean get() = config.isVertical
+ private val reverseLayout: Boolean get() = config.reverseLayout
+
+ @get:Rule
+ val rule = createComposeRule()
+
+ // the numbers should be divisible by 8 to avoid the rounding issues as we run 4 or 8 frames
+ // of the animation.
+ private val itemSize: Float = 40f
+ private var itemSizeDp: Dp = Dp.Infinity
+ private val itemSize2: Float = 24f
+ private var itemSize2Dp: Dp = Dp.Infinity
+ private val itemSize3: Float = 16f
+ private var itemSize3Dp: Dp = Dp.Infinity
+ private val containerSize: Float = itemSize * 5
+ private var containerSizeDp: Dp = Dp.Infinity
+ private val spacing: Float = 8f
+ private var spacingDp: Dp = Dp.Infinity
+ private val itemSizePlusSpacing = itemSize + spacing
+ private var itemSizePlusSpacingDp = Dp.Infinity
+ private lateinit var state: LazyStaggeredGridState
+
+ @Before
+ fun before() {
+ rule.mainClock.autoAdvance = false
+ with(rule.density) {
+ itemSizeDp = itemSize.toDp()
+ itemSize2Dp = itemSize2.toDp()
+ itemSize3Dp = itemSize3.toDp()
+ containerSizeDp = containerSize.toDp()
+ spacingDp = spacing.toDp()
+ itemSizePlusSpacingDp = itemSizePlusSpacing.toDp()
+ }
+ }
+
+ @Test
+ fun reorderTwoItems() {
+ var list by mutableStateOf(listOf(0, 1))
+ rule.setContent {
+ LazyStaggeredGrid(1) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(1, 0)
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, 0f + itemSize * fraction),
+ 1 to AxisOffset(0f, itemSize - itemSize * fraction),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun reorderTwoByTwoItems() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3))
+ rule.setContent {
+ LazyStaggeredGrid(2) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(0f, itemSize),
+ 3 to AxisOffset(itemSize, itemSize)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(3, 2, 1, 0)
+ }
+
+ onAnimationFrame { fraction ->
+ val increasing = 0 + itemSize * fraction
+ val decreasing = itemSize - itemSize * fraction
+ assertPositions(
+ 0 to AxisOffset(increasing, increasing),
+ 1 to AxisOffset(decreasing, increasing),
+ 2 to AxisOffset(increasing, decreasing),
+ 3 to AxisOffset(decreasing, decreasing),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun reorderTwoItems_layoutInfoHasFinalPositions() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3))
+ rule.setContent {
+ LazyStaggeredGrid(2) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertLayoutInfoPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(0f, itemSize),
+ 3 to AxisOffset(itemSize, itemSize)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(3, 2, 1, 0)
+ }
+
+ onAnimationFrame {
+ // fraction doesn't affect the offsets in layout info
+ assertLayoutInfoPositions(
+ 3 to AxisOffset(0f, 0f),
+ 2 to AxisOffset(itemSize, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ 0 to AxisOffset(itemSize, itemSize)
+ )
+ }
+ }
+
+ @Test
+ fun reorderFirstAndLastItems() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ rule.setContent {
+ LazyStaggeredGrid(1) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ 2 to AxisOffset(0f, itemSize * 2),
+ 3 to AxisOffset(0f, itemSize * 3),
+ 4 to AxisOffset(0f, itemSize * 4)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(4, 1, 2, 3, 0)
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, 0f + itemSize * 4 * fraction),
+ 1 to AxisOffset(0f, itemSize),
+ 2 to AxisOffset(0f, itemSize * 2),
+ 3 to AxisOffset(0f, itemSize * 3),
+ 4 to AxisOffset(0f, itemSize * 4 - itemSize * 4 * fraction),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveFirstItemToEndCausingAllItemsToAnimate() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+ rule.setContent {
+ LazyStaggeredGrid(2) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(0f, itemSize),
+ 3 to AxisOffset(itemSize, itemSize),
+ 4 to AxisOffset(0f, itemSize * 2),
+ 5 to AxisOffset(itemSize, itemSize * 2)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(1, 2, 3, 4, 5, 0)
+ }
+
+ onAnimationFrame { fraction ->
+ val increasingX = 0 + itemSize * fraction
+ val decreasingX = itemSize - itemSize * fraction
+ assertPositions(
+ 0 to AxisOffset(increasingX, 0f + itemSize * 2 * fraction),
+ 1 to AxisOffset(decreasingX, 0f),
+ 2 to AxisOffset(increasingX, itemSize - itemSize * fraction),
+ 3 to AxisOffset(decreasingX, itemSize),
+ 4 to AxisOffset(increasingX, itemSize * 2 - itemSize * fraction),
+ 5 to AxisOffset(decreasingX, itemSize * 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun itemSizeChangeAnimatesNextItems() {
+ var size by mutableStateOf(itemSizeDp)
+ rule.setContent {
+ LazyStaggeredGrid(1, minSize = itemSizeDp * 5, maxSize = itemSizeDp * 5) {
+ items(listOf(0, 1, 2, 3), key = { it }) {
+ Item(it, size = if (it == 1) size else itemSizeDp)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ size = itemSizeDp * 2
+ }
+ rule.mainClock.advanceTimeByFrame()
+
+ rule.onNodeWithTag("1")
+ .assertMainAxisSizeIsEqualTo(size)
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ 2 to AxisOffset(0f, itemSize * 2 + itemSize * fraction),
+ 3 to AxisOffset(0f, itemSize * 3 + itemSize * fraction),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun onlyItemsWithModifierAnimates() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ rule.setContent {
+ LazyStaggeredGrid(1) {
+ items(list, key = { it }) {
+ Item(it, animSpec = if (it == 1 || it == 3) AnimSpec else null)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(1, 2, 3, 4, 0)
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, itemSize * 4),
+ 1 to AxisOffset(0f, itemSize - itemSize * fraction),
+ 2 to AxisOffset(0f, itemSize),
+ 3 to AxisOffset(0f, itemSize * 3 - itemSize * fraction),
+ 4 to AxisOffset(0f, itemSize * 3),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun animationsWithDifferentDurations() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ rule.setContent {
+ LazyStaggeredGrid(1) {
+ items(list, key = { it }) {
+ val duration = if (it == 1 || it == 3) Duration * 2 else Duration
+ Item(it, animSpec = tween(duration.toInt(), easing = LinearEasing))
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(1, 2, 3, 4, 0)
+ }
+
+ onAnimationFrame(duration = Duration * 2) { fraction ->
+ val shorterAnimFraction = (fraction * 2).coerceAtMost(1f)
+ assertPositions(
+ 0 to AxisOffset(0f, 0 + itemSize * 4 * shorterAnimFraction),
+ 1 to AxisOffset(0f, itemSize - itemSize * fraction),
+ 2 to AxisOffset(0f, itemSize * 2 - itemSize * shorterAnimFraction),
+ 3 to AxisOffset(0f, itemSize * 3 - itemSize * fraction),
+ 4 to AxisOffset(0f, itemSize * 4 - itemSize * shorterAnimFraction),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun multipleChildrenPerItem() {
+ var list by mutableStateOf(listOf(0, 2))
+ rule.setContent {
+ LazyStaggeredGrid(1) {
+ items(list, key = { it }) {
+ Item(it)
+ Item(it + 1)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, 0f),
+ 2 to AxisOffset(0f, itemSize),
+ 3 to AxisOffset(0f, itemSize)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(2, 0)
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, 0 + itemSize * fraction),
+ 1 to AxisOffset(0f, 0 + itemSize * fraction),
+ 2 to AxisOffset(0f, itemSize - itemSize * fraction),
+ 3 to AxisOffset(0f, itemSize - itemSize * fraction),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun multipleChildrenPerItemSomeDoNotAnimate() {
+ var list by mutableStateOf(listOf(0, 2))
+ rule.setContent {
+ LazyStaggeredGrid(1) {
+ items(list, key = { it }) {
+ Item(it)
+ Item(it + 1, animSpec = null)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(2, 0)
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, 0 + itemSize * fraction),
+ 1 to AxisOffset(0f, itemSize),
+ 2 to AxisOffset(0f, itemSize - itemSize * fraction),
+ 3 to AxisOffset(0f, 0f),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun animateSpacingChange() {
+ var currentSpacing by mutableStateOf(0.dp)
+ rule.setContent {
+ LazyStaggeredGrid(
+ 1,
+ spacing = currentSpacing
+ ) {
+ items(listOf(0, 1), key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ )
+
+ rule.runOnUiThread {
+ currentSpacing = spacingDp
+ }
+ rule.mainClock.advanceTimeByFrame()
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize + spacing * fraction),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveItemToTheBottomOutsideOfBounds() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
+ val gridSize = itemSize * 3
+ val gridSizeDp = with(rule.density) { gridSize.toDp() }
+ rule.setContent {
+ LazyStaggeredGrid(2, maxSize = gridSizeDp) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(0f, itemSize),
+ 3 to AxisOffset(itemSize, itemSize),
+ 4 to AxisOffset(0f, itemSize * 2),
+ 5 to AxisOffset(itemSize, itemSize * 2)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 8, 2, 3, 4, 5, 6, 7, 1, 9, 10, 11)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 1 moves to and item 8 moves from `gridSize`, right after the end edge
+ val item1Offset = AxisOffset(itemSize, 0 + gridSize * fraction)
+ val item8Offset =
+ AxisOffset(itemSize, gridSize - gridSize * fraction)
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ add(0 to AxisOffset(0f, 0f))
+ if (item1Offset.mainAxis < itemSize * 3) {
+ add(1 to item1Offset)
+ } else {
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ }
+ add(2 to AxisOffset(0f, itemSize))
+ add(3 to AxisOffset(itemSize, itemSize))
+ add(4 to AxisOffset(0f, itemSize * 2))
+ add(5 to AxisOffset(itemSize, itemSize * 2))
+ if (item8Offset.mainAxis < itemSize * 3) {
+ add(8 to item8Offset)
+ } else {
+ rule.onNodeWithTag("8").assertIsNotDisplayed()
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveItemToTheTopOutsideOfBounds() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
+ rule.setContent {
+ LazyStaggeredGrid(2, maxSize = itemSizeDp * 3, startIndex = 6) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 6 to AxisOffset(0f, 0f),
+ 7 to AxisOffset(itemSize, 0f),
+ 8 to AxisOffset(0f, itemSize),
+ 9 to AxisOffset(itemSize, itemSize),
+ 10 to AxisOffset(0f, itemSize * 2),
+ 11 to AxisOffset(itemSize, itemSize * 2)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 8, 2, 3, 4, 5, 6, 7, 1, 9, 10, 11)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 1 moves from and item 8 moves to `0 - itemSize`, right before the start edge
+ val item8Offset = AxisOffset(0f, itemSize - itemSize * 2 * fraction)
+ val item1Offset = AxisOffset(0f, -itemSize + itemSize * 2 * fraction)
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ if (item1Offset.mainAxis > -itemSize) {
+ add(1 to item1Offset)
+ } else {
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ }
+ add(6 to AxisOffset(0f, 0f))
+ add(7 to AxisOffset(itemSize, 0f))
+ if (item8Offset.mainAxis > -itemSize) {
+ add(8 to item8Offset)
+ } else {
+ rule.onNodeWithTag("8").assertIsNotDisplayed()
+ }
+ add(9 to AxisOffset(itemSize, itemSize))
+ add(10 to AxisOffset(0f, itemSize * 2))
+ add(11 to AxisOffset(itemSize, itemSize * 2))
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveFirstItemToEndCausingAllItemsToAnimate_withSpacing() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7))
+ rule.setContent {
+ LazyStaggeredGrid(2, spacing = spacingDp) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(1, 2, 3, 4, 5, 6, 7, 0)
+ }
+
+ onAnimationFrame { fraction ->
+ val increasingX = fraction * itemSize
+ val decreasingX = itemSize - itemSize * fraction
+ assertPositions(
+ 0 to AxisOffset(increasingX, itemSizePlusSpacing * 3 * fraction),
+ 1 to AxisOffset(decreasingX, 0f),
+ 2 to AxisOffset(
+ increasingX,
+ itemSizePlusSpacing - itemSizePlusSpacing * fraction
+ ),
+ 3 to AxisOffset(decreasingX, itemSizePlusSpacing),
+ 4 to AxisOffset(
+ increasingX,
+ itemSizePlusSpacing * 2 - itemSizePlusSpacing * fraction
+ ),
+ 5 to AxisOffset(decreasingX, itemSizePlusSpacing * 2),
+ 6 to AxisOffset(
+ increasingX,
+ itemSizePlusSpacing * 3 - itemSizePlusSpacing * fraction
+ ),
+ 7 to AxisOffset(decreasingX, itemSizePlusSpacing * 3),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveItemToTheBottomOutsideOfBounds_withSpacing() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
+ val gridSize = itemSize * 3 + spacing * 2
+ val gridSizeDp = with(rule.density) { gridSize.toDp() }
+ rule.setContent {
+ LazyStaggeredGrid(
+ 2,
+ maxSize = gridSizeDp,
+ spacing = spacingDp
+ ) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(0f, itemSizePlusSpacing),
+ 3 to AxisOffset(itemSize, itemSizePlusSpacing),
+ 4 to AxisOffset(0f, itemSizePlusSpacing * 2),
+ 5 to AxisOffset(itemSize, itemSizePlusSpacing * 2)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 8, 2, 3, 4, 5, 6, 7, 1, 9)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 1 moves to and item 8 moves from `gridSize`, right after the end edge
+ val item1Offset = AxisOffset(itemSize, gridSize * fraction)
+ val item8Offset = AxisOffset(itemSize, gridSize - gridSize * fraction)
+ val screenSize = itemSize * 3 + spacing * 2
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ add(0 to AxisOffset(0f, 0f))
+ if (item1Offset.mainAxis < screenSize) {
+ add(1 to item1Offset)
+ }
+ add(2 to AxisOffset(0f, itemSizePlusSpacing))
+ add(3 to AxisOffset(itemSize, itemSizePlusSpacing))
+ add(4 to AxisOffset(0f, itemSizePlusSpacing * 2))
+ add(5 to AxisOffset(itemSize, itemSizePlusSpacing * 2))
+ if (item8Offset.mainAxis < screenSize) {
+ add(8 to item8Offset)
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveItemToTheTopOutsideOfBounds_withSpacing() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
+ rule.setContent {
+ LazyStaggeredGrid(
+ 2,
+ maxSize = itemSizeDp * 3 + spacingDp * 2,
+ spacing = spacingDp,
+ startIndex = 4
+ ) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 4 to AxisOffset(0f, 0f),
+ 5 to AxisOffset(itemSize, 0f),
+ 6 to AxisOffset(0f, itemSizePlusSpacing),
+ 7 to AxisOffset(itemSize, itemSizePlusSpacing),
+ 8 to AxisOffset(0f, itemSizePlusSpacing * 2),
+ 9 to AxisOffset(itemSize, itemSizePlusSpacing * 2)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 8, 2, 3, 4, 5, 6, 7, 1, 9, 10, 11)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 8 moves to and item 1 moves from `-itemSize`, right before the start edge
+ val item1Offset = AxisOffset(
+ 0f,
+ -itemSize + (itemSize + itemSizePlusSpacing * 2) * fraction
+ )
+ val item8Offset = AxisOffset(
+ 0f,
+ itemSizePlusSpacing * 2 -
+ (itemSize + itemSizePlusSpacing * 2) * fraction
+ )
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ if (item1Offset.mainAxis > -itemSize) {
+ add(1 to item1Offset)
+ }
+ add(4 to AxisOffset(0f, 0f))
+ add(5 to AxisOffset(itemSize, 0f))
+ add(6 to AxisOffset(0f, itemSizePlusSpacing))
+ add(7 to AxisOffset(itemSize, itemSizePlusSpacing))
+ if (item8Offset.mainAxis > -itemSize) {
+ add(8 to item8Offset)
+ }
+ add(9 to AxisOffset(itemSize, itemSizePlusSpacing * 2))
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveItemToTheTopOutsideOfBounds_differentSizes() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
+ rule.setContent {
+ LazyStaggeredGrid(2, maxSize = itemSize2Dp * 2, startIndex = 6) {
+ items(list, key = { it }) {
+ val height = when (it) {
+ 2 -> itemSize3Dp
+ 6, 9 -> itemSize2Dp
+ 7 -> itemSize3Dp
+ 8 -> itemSizeDp
+ else -> itemSizeDp
+ }
+ Item(it, size = height)
+ }
+ }
+ }
+
+ val item2Size = itemSize3
+ val item6Size = itemSize2
+ val item7Size = itemSize3
+ val item8Size = itemSize
+ assertPositions(
+ 6 to AxisOffset(0f, 0f),
+ 7 to AxisOffset(itemSize, 0f),
+ 8 to AxisOffset(itemSize, item7Size),
+ 9 to AxisOffset(0f, item6Size)
+ )
+
+ rule.runOnUiThread {
+ // swap 8 and 2
+ list = listOf(0, 1, 8, 3, 4, 5, 6, 7, 2, 9, 10, 11)
+ }
+
+ onAnimationFrame { fraction ->
+ rule.onNodeWithTag("3").assertDoesNotExist()
+ rule.onNodeWithTag("4").assertDoesNotExist()
+ rule.onNodeWithTag("5").assertDoesNotExist()
+ // item 2 moves from and item 8 moves to `0 - item size`, right before the start edge
+ val startItem2Offset = -item2Size
+ val item2Offset =
+ startItem2Offset + (item7Size - startItem2Offset) * fraction
+ val endItem8Offset = -item8Size
+ val item8Offset = item7Size - (item7Size - endItem8Offset) * fraction
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ if (item8Offset > -item8Size) {
+ add(8 to AxisOffset(itemSize, item8Offset))
+ } else {
+ rule.onNodeWithTag("8").assertIsNotDisplayed()
+ }
+ add(6 to AxisOffset(0f, 0f))
+ add(7 to AxisOffset(itemSize, 0f))
+ if (item2Offset > -item2Size) {
+ add(2 to AxisOffset(itemSize, item2Offset))
+ } else {
+ rule.onNodeWithTag("2").assertIsNotDisplayed()
+ }
+ add(9 to AxisOffset(0f, item6Size))
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveItemToTheBottomOutsideOfBounds_differentSizes() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8))
+ val gridSize = itemSize2 * 2
+ val gridSizeDp = with(rule.density) { gridSize.toDp() }
+ rule.setContent {
+ LazyStaggeredGrid(2, maxSize = gridSizeDp) {
+ items(list, key = { it }) {
+ val height = when (it) {
+ 0, 3 -> itemSize2Dp
+ 1 -> itemSize3Dp
+ 2 -> itemSizeDp
+ 8 -> itemSize3Dp
+ else -> itemSizeDp
+ }
+ Item(it, size = height)
+ }
+ }
+ }
+
+ val item0Size = itemSize2
+ val item1Size = itemSize3
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(itemSize, item1Size),
+ 3 to AxisOffset(0f, item0Size)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 1, 8, 3, 4, 5, 6, 7, 2, 9, 10, 11)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 8 moves from and item 2 moves to `gridSize`, right after the end edge
+ val startItem8Offset = gridSize
+ val endItem2Offset = gridSize
+ val item2Offset =
+ item1Size + (endItem2Offset - item1Size) * fraction
+ val item8Offset =
+ startItem8Offset - (startItem8Offset - item1Size) * fraction
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ add(0 to AxisOffset(0f, 0f))
+ add(1 to AxisOffset(itemSize, 0f))
+ if (item8Offset < gridSize) {
+ add(8 to AxisOffset(itemSize, item8Offset))
+ } else {
+ rule.onNodeWithTag("8").assertIsNotDisplayed()
+ }
+ add(3 to AxisOffset(0f, item0Size))
+ if (item2Offset < gridSize) {
+ add(2 to AxisOffset(itemSize, item2Offset))
+ } else {
+ rule.onNodeWithTag("2").assertIsNotDisplayed()
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveItemToEndCausingNextItemsToAnimate_withContentPadding() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ val rawStartPadding = 8f
+ val rawEndPadding = 12f
+ val (startPaddingDp, endPaddingDp) = with(rule.density) {
+ rawStartPadding.toDp() to rawEndPadding.toDp()
+ }
+ rule.setContent {
+ LazyStaggeredGrid(1, startPadding = startPaddingDp, endPadding = endPaddingDp) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ val startPadding = if (reverseLayout) rawEndPadding else rawStartPadding
+ assertPositions(
+ 0 to AxisOffset(0f, startPadding),
+ 1 to AxisOffset(0f, startPadding + itemSize),
+ 2 to AxisOffset(0f, startPadding + itemSize * 2),
+ 3 to AxisOffset(0f, startPadding + itemSize * 3),
+ 4 to AxisOffset(0f, startPadding + itemSize * 4),
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 2, 3, 4, 1)
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, startPadding),
+ 1 to AxisOffset(
+ 0f,
+ startPadding + itemSize + itemSize * 3 * fraction
+ ),
+ 2 to AxisOffset(
+ 0f,
+ startPadding + itemSize * 2 - itemSize * fraction
+ ),
+ 3 to AxisOffset(
+ 0f,
+ startPadding + itemSize * 3 - itemSize * fraction
+ ),
+ 4 to AxisOffset(
+ 0f,
+ startPadding + itemSize * 4 - itemSize * fraction
+ ),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun reorderFirstAndLastItems_noNewLayoutInfoProduced() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+
+ var measurePasses = 0
+ rule.setContent {
+ LazyStaggeredGrid(1) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ LaunchedEffect(Unit) {
+ snapshotFlow { state.layoutInfo }
+ .collect {
+ measurePasses++
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(4, 1, 2, 3, 0)
+ }
+
+ var startMeasurePasses = Int.MIN_VALUE
+ onAnimationFrame { fraction ->
+ if (fraction == 0f) {
+ startMeasurePasses = measurePasses
+ }
+ }
+ rule.mainClock.advanceTimeByFrame()
+ // new layoutInfo is produced on every remeasure of Lazy lists.
+ // but we want to avoid remeasuring and only do relayout on each animation frame.
+ // two extra measures are possible as we switch inProgress flag.
+ assertThat(measurePasses).isAtMost(startMeasurePasses + 2)
+ }
+
+ @Test
+ fun noAnimationWhenScrolledToOtherPosition() {
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 3) {
+ items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollToItem(0, (itemSize / 2).roundToInt())
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, -itemSize / 2),
+ 1 to AxisOffset(0f, itemSize / 2),
+ 2 to AxisOffset(0f, itemSize * 3 / 2),
+ 3 to AxisOffset(0f, itemSize * 5 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollForwardBySmallOffset() {
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 3) {
+ items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(itemSize / 2f)
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, -itemSize / 2),
+ 1 to AxisOffset(0f, itemSize / 2),
+ 2 to AxisOffset(0f, itemSize * 3 / 2),
+ 3 to AxisOffset(0f, itemSize * 5 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollBackwardBySmallOffset() {
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 3, startIndex = 2) {
+ items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(-itemSize / 2f)
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 1 to AxisOffset(0f, -itemSize / 2),
+ 2 to AxisOffset(0f, itemSize / 2),
+ 3 to AxisOffset(0f, itemSize * 3 / 2),
+ 4 to AxisOffset(0f, itemSize * 5 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollForwardByLargeOffset() {
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 3) {
+ items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(itemSize * 2.5f)
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 2 to AxisOffset(0f, -itemSize / 2),
+ 3 to AxisOffset(0f, itemSize / 2),
+ 4 to AxisOffset(0f, itemSize * 3 / 2),
+ 5 to AxisOffset(0f, itemSize * 5 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollBackwardByLargeOffset() {
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 3, startIndex = 3) {
+ items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(-itemSize * 2.5f)
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, -itemSize / 2),
+ 1 to AxisOffset(0f, itemSize / 2),
+ 2 to AxisOffset(0f, itemSize * 3 / 2),
+ 3 to AxisOffset(0f, itemSize * 5 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollForwardByLargeOffset_differentSizes() {
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 3) {
+ items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+ Item(it, size = if (it % 2 == 0) itemSizeDp else itemSize2Dp)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(itemSize + itemSize2 + itemSize / 2f)
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 2 to AxisOffset(0f, -itemSize / 2),
+ 3 to AxisOffset(0f, itemSize / 2),
+ 4 to AxisOffset(0f, itemSize2 + itemSize / 2),
+ 5 to AxisOffset(0f, itemSize2 + itemSize * 3 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollBackwardByLargeOffset_differentSizes() {
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 3, startIndex = 3) {
+ items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
+ Item(it, size = if (it % 2 == 0) itemSizeDp else itemSize2Dp)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(-(itemSize + itemSize2 + itemSize / 2f))
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, -itemSize / 2),
+ 1 to AxisOffset(0f, itemSize / 2),
+ 2 to AxisOffset(0f, itemSize2 + itemSize / 2),
+ 3 to AxisOffset(0f, itemSize2 + itemSize * 3 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollForwardByLargeOffset_multipleCells() {
+ rule.setContent {
+ LazyStaggeredGrid(3, maxSize = itemSizeDp * 2) {
+ items(List(20) { it }, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(itemSize * 2, 0f),
+ 3 to AxisOffset(0f, itemSize),
+ 4 to AxisOffset(itemSize, itemSize),
+ 5 to AxisOffset(itemSize * 2, itemSize)
+ )
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(itemSize * 2.5f)
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 6 to AxisOffset(0f, -itemSize / 2),
+ 7 to AxisOffset(itemSize, -itemSize / 2),
+ 8 to AxisOffset(itemSize * 2, -itemSize / 2),
+ 9 to AxisOffset(0f, itemSize / 2),
+ 10 to AxisOffset(itemSize, itemSize / 2),
+ 11 to AxisOffset(itemSize * 2, itemSize / 2),
+ 12 to AxisOffset(0f, itemSize * 3 / 2),
+ 13 to AxisOffset(itemSize, itemSize * 3 / 2),
+ 14 to AxisOffset(itemSize * 2, itemSize * 3 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollBackwardByLargeOffset_multipleCells() {
+ rule.setContent {
+ LazyStaggeredGrid(3, maxSize = itemSizeDp * 2, startIndex = 9) {
+ items(List(20) { it }, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 9 to AxisOffset(0f, 0f),
+ 10 to AxisOffset(itemSize, 0f),
+ 11 to AxisOffset(itemSize * 2, 0f),
+ 12 to AxisOffset(0f, itemSize),
+ 13 to AxisOffset(itemSize, itemSize),
+ 14 to AxisOffset(itemSize * 2, itemSize)
+ )
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(-itemSize * 2.5f)
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, -itemSize / 2),
+ 1 to AxisOffset(itemSize, -itemSize / 2),
+ 2 to AxisOffset(itemSize * 2, -itemSize / 2),
+ 3 to AxisOffset(0f, itemSize / 2),
+ 4 to AxisOffset(itemSize, itemSize / 2),
+ 5 to AxisOffset(itemSize * 2, itemSize / 2),
+ 6 to AxisOffset(0f, itemSize * 3 / 2),
+ 7 to AxisOffset(itemSize, itemSize * 3 / 2),
+ 8 to AxisOffset(itemSize * 2, itemSize * 3 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollForwardByLargeOffset_differentSpans() {
+ rule.setContent {
+ LazyStaggeredGrid(2, maxSize = itemSizeDp * 2) {
+ items(
+ List(10) { it },
+ key = { it },
+ span = {
+ if (it == 6) {
+ StaggeredGridItemSpan.FullLine
+ } else {
+ StaggeredGridItemSpan.SingleLane
+ }
+ }
+ ) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(0f, itemSize),
+ 3 to AxisOffset(itemSize, itemSize),
+ )
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(itemSize * 2.5f)
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 4 to AxisOffset(0f, -itemSize / 2),
+ 5 to AxisOffset(itemSize, -itemSize / 2),
+ 6 to AxisOffset(0f, itemSize / 2), // 3 spans
+ 7 to AxisOffset(0f, itemSize * 3 / 2),
+ 8 to AxisOffset(itemSize, itemSize * 3 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenScrollBackwardByLargeOffset_differentSpans() {
+ rule.setContent {
+ LazyStaggeredGrid(2, maxSize = itemSizeDp * 2) {
+ items(
+ List(10) { it },
+ key = { it },
+ span = {
+ if (it == 2) {
+ StaggeredGridItemSpan.FullLine
+ } else {
+ StaggeredGridItemSpan.SingleLane
+ }
+ }
+ ) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(itemSize * 3f)
+ }
+ }
+
+ assertPositions(
+ 5 to AxisOffset(0f, 0f),
+ 6 to AxisOffset(itemSize, 0f),
+ 7 to AxisOffset(0f, itemSize),
+ 8 to AxisOffset(itemSize, itemSize),
+ )
+
+ rule.runOnUiThread {
+ runBlocking {
+ state.scrollBy(-itemSize * 2.5f)
+ }
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, -itemSize / 2),
+ 1 to AxisOffset(itemSize, -itemSize / 2),
+ 2 to AxisOffset(0f, itemSize / 2), // 3 spans
+ 3 to AxisOffset(0f, itemSize * 3 / 2),
+ 4 to AxisOffset(itemSize, itemSize * 3 / 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun animatingItemWithPreviousIndexLargerThanTheNewItemCount() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6, 7))
+ val gridSize = itemSize * 2
+ val gridSizeDp = with(rule.density) { gridSize.toDp() }
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = gridSizeDp) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertLayoutInfoPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 6)
+ }
+
+ onAnimationFrame { fraction ->
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ add(0 to AxisOffset(0f, 0f))
+ val item6MainAxis = gridSize - (gridSize - itemSize) * fraction
+ if (item6MainAxis < gridSize) {
+ add(6 to AxisOffset(0f, item6MainAxis))
+ } else {
+ rule.onNodeWithTag("6").assertIsNotDisplayed()
+ }
+ }
+
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun animatingItemsWithPreviousIndexLargerThanTheNewItemCount_differentSpans() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5, 6))
+ val gridSize = itemSize * 2
+ val gridSizeDp = with(rule.density) { gridSize.toDp() }
+ rule.setContent {
+ LazyStaggeredGrid(2, maxSize = gridSizeDp) {
+ items(list, key = { it }, span = {
+ if (it == 6) {
+ StaggeredGridItemSpan.FullLine
+ } else {
+ StaggeredGridItemSpan.SingleLane
+ }
+ }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertLayoutInfoPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(0f, itemSize),
+ 3 to AxisOffset(itemSize, itemSize)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 4, 6)
+ }
+
+ onAnimationFrame { fraction ->
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ add(0 to AxisOffset(0f, 0f))
+ val item4MainAxis = gridSize - gridSize * fraction
+ if (item4MainAxis < gridSize) {
+ add(
+ 4 to AxisOffset(itemSize, item4MainAxis)
+ )
+ } else {
+ rule.onNodeWithTag("4").assertIsNotDisplayed()
+ }
+ val item6MainAxis = gridSize - (gridSize - itemSize) * fraction
+ if (item6MainAxis < gridSize) {
+ add(
+ 6 to AxisOffset(0f, item6MainAxis)
+ )
+ } else {
+ rule.onNodeWithTag("6").assertIsNotDisplayed()
+ }
+ }
+
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun itemWithSpecsIsMovingOut() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3))
+ val gridSize = itemSize * 2
+ val gridSizeDp = with(rule.density) { gridSize.toDp() }
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = gridSizeDp) {
+ items(list, key = { it }) {
+ Item(it, animSpec = if (it == 1) AnimSpec else null)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(0, 2, 3, 1)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 1 moves to `gridSize`
+ val item1Offset = itemSize + (gridSize - itemSize) * fraction
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ add(0 to AxisOffset(0f, 0f))
+ if (item1Offset < gridSize) {
+ add(1 to AxisOffset(0f, item1Offset))
+ } else {
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveTwoItemsToTheTopOutsideOfBounds() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 3f, startIndex = 3) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 3 to AxisOffset(0f, 0f),
+ 4 to AxisOffset(0f, itemSize),
+ 5 to AxisOffset(0f, itemSize * 2)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 4, 5, 3, 1, 2)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 2 moves from and item 5 moves to `-itemSize`, right before the start edge
+ val item2Offset = -itemSize + itemSize * 3 * fraction
+ val item5Offset = itemSize * 2 - itemSize * 3 * fraction
+ // item 1 moves from and item 4 moves to `-itemSize * 2`, right before item 2
+ val item1Offset = -itemSize * 2 + itemSize * 3 * fraction
+ val item4Offset = itemSize - itemSize * 3 * fraction
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ if (item1Offset > -itemSize) {
+ add(1 to AxisOffset(0f, item1Offset))
+ } else {
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ }
+ if (item2Offset > -itemSize) {
+ add(2 to AxisOffset(0f, item2Offset))
+ } else {
+ rule.onNodeWithTag("2").assertIsNotDisplayed()
+ }
+ add(3 to AxisOffset(0f, 0f))
+ if (item4Offset > -itemSize) {
+ add(4 to AxisOffset(0f, item4Offset))
+ } else {
+ rule.onNodeWithTag("4").assertIsNotDisplayed()
+ }
+ if (item5Offset > -itemSize) {
+ add(5 to AxisOffset(0f, item5Offset))
+ } else {
+ rule.onNodeWithTag("5").assertIsNotDisplayed()
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveTwoItemsToTheTopOutsideOfBounds_withReordering() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 3f, startIndex = 3) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 3 to AxisOffset(0f, 0f),
+ 4 to AxisOffset(0f, itemSize),
+ 5 to AxisOffset(0f, itemSize * 2)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 5, 4, 3, 2, 1)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 2 moves from and item 4 moves to `-itemSize`, right before the start edge
+ val item2Offset = -itemSize + itemSize * 2 * fraction
+ val item4Offset = itemSize - itemSize * 2 * fraction
+ // item 1 moves from and item 5 moves to `-itemSize * 2`, right before item 2
+ val item1Offset = -itemSize * 2 + itemSize * 4 * fraction
+ val item5Offset = itemSize * 2 - itemSize * 4 * fraction
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ if (item1Offset > -itemSize) {
+ add(1 to AxisOffset(0f, item1Offset))
+ } else {
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ }
+ if (item2Offset > -itemSize) {
+ add(2 to AxisOffset(0f, item2Offset))
+ } else {
+ rule.onNodeWithTag("2").assertIsNotDisplayed()
+ }
+ add(3 to AxisOffset(0f, 0f))
+ if (item4Offset > -itemSize) {
+ add(4 to AxisOffset(0f, item4Offset))
+ } else {
+ rule.onNodeWithTag("4").assertIsNotDisplayed()
+ }
+ if (item5Offset > -itemSize) {
+ add(5 to AxisOffset(0f, item5Offset))
+ } else {
+ rule.onNodeWithTag("5").assertIsNotDisplayed()
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveTwoItemsToTheTopOutsideOfBounds_itemsOfDifferentLanes() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+ rule.setContent {
+ LazyStaggeredGrid(2, maxSize = itemSizeDp * 2f, startIndex = 2) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 2 to AxisOffset(0f, 0f),
+ 3 to AxisOffset(itemSize, 0f),
+ 4 to AxisOffset(0f, itemSize),
+ 5 to AxisOffset(itemSize, itemSize)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(4, 5, 2, 3, 0, 1)
+ }
+
+ onAnimationFrame { fraction ->
+ // items 0 and 2 moves from and items 4 and 5 moves to `-itemSize`,
+ // right before the start edge
+ val items0and1Offset = -itemSize + itemSize * 2 * fraction
+ val items4and5Offset = itemSize - itemSize * 2 * fraction
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ if (items0and1Offset > -itemSize) {
+ add(0 to AxisOffset(0f, items0and1Offset))
+ add(1 to AxisOffset(itemSize, items0and1Offset))
+ } else {
+ rule.onNodeWithTag("0").assertIsNotDisplayed()
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ }
+ add(2 to AxisOffset(0f, 0f))
+ add(3 to AxisOffset(itemSize, 0f))
+ if (items4and5Offset > -itemSize) {
+ add(4 to AxisOffset(0f, items4and5Offset))
+ add(5 to AxisOffset(itemSize, items4and5Offset))
+ } else {
+ rule.onNodeWithTag("4").assertIsNotDisplayed()
+ rule.onNodeWithTag("5").assertIsNotDisplayed()
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveTwoItemsToTheBottomOutsideOfBounds() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ val gridSize = itemSize * 3
+ val gridSizeDp = with(rule.density) { gridSize.toDp() }
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = gridSizeDp) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ 2 to AxisOffset(0f, itemSize * 2)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 3, 4, 1, 2)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 1 moves to and item 3 moves from `gridSize`, right after the end edge
+ val item1Offset = itemSize + (gridSize - itemSize) * fraction
+ val item3Offset = gridSize - (gridSize - itemSize) * fraction
+ // item 2 moves to and item 4 moves from `gridSize + itemSize`, right after item 4
+ val item2Offset = itemSize * 2 + (gridSize - itemSize) * fraction
+ val item4Offset = gridSize + itemSize - (gridSize - itemSize) * fraction
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ add(0 to AxisOffset(0f, 0f))
+ if (item1Offset < gridSize) {
+ add(1 to AxisOffset(0f, item1Offset))
+ } else {
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ }
+ if (item2Offset < gridSize) {
+ add(2 to AxisOffset(0f, item2Offset))
+ } else {
+ rule.onNodeWithTag("2").assertIsNotDisplayed()
+ }
+ if (item3Offset < gridSize) {
+ add(3 to AxisOffset(0f, item3Offset))
+ } else {
+ rule.onNodeWithTag("3").assertIsNotDisplayed()
+ }
+ if (item4Offset < gridSize) {
+ add(4 to AxisOffset(0f, item4Offset))
+ } else {
+ rule.onNodeWithTag("4").assertIsNotDisplayed()
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveTwoItemsToTheBottomOutsideOfBounds_withReordering() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ val gridSize = itemSize * 3
+ val gridSizeDp = with(rule.density) { gridSize.toDp() }
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = gridSizeDp) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ 2 to AxisOffset(0f, itemSize * 2)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 4, 3, 2, 1)
+ }
+
+ onAnimationFrame { fraction ->
+ // item 2 moves to and item 3 moves from `gridSize`, right after the end edge
+ val item2Offset = itemSize * 2 + (gridSize - itemSize * 2) * fraction
+ val item3Offset = gridSize - (gridSize - itemSize * 2) * fraction
+ // item 1 moves to and item 4 moves from `gridSize + itemSize`, right after item 4
+ val item1Offset = itemSize + (gridSize + itemSize - itemSize) * fraction
+ val item4Offset =
+ gridSize + itemSize - (gridSize + itemSize - itemSize) * fraction
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ add(0 to AxisOffset(0f, 0f))
+ if (item1Offset < gridSize) {
+ add(1 to AxisOffset(0f, item1Offset))
+ } else {
+ rule.onNodeWithTag("1").assertIsNotDisplayed()
+ }
+ if (item2Offset < gridSize) {
+ add(2 to AxisOffset(0f, item2Offset))
+ } else {
+ rule.onNodeWithTag("2").assertIsNotDisplayed()
+ }
+ if (item3Offset < gridSize) {
+ add(3 to AxisOffset(0f, item3Offset))
+ } else {
+ rule.onNodeWithTag("3").assertIsNotDisplayed()
+ }
+ if (item4Offset < gridSize) {
+ add(4 to AxisOffset(0f, item4Offset))
+ } else {
+ rule.onNodeWithTag("4").assertIsNotDisplayed()
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun moveTwoItemsToTheBottomOutsideOfBounds_itemsOfDifferentLanes() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+ val gridSize = itemSize * 2
+ val gridSizeDp = with(rule.density) { gridSize.toDp() }
+ rule.setContent {
+ LazyStaggeredGrid(2, maxSize = gridSizeDp) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(itemSize, 0f),
+ 2 to AxisOffset(0f, itemSize),
+ 3 to AxisOffset(itemSize, itemSize)
+ )
+
+ rule.runOnUiThread {
+ list = listOf(0, 1, 4, 5, 2, 3)
+ }
+
+ onAnimationFrame { fraction ->
+ // items 4 and 5 moves from and items 2 and 3 moves to `gridSize`,
+ // right before the start edge
+ val items4and5Offset = gridSize - (gridSize - itemSize) * fraction
+ val items2and3Offset = itemSize + (gridSize - itemSize) * fraction
+ val expected = mutableListOf<Pair<Any, Offset>>().apply {
+ add(0 to AxisOffset(0f, 0f))
+ add(1 to AxisOffset(itemSize, 0f))
+ if (items2and3Offset < gridSize) {
+ add(2 to AxisOffset(0f, items2and3Offset))
+ add(3 to AxisOffset(itemSize, items2and3Offset))
+ } else {
+ rule.onNodeWithTag("2").assertIsNotDisplayed()
+ rule.onNodeWithTag("3").assertIsNotDisplayed()
+ }
+ if (items4and5Offset < gridSize) {
+ add(4 to AxisOffset(0f, items4and5Offset))
+ add(5 to AxisOffset(itemSize, items4and5Offset))
+ } else {
+ rule.onNodeWithTag("4").assertIsNotDisplayed()
+ rule.onNodeWithTag("5").assertIsNotDisplayed()
+ }
+ }
+ assertPositions(
+ expected = expected.toTypedArray(),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun noAnimationWhenParentSizeShrinks() {
+ var size by mutableStateOf(itemSizeDp * 3)
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = size) {
+ items(listOf(0, 1, 2), key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ size = itemSizeDp * 2
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ fraction = fraction
+ )
+ rule.onNodeWithTag("2").assertIsNotDisplayed()
+ }
+ }
+
+ @Test
+ fun noAnimationWhenParentSizeExpands() {
+ var size by mutableStateOf(itemSizeDp * 2)
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = size) {
+ items(listOf(0, 1, 2), key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ size = itemSizeDp * 3
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ 2 to AxisOffset(0f, itemSize * 2),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun scrollIsAffectingItemsMovingWithinViewport() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3))
+ val scrollDelta = spacing
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = itemSizeDp * 2) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(0, 2, 1, 3)
+ }
+
+ onAnimationFrame { fraction ->
+ if (fraction == 0f) {
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ 2 to AxisOffset(0f, itemSize * 2),
+ fraction = fraction
+ )
+ rule.runOnUiThread {
+ runBlocking { state.scrollBy(scrollDelta) }
+ }
+ }
+ assertPositions(
+ 0 to AxisOffset(0f, -scrollDelta),
+ 1 to AxisOffset(0f, itemSize - scrollDelta + itemSize * fraction),
+ 2 to AxisOffset(0f, itemSize * 2 - scrollDelta - itemSize * fraction),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun scrollIsNotAffectingItemMovingToTheBottomOutsideOfBounds() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ val scrollDelta = spacing
+ val containerSizeDp = itemSizeDp * 2
+ val containerSize = itemSize * 2
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = containerSizeDp) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(0, 4, 2, 3, 1)
+ }
+
+ onAnimationFrame { fraction ->
+ if (fraction == 0f) {
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ fraction = fraction
+ )
+ rule.runOnUiThread {
+ runBlocking { state.scrollBy(scrollDelta) }
+ }
+ }
+ assertPositions(
+ 0 to AxisOffset(0f, -scrollDelta),
+ 1 to AxisOffset(0f, itemSize + (containerSize - itemSize) * fraction),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun scrollIsNotAffectingItemMovingToTheTopOutsideOfBounds() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ val scrollDelta = -spacing
+ val containerSizeDp = itemSizeDp * 2
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = containerSizeDp, startIndex = 2) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(3, 0, 1, 2, 4)
+ }
+
+ onAnimationFrame { fraction ->
+ if (fraction == 0f) {
+ assertPositions(
+ 2 to AxisOffset(0f, 0f),
+ 3 to AxisOffset(0f, itemSize),
+ fraction = fraction
+ )
+ rule.runOnUiThread {
+ runBlocking { state.scrollBy(scrollDelta) }
+ }
+ }
+ assertPositions(
+ 2 to AxisOffset(0f, -scrollDelta),
+ 3 to AxisOffset(0f, itemSize - (itemSize * 2 * fraction)),
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
+ fun afterScrollingEnoughToReachNewPositionScrollDeltasStartAffectingPosition() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ val containerSizeDp = itemSizeDp * 2
+ val scrollDelta = spacing
+ rule.setContent {
+ LazyStaggeredGrid(1, maxSize = containerSizeDp) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ list = listOf(0, 4, 2, 3, 1)
+ }
+
+ onAnimationFrame { fraction ->
+ if (fraction == 0f) {
+ assertPositions(
+ 0 to AxisOffset(0f, 0f),
+ 1 to AxisOffset(0f, itemSize),
+ fraction = fraction
+ )
+ rule.runOnUiThread {
+ runBlocking { state.scrollBy(itemSize * 2) }
+ }
+ assertPositions(
+ 2 to AxisOffset(0f, 0f),
+ 3 to AxisOffset(0f, itemSize),
+ // after the first scroll the new position of item 1 is still not reached
+ // so the target didn't change, we still aim to end right after the bounds
+ 1 to AxisOffset(0f, itemSize),
+ fraction = fraction
+ )
+ rule.runOnUiThread {
+ runBlocking { state.scrollBy(scrollDelta) }
+ }
+ assertPositions(
+ 2 to AxisOffset(0f, 0f - scrollDelta),
+ 3 to AxisOffset(0f, itemSize - scrollDelta),
+ // after the second scroll the item 1 is visible, so we know its new target
+ // position. the animation is now targeting the real end position and now
+ // we are reacting on the scroll deltas
+ 1 to AxisOffset(0f, itemSize - scrollDelta),
+ fraction = fraction
+ )
+ }
+ assertPositions(
+ 2 to AxisOffset(0f, -scrollDelta),
+ 3 to AxisOffset(0f, itemSize - scrollDelta),
+ 1 to AxisOffset(0f, itemSize - scrollDelta + itemSize * fraction),
+ fraction = fraction
+ )
+ }
+ }
+
+ private fun AxisOffset(crossAxis: Float, mainAxis: Float) =
+ if (isVertical) Offset(crossAxis, mainAxis) else Offset(mainAxis, crossAxis)
+
+ private val Offset.mainAxis: Float get() = if (isVertical) y else x
+
+ private fun assertPositions(
+ vararg expected: Pair<Any, Offset>,
+ crossAxis: List<Pair<Any, Float>>? = null,
+ fraction: Float? = null,
+ autoReverse: Boolean = reverseLayout
+ ) {
+ val roundedExpected = expected.map { it.first to it.second.round() }
+ val actualBounds = rule.onAllNodes(NodesWithTagMatcher)
+ .fetchSemanticsNodes()
+ .associateBy(
+ keySelector = { it.config[SemanticsProperties.TestTag] },
+ valueTransform = { IntRect(it.positionInRoot.round(), it.size) }
+ )
+ val actualPositions = expected.map {
+ it.first to actualBounds.getValue(it.first.toString()).topLeft
+ }
+ val subject = if (fraction == null) {
+ assertThat(actualPositions)
+ } else {
+ Truth.assertWithMessage("Fraction=$fraction").that(actualPositions)
+ }
+ subject.isEqualTo(
+ roundedExpected.let { list ->
+ if (!autoReverse) {
+ list
+ } else {
+ val containerSize = actualBounds.getValue(ContainerTag).size
+ list.map {
+ val itemSize = actualBounds.getValue(it.first.toString()).size
+ it.first to
+ IntOffset(
+ if (isVertical) {
+ it.second.x
+ } else {
+ containerSize.width - itemSize.width - it.second.x
+ },
+ if (!isVertical) {
+ it.second.y
+ } else {
+ containerSize.height - itemSize.height - it.second.y
+ }
+ )
+ }
+ }
+ }
+ )
+ if (crossAxis != null) {
+ val actualCross = expected.map {
+ it.first to actualBounds.getValue(it.first.toString()).topLeft
+ .let { offset -> if (isVertical) offset.x else offset.y }
+ }
+ Truth.assertWithMessage(
+ "CrossAxis" + if (fraction != null) "for fraction=$fraction" else ""
+ )
+ .that(actualCross)
+ .isEqualTo(crossAxis.map { it.first to it.second.roundToInt() })
+ }
+ }
+
+ private fun assertLayoutInfoPositions(vararg offsets: Pair<Any, Offset>) {
+ rule.runOnIdle {
+ assertThat(visibleItemsOffsets).isEqualTo(offsets.map { it.first to it.second.round() })
+ }
+ }
+
+ private val visibleItemsOffsets: List<Pair<Any, IntOffset>>
+ get() = state.layoutInfo.visibleItemsInfo.map {
+ it.key to it.offset
+ }
+
+ private fun onAnimationFrame(duration: Long = Duration, onFrame: (fraction: Float) -> Unit) {
+ require(duration.mod(FrameDuration) == 0L)
+ rule.waitForIdle()
+ rule.mainClock.advanceTimeByFrame()
+ var expectedTime = rule.mainClock.currentTime
+ for (i in 0..duration step FrameDuration) {
+ val fraction = i / duration.toFloat()
+ onFrame(fraction)
+ rule.mainClock.advanceTimeBy(FrameDuration)
+ expectedTime += FrameDuration
+ assertThat(expectedTime).isEqualTo(rule.mainClock.currentTime)
+ }
+ }
+
+ @Composable
+ private fun LazyStaggeredGrid(
+ cells: Int,
+ minSize: Dp = 0.dp,
+ maxSize: Dp = containerSizeDp,
+ startIndex: Int = 0,
+ startPadding: Dp = 0.dp,
+ endPadding: Dp = 0.dp,
+ spacing: Dp = 0.dp,
+ content: LazyStaggeredGridScope.() -> Unit
+ ) {
+ state = rememberLazyStaggeredGridState(startIndex)
+ if (isVertical) {
+ LazyVerticalStaggeredGrid(
+ StaggeredGridCells.Fixed(cells),
+ Modifier
+ .requiredHeightIn(minSize, maxSize)
+ .requiredWidth(itemSizeDp * cells)
+ .testTag(ContainerTag),
+ state = state,
+ verticalItemSpacing = spacing,
+ reverseLayout = reverseLayout,
+ contentPadding = PaddingValues(top = startPadding, bottom = endPadding),
+ content = content
+ )
+ } else {
+ LazyHorizontalStaggeredGrid(
+ StaggeredGridCells.Fixed(cells),
+ Modifier
+ .requiredWidthIn(minSize, maxSize)
+ .requiredHeight(itemSizeDp * cells)
+ .testTag(ContainerTag),
+ state = state,
+ reverseLayout = reverseLayout,
+ horizontalItemSpacing = spacing,
+ contentPadding = PaddingValues(start = startPadding, end = endPadding),
+ content = content
+ )
+ }
+ }
+
+ @Composable
+ private fun LazyStaggeredGridItemScope.Item(
+ tag: Int,
+ size: Dp = itemSizeDp,
+ animSpec: FiniteAnimationSpec<IntOffset>? = AnimSpec
+ ) {
+ Box(
+ if (animSpec != null) {
+ Modifier.animateItemPlacement(animSpec)
+ } else {
+ Modifier
+ }
+ .then(
+ if (isVertical) {
+ Modifier.requiredHeight(size)
+ } else {
+ Modifier.requiredWidth(size)
+ }
+ )
+ .testTag(tag.toString())
+ )
+ }
+
+ private fun SemanticsNodeInteraction.assertMainAxisSizeIsEqualTo(
+ expected: Dp
+ ): SemanticsNodeInteraction {
+ return if (isVertical) assertHeightIsEqualTo(expected) else assertWidthIsEqualTo(expected)
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun params() = arrayOf(
+ Config(isVertical = true, reverseLayout = false),
+ Config(isVertical = false, reverseLayout = false),
+ Config(isVertical = true, reverseLayout = true),
+ Config(isVertical = false, reverseLayout = true),
+ )
+
+ class Config(
+ val isVertical: Boolean,
+ val reverseLayout: Boolean
+ ) {
+ override fun toString() =
+ (if (isVertical) "LazyVerticalGrid" else "LazyHorizontalGrid") +
+ (if (reverseLayout) "(reverse)" else "")
+ }
+ }
+}
+
+private val FrameDuration = 16L
+private val Duration = 64L // 4 frames, so we get 0f, 0.25f, 0.5f, 0.75f and 1f fractions
+private val AnimSpec = tween<IntOffset>(Duration.toInt(), easing = LinearEasing)
+private val ContainerTag = "container"
+private val NodesWithTagMatcher = SemanticsMatcher("NodesWithTag") {
+ it.config.contains(SemanticsProperties.TestTag)
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimatedScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimatedScrollTest.kt
index 627f6fb..d68ad55 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimatedScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimatedScrollTest.kt
@@ -69,16 +69,22 @@
itemSizeDp = with(rule.density) {
itemSizePx.toDp()
}
+ }
+
+ private fun testScroll(spacingPx: Int = 0, assertBlock: suspend () -> Unit) {
rule.setContent {
- scope = rememberCoroutineScope()
state = rememberLazyStaggeredGridState()
- TestContent()
+ scope = rememberCoroutineScope()
+ TestContent(with(rule.density) { spacingPx.toDp() })
}
rule.waitForIdle()
+ runBlocking {
+ assertBlock()
+ }
}
@Test
- fun animateScrollBy() = runBlocking {
+ fun animateScrollBy() = testScroll {
val scrollDistance = 320
val expectedIndex = scrollDistance * 2 / itemSizePx // resolves to 6
@@ -92,7 +98,7 @@
}
@Test
- fun animateScrollToItem_positiveOffset() = runBlocking {
+ fun animateScrollToItem_positiveOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(10, 10)
}
@@ -101,7 +107,7 @@
}
@Test
- fun animateScrollToItem_positiveOffset_largerThanItem() = runBlocking {
+ fun animateScrollToItem_positiveOffset_largerThanItem() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(10, 150)
}
@@ -110,7 +116,7 @@
}
@Test
- fun animateScrollToItem_negativeOffset() = runBlocking {
+ fun animateScrollToItem_negativeOffset() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(10, -10)
}
@@ -119,7 +125,7 @@
}
@Test
- fun animateScrollToItem_beforeFirstItem() = runBlocking {
+ fun animateScrollToItem_beforeFirstItem() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(10)
state.animateScrollToItem(0, -10)
@@ -129,7 +135,7 @@
}
@Test
- fun animateScrollToItem_afterLastItem() {
+ fun animateScrollToItem_afterLastItem() = testScroll {
runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(100)
}
@@ -139,7 +145,7 @@
}
@Test
- fun animateScrollToItem_toFullSpan() {
+ fun animateScrollToItem_toFullSpan() = testScroll {
runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(50, 10)
}
@@ -149,7 +155,7 @@
}
@Test
- fun animateScrollToItem_toFullSpan_andBack() {
+ fun animateScrollToItem_toFullSpan_andBack() = testScroll {
runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(50, 10)
}
@@ -164,32 +170,32 @@
}
@Test
- fun animateScrollToItem_inBounds() {
+ fun animateScrollToItem_inBounds() = testScroll {
assertSpringAnimation(2)
}
@Test
- fun animateScrollToItem_inBounds_withOffset() {
+ fun animateScrollToItem_inBounds_withOffset() = testScroll {
assertSpringAnimation(2, itemSizePx / 2)
}
@Test
- fun animateScrollToItem_outOfBounds() {
+ fun animateScrollToItem_outOfBounds() = testScroll {
assertSpringAnimation(10)
}
@Test
- fun animateScrollToItem_firstItem() {
+ fun animateScrollToItem_firstItem() = testScroll {
assertSpringAnimation(fromIndex = 10, fromOffset = 10, toIndex = 0)
}
@Test
- fun animateScrollToItem_firstItem_toOffset() {
+ fun animateScrollToItem_firstItem_toOffset() = testScroll {
assertSpringAnimation(fromIndex = 10, fromOffset = 10, toIndex = 0, toOffset = 10)
}
@Test
- fun animateScrollToItemWithOffsetLargerThanItemSize_forward() {
+ fun animateScrollToItemWithOffsetLargerThanItemSize_forward() = testScroll {
runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(20, -itemSizePx * 3)
}
@@ -199,7 +205,7 @@
}
@Test
- fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = runBlocking {
+ fun animateScrollToItemWithOffsetLargerThanItemSize_backward() = testScroll {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(20)
state.animateScrollToItem(0, itemSizePx * 3)
@@ -208,11 +214,32 @@
assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
}
+ @Test
+ fun animateScrollToItem_outOfBounds_withSpacing() = testScroll(spacingPx = 10) {
+ assertSpringAnimation(20, spacingPx = 10)
+ }
+
+ @Test
+ fun animateScrollToItem_outOfBounds_withNegativeSpacing() = testScroll(spacingPx = -10) {
+ assertSpringAnimation(20, spacingPx = -10)
+ }
+
+ @Test
+ fun animateScrollToItem_backwards_withSpacing() = testScroll(spacingPx = 10) {
+ assertSpringAnimation(toIndex = 0, fromIndex = 20, spacingPx = 10)
+ }
+
+ @Test
+ fun animateScrollToItem_backwards_withNegativeSpacing() = testScroll(spacingPx = -10) {
+ assertSpringAnimation(toIndex = 0, fromIndex = 20, spacingPx = -10)
+ }
+
private fun assertSpringAnimation(
toIndex: Int,
toOffset: Int = 0,
fromIndex: Int = 0,
- fromOffset: Int = 0
+ fromOffset: Int = 0,
+ spacingPx: Int = 0
) {
if (fromIndex != 0 || fromOffset != 0) {
rule.runOnIdle {
@@ -236,8 +263,10 @@
Thread.sleep(5)
}
- val startOffset = (fromIndex / 2 * itemSizePx + fromOffset).toFloat()
- val endOffset = (toIndex / 2 * itemSizePx + toOffset).toFloat()
+ val itemSizeWSpacing = spacingPx + itemSizePx
+ val startOffset = (fromIndex / 2 * itemSizeWSpacing + fromOffset).toFloat()
+ val endOffset = (toIndex / 2 * itemSizeWSpacing + toOffset).toFloat()
+
val spec = FloatSpringSpec()
val duration =
@@ -250,9 +279,10 @@
val expectedValue =
spec.getValueFromNanos(nanosTime, startOffset, endOffset, 0f)
val actualValue =
- (state.firstVisibleItemIndex / 2 * itemSizePx + state.firstVisibleItemScrollOffset)
+ (state.firstVisibleItemIndex / 2 * itemSizeWSpacing +
+ state.firstVisibleItemScrollOffset)
assertWithMessage(
- "On animation frame at $i index=${state.firstVisibleItemIndex} " +
+ "On animation frame at ${i}ms index=${state.firstVisibleItemIndex} " +
"offset=${state.firstVisibleItemScrollOffset} expectedValue=$expectedValue"
).that(actualValue).isEqualTo(expectedValue.roundToInt(), tolerance = 1)
@@ -266,11 +296,12 @@
}
@Composable
- private fun TestContent() {
+ private fun TestContent(spacingDp: Dp) {
LazyStaggeredGrid(
lanes = 2,
state = state,
- modifier = Modifier.axisSize(itemSizeDp * 2, itemSizeDp * 5)
+ modifier = Modifier.axisSize(itemSizeDp * 2, itemSizeDp * 5),
+ mainAxisSpacing = spacingDp
) {
items(
count = 100,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt
index 74e519c..01f2c1c 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt
@@ -48,7 +48,6 @@
package androidx.compose.foundation.lazy.staggeredgrid
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -62,13 +61,12 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.test.filters.MediumTest
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-@OptIn(ExperimentalFoundationApi::class)
@MediumTest
@RunWith(Parameterized::class)
class LazyStaggeredGridArrangementsTest(
@@ -220,6 +218,55 @@
rule.onNodeWithTag("2")
.assertMainAxisStartPositionInRootIsEqualTo(0.dp)
.assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+ assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+ assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+ }
+
+ @Test
+ fun negativeSpacing_withContentPadding_itemsVisible() {
+ state = LazyStaggeredGridState()
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ modifier = Modifier
+ .testTag(LazyStaggeredGrid)
+ .axisSize(itemSizeDp * 2, itemSizeDp * 5),
+ state = state,
+ mainAxisSpacing = -itemSizeDp,
+ contentPadding = PaddingValues(beforeContent = itemSizeDp)
+ ) {
+ items(100) {
+ BasicText(
+ text = "$it",
+ modifier = Modifier
+ .testTag("$it")
+ .mainAxisSize(itemSizeDp * 2)
+ )
+ }
+ }
+ }
+
+ rule.onNodeWithTag("0")
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+ .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+ rule.onNodeWithTag("2")
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp * 2)
+ .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+ state.scrollBy(itemSizeDp)
+
+ rule.onNodeWithTag("0")
+ .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+ .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+ rule.onNodeWithTag("2")
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+ .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+ assertThat(state.firstVisibleItemIndex).isEqualTo(2)
+ assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
}
@Test
@@ -245,8 +292,8 @@
}
rule.runOnIdle {
- Truth.assertThat(state.firstVisibleItemIndex).isEqualTo(0)
- Truth.assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+ assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+ assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsTest.kt
new file mode 100644
index 0000000..1b82ba8
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsTest.kt
@@ -0,0 +1,707 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.BeyondBoundsLayout
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Above
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.After
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Before
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Below
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Left
+import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Right
+import androidx.compose.ui.layout.ModifierLocalBeyondBoundsLayout
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.modifier.modifierLocalConsumer
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.LayoutDirection.Ltr
+import androidx.compose.ui.unit.LayoutDirection.Rtl
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalComposeUiApi::class)
+@MediumTest
+@RunWith(Parameterized::class)
+class LazyStaggeredGridBeyondBoundsTest(param: Param) {
+
+ @get:Rule
+ val rule = createComposeRule()
+
+ // We need to wrap the inline class parameter in another class because Java can't instantiate
+ // the inline class.
+ class Param(
+ val beyondBoundsLayoutDirection: BeyondBoundsLayout.LayoutDirection,
+ val reverseLayout: Boolean,
+ val layoutDirection: LayoutDirection,
+ ) {
+ override fun toString() = "beyondBoundsLayoutDirection=$beyondBoundsLayoutDirection " +
+ "reverseLayout=$reverseLayout " +
+ "layoutDirection=$layoutDirection"
+ }
+
+ private val beyondBoundsLayoutDirection = param.beyondBoundsLayoutDirection
+ private val reverseLayout = param.reverseLayout
+ private val layoutDirection = param.layoutDirection
+ private val placedItems = mutableSetOf<Int>()
+ private var beyondBoundsLayout: BeyondBoundsLayout? = null
+ private lateinit var lazyStaggeredGridState: LazyStaggeredGridState
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun initParameters() = buildList {
+ for (beyondBoundsLayoutDirection in listOf(Left, Right, Above, Below, Before, After)) {
+ for (reverseLayout in listOf(false, true)) {
+ for (layoutDirection in listOf(Ltr, Rtl)) {
+ add(Param(beyondBoundsLayoutDirection, reverseLayout, layoutDirection))
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun onlyOneVisibleItemIsPlaced() {
+ // Arrange.
+ rule.setLazyContent(size = 10.toDp(), firstVisibleItem = 0) {
+ items(100) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(0)
+ assertThat(visibleItems).containsExactly(0)
+ }
+ }
+
+ @Test
+ fun onlyTwoVisibleItemsArePlaced() {
+ // Arrange.
+ rule.setLazyContent(size = 20.toDp(), firstVisibleItem = 0) {
+ items(100) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(0, 1)
+ assertThat(visibleItems).containsExactly(0, 1)
+ }
+ }
+
+ @Test
+ fun onlyThreeVisibleItemsArePlaced() {
+ // Arrange.
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 0) {
+ items(100) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(0, 1, 2)
+ assertThat(visibleItems).containsExactly(0, 1, 2)
+ }
+ }
+
+ @Test
+ fun emptyLazyList_doesNotCrash() {
+ // Arrange.
+ var addItems by mutableStateOf(true)
+ lateinit var beyondBoundsLayoutRef: BeyondBoundsLayout
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 0) {
+ if (addItems) {
+ item {
+ Box(
+ Modifier.modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ }
+ }
+ rule.runOnIdle {
+ beyondBoundsLayoutRef = beyondBoundsLayout!!
+ addItems = false
+ }
+
+ // Act.
+ val hasMoreContent = rule.runOnIdle {
+ beyondBoundsLayoutRef.layout(beyondBoundsLayoutDirection) {
+ hasMoreContent
+ }
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(hasMoreContent).isFalse()
+ }
+ }
+
+ @Test
+ fun oneExtraItemBeyondVisibleBounds() {
+ // Arrange.
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += 5 }
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index + 6 }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ // Assert that the beyond bounds items are present.
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ } else {
+ assertThat(placedItems).containsExactly(5, 6, 7, 8)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ placedItems.clear()
+ // Just return true so that we stop as soon as we run this once.
+ // This should result in one extra item being added.
+ true
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ }
+
+ @Test
+ fun oneExtraItemBeyondVisibleBounds_multipleCells() {
+ val itemSize = 50
+ val itemSizeDp = itemSize.toDp()
+ // Arrange.
+ rule.setLazyContent(cells = 2, size = itemSizeDp * 3, firstVisibleItem = 10) {
+ // item | item | x5
+ // item | local | x1
+ // item | item | x5
+ items(11) { index ->
+ Box(
+ Modifier
+ .size(itemSizeDp)
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(Modifier
+ .size(itemSizeDp)
+ .onPlaced { placedItems += 11 }
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ items(10) { index ->
+ Box(
+ Modifier
+ .size(itemSizeDp)
+ .onPlaced { placedItems += index + 12 }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ // Assert that the beyond bounds items are present.
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(9, 10, 11, 12, 13, 14, 15)
+ assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+ } else {
+ assertThat(placedItems).containsExactly(10, 11, 12, 13, 14, 15, 16)
+ assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+ }
+ placedItems.clear()
+ // Just return true so that we stop as soon as we run this once.
+ // This should result in one extra item being added.
+ true
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(10, 11, 12, 13, 14, 15)
+ assertThat(visibleItems).containsExactly(10, 11, 12, 13, 14, 15)
+ }
+ }
+
+ @Test
+ fun oneExtraItemBeyondVisibleBounds_multipleCells_staggered() {
+ val itemSize = 50
+ val itemSizeDp = itemSize.toDp()
+ // Arrange.
+ rule.setLazyContent(cells = 3, size = itemSizeDp * 2, firstVisibleItem = 4) {
+ // -------------
+ // | | 1 | |
+ // | 0 |---| 2 |
+ // | | 3 | |
+ // |-----------|
+ // | 4 |
+ // |-----------|
+ // | | 6 | |
+ // | 5 |---| 7 |
+ // | | 8 | |
+ // -------------
+ items(4) { index ->
+ Box(
+ Modifier
+ .size(itemSizeDp * if (index % 2 == 0) 2f else 1f)
+ .onPlaced { placedItems += index }
+ )
+ }
+ item(span = StaggeredGridItemSpan.FullLine) {
+ Box(Modifier
+ .size(itemSizeDp)
+ .onPlaced { placedItems += 4 }
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ items(4) { index ->
+ Box(
+ Modifier
+ .size(itemSizeDp * if (index % 2 == 0) 2f else 1f)
+ .onPlaced { placedItems += index + 5 }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ // Assert that the beyond bounds items are present.
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(3, 4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(4, 5, 6, 7)
+ } else {
+ assertThat(placedItems).containsExactly(4, 5, 6, 7, 8)
+ assertThat(visibleItems).containsExactly(4, 5, 6, 7)
+ }
+ placedItems.clear()
+ // Just return true so that we stop as soon as we run this once.
+ // This should result in one extra item being added.
+ true
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(4, 5, 6, 7)
+ }
+ }
+
+ @Test
+ fun twoExtraItemsBeyondVisibleBounds() {
+ // Arrange.
+ var extraItemCount = 2
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += 5 }
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index + 6 }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ if (--extraItemCount > 0) {
+ placedItems.clear()
+ // Return null to continue the search.
+ null
+ } else {
+ // Assert that the beyond bounds items are present.
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(3, 4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ } else {
+ assertThat(placedItems).containsExactly(5, 6, 7, 8, 9)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ placedItems.clear()
+ // Return true to stop the search.
+ true
+ }
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ }
+
+ @Test
+ fun allBeyondBoundsItemsInSpecifiedDirection() {
+ // Arrange.
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(
+ Modifier
+ .size(10.toDp())
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ .onPlaced { placedItems += 5 }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced {
+ placedItems += index + 6
+ }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ if (hasMoreContent) {
+ placedItems.clear()
+ // Just return null so that we keep adding more items till we reach the end.
+ null
+ } else {
+ // Assert that the beyond bounds items are present.
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(0, 1, 2, 3, 4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ } else {
+ assertThat(placedItems).containsExactly(5, 6, 7, 8, 9, 10)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ placedItems.clear()
+ // Return true to end the search.
+ true
+ }
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ }
+ }
+
+ @Test
+ fun beyondBoundsLayoutRequest_inDirectionPerpendicularToLazyListOrientation() {
+ // Arrange.
+ var beyondBoundsLayoutCount = 0
+ rule.setLazyContentInPerpendicularDirection(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index }
+ )
+ }
+ item {
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += 5 }
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced { placedItems += index + 6 }
+ )
+ }
+ }
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ placedItems.clear()
+ }
+
+ // Act.
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ beyondBoundsLayoutCount++
+ when (beyondBoundsLayoutDirection) {
+ Left, Right, Above, Below -> {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ Before, After -> {
+ if (expectedExtraItemsBeforeVisibleBounds()) {
+ assertThat(placedItems).containsExactly(4, 5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ } else {
+ assertThat(placedItems).containsExactly(5, 6, 7, 8)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ }
+ }
+ placedItems.clear()
+ // Just return true so that we stop as soon as we run this once.
+ // This should result in one extra item being added.
+ true
+ }
+ }
+
+ rule.runOnIdle {
+ when (beyondBoundsLayoutDirection) {
+ Left, Right, Above, Below -> {
+ assertThat(beyondBoundsLayoutCount).isEqualTo(0)
+ }
+ Before, After -> {
+ assertThat(beyondBoundsLayoutCount).isEqualTo(1)
+
+ // Assert that the beyond bounds items are removed.
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ else -> error("Unsupported BeyondBoundsLayoutDirection")
+ }
+ }
+ }
+
+ @Test
+ fun returningNullDoesNotCauseInfiniteLoop() {
+ // Arrange.
+ rule.setLazyContent(size = 30.toDp(), firstVisibleItem = 5) {
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced {
+ placedItems += index
+ }
+ )
+ }
+ item {
+ Box(
+ Modifier
+ .size(10.toDp())
+ .modifierLocalConsumer {
+ beyondBoundsLayout = ModifierLocalBeyondBoundsLayout.current
+ }
+ .onPlaced { placedItems += 5 }
+ )
+ }
+ items(5) { index ->
+ Box(
+ Modifier
+ .size(10.toDp())
+ .onPlaced {
+ placedItems += index + 6
+ }
+ )
+ }
+ }
+ rule.runOnIdle { placedItems.clear() }
+
+ // Act.
+ var count = 0
+ rule.runOnUiThread {
+ beyondBoundsLayout!!.layout(beyondBoundsLayoutDirection) {
+ // Assert that we don't keep iterating when there is no ending condition.
+ assertThat(count++).isLessThan(lazyStaggeredGridState.layoutInfo.totalItemsCount)
+ placedItems.clear()
+ // Always return null to continue the search.
+ null
+ }
+ }
+
+ // Assert that the beyond bounds items are removed.
+ rule.runOnIdle {
+ assertThat(placedItems).containsExactly(5, 6, 7)
+ assertThat(visibleItems).containsExactly(5, 6, 7)
+ }
+ }
+
+ private fun ComposeContentTestRule.setLazyContent(
+ size: Dp,
+ firstVisibleItem: Int,
+ cells: Int = 1,
+ content: LazyStaggeredGridScope.() -> Unit
+ ) {
+ setContent {
+ CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+ lazyStaggeredGridState = rememberLazyStaggeredGridState(firstVisibleItem)
+ when (beyondBoundsLayoutDirection) {
+ Left, Right, Before, After ->
+ LazyHorizontalStaggeredGrid(
+ rows = StaggeredGridCells.Fixed(cells),
+ modifier = Modifier.size(size),
+ state = lazyStaggeredGridState,
+ reverseLayout = reverseLayout,
+ content = content
+ )
+ Above, Below ->
+ LazyVerticalStaggeredGrid(
+ columns = StaggeredGridCells.Fixed(cells),
+ modifier = Modifier.size(size),
+ state = lazyStaggeredGridState,
+ reverseLayout = reverseLayout,
+ content = content
+ )
+ else -> unsupportedDirection()
+ }
+ }
+ }
+ }
+
+ private fun ComposeContentTestRule.setLazyContentInPerpendicularDirection(
+ size: Dp,
+ firstVisibleItem: Int,
+ content: LazyStaggeredGridScope.() -> Unit
+ ) {
+ setContent {
+ CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+ lazyStaggeredGridState = rememberLazyStaggeredGridState(firstVisibleItem)
+ when (beyondBoundsLayoutDirection) {
+ Left, Right, Before, After ->
+ LazyVerticalStaggeredGrid(
+ columns = StaggeredGridCells.Fixed(1),
+ modifier = Modifier.size(size),
+ state = lazyStaggeredGridState,
+ reverseLayout = reverseLayout,
+ content = content
+ )
+ Above, Below ->
+ LazyHorizontalStaggeredGrid(
+ rows = StaggeredGridCells.Fixed(1),
+ modifier = Modifier.size(size),
+ state = lazyStaggeredGridState,
+ reverseLayout = reverseLayout,
+ content = content
+ )
+ else -> unsupportedDirection()
+ }
+ }
+ }
+ }
+
+ private fun Int.toDp(): Dp = with(rule.density) { toDp() }
+
+ private val visibleItems: List<Int>
+ get() = lazyStaggeredGridState.layoutInfo.visibleItemsInfo.map { it.index }
+
+ private fun expectedExtraItemsBeforeVisibleBounds() = when (beyondBoundsLayoutDirection) {
+ Right -> if (layoutDirection == Ltr) reverseLayout else !reverseLayout
+ Left -> if (layoutDirection == Ltr) !reverseLayout else reverseLayout
+ Above -> !reverseLayout
+ Below -> reverseLayout
+ After -> false
+ Before -> true
+ else -> error("Unsupported BeyondBoundsDirection")
+ }
+
+ private fun unsupportedDirection(): Nothing = error(
+ "Lazy list does not support beyond bounds layout for the specified direction"
+ )
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridReverseLayoutTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridReverseLayoutTest.kt
index 54057c2..6af08de 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridReverseLayoutTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridReverseLayoutTest.kt
@@ -397,9 +397,40 @@
// bottom padding applies instead of the top
rule.onNodeWithTag("0")
- .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 3)
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 2)
rule.onNodeWithTag("1")
- .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 3)
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 2)
+ }
+
+ @Test
+ fun contentPadding_isPreserved() {
+ val state = LazyStaggeredGridState()
+ rule.setContent {
+ LazyStaggeredGrid(
+ lanes = 2,
+ modifier = Modifier
+ .axisSize(itemSize * 2, itemSize * 5)
+ .testTag(StaggeredGridTag),
+ contentPadding = PaddingValues(afterContent = itemSize * 2),
+ reverseLayout = true,
+ state = state
+ ) {
+ items(6) {
+ Box(Modifier.size(itemSize).testTag("$it"))
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ assertThat(state.firstVisibleItemIndex).isEqualTo(0)
+ assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
+ }
+
+ // bottom padding applies instead of the top
+ rule.onNodeWithTag("0")
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 2)
+ rule.onNodeWithTag("1")
+ .assertMainAxisStartPositionInRootIsEqualTo(itemSize * 2)
}
@Test
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 b6bbb66..80cc4f9 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
@@ -125,7 +125,7 @@
snapVelocityThreshold: Dp = MinFlingVelocityDp,
snapPositionalThreshold: Float = 0.5f,
key: ((index: Int) -> Any)? = null,
- pageContent: @Composable (page: Int) -> Unit = { Page(index = it) }
+ pageContent: @Composable PagerScope.(page: Int) -> Unit = { Page(index = it) }
) {
rule.setContent {
@@ -278,7 +278,7 @@
flingBehavior: SnapFlingBehavior = PagerDefaults.flingBehavior(state = state),
pageSpacing: Dp = 0.dp,
key: ((index: Int) -> Any)? = null,
- pageContent: @Composable (pager: Int) -> Unit
+ pageContent: @Composable PagerScope.(pager: Int) -> Unit
) {
if (vertical) {
VerticalPager(
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 77b0562..9bf833a 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
@@ -32,6 +32,7 @@
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
@@ -864,6 +865,21 @@
}
}
+ @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/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapter.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapter.kt
index 1877cfa..47f6759 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapter.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/AndroidTextInputAdapter.kt
@@ -26,7 +26,6 @@
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
-import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text2.input.TextEditFilter
@@ -230,7 +229,7 @@
* and grab the [InputConnection] to inject commands.
*/
@TestOnly
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
fun setInputConnectionCreatedListenerForTests(
listener: ((EditorInfo, InputConnection) -> Unit)?
) {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/ComposeInputMethodManager.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/ComposeInputMethodManager.kt
index 71b3288..caccd1a 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/ComposeInputMethodManager.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/ComposeInputMethodManager.kt
@@ -24,7 +24,7 @@
import android.view.inputmethod.ExtractedText
import android.view.inputmethod.InputMethodManager
import androidx.annotation.RequiresApi
-import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
import androidx.core.view.SoftwareKeyboardControllerCompat
import org.jetbrains.annotations.TestOnly
@@ -88,7 +88,7 @@
* avoid breaking unrelated tests.
*/
@TestOnly
-@RestrictTo(RestrictTo.Scope.TESTS)
+@VisibleForTesting
internal fun overrideComposeInputMethodManagerFactoryForTests(
factory: (View) -> ComposeInputMethodManager
): (View) -> ComposeInputMethodManager {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt
index b1955d7..e1546c6 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt
@@ -137,7 +137,7 @@
KeyInputModifierNode,
CompositionLocalConsumerModifierNode {
- private val pointerInputNode = SuspendingPointerInputModifierNode {
+ private val pointerInputNode = delegate(SuspendingPointerInputModifierNode {
detectTapAndPress(onTap = {
if (!isFocused) {
requestFocus()
@@ -145,10 +145,7 @@
textInputSession?.showSoftwareKeyboard()
}
})
- }
- // TODO: remove `.node` after aosp/2462416 lands and merge everything into one delegated
- // block
- .also { delegated { it.node } }
+ })
var keyboardOptions: KeyboardOptions = keyboardOptions.withDefaultsFrom(filter?.keyboardOptions)
private set
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 62f02ee..87d9b57 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
@@ -528,7 +528,7 @@
role: Role?,
onClick: () -> Unit
) : AbstractClickableNode(interactionSource, enabled, onClickLabel, role, onClick) {
- override val clickableSemanticsNode = delegated {
+ override val clickableSemanticsNode = delegate(
ClickableSemanticsNode(
enabled = enabled,
role = role,
@@ -537,16 +537,16 @@
onLongClick = null,
onLongClickLabel = null
)
- }
+ )
- override val clickablePointerInputNode = delegated {
+ override val clickablePointerInputNode = delegate(
ClickablePointerInputNode(
enabled = enabled,
interactionSource = interactionSource,
onClick = onClick,
interactionData = interactionData
)
- }
+ )
fun update(
interactionSource: MutableInteractionSource,
@@ -582,7 +582,7 @@
private var onLongClick: (() -> Unit)?,
onDoubleClick: (() -> Unit)?
) : AbstractClickableNode(interactionSource, enabled, onClickLabel, role, onClick) {
- override val clickableSemanticsNode = delegated {
+ override val clickableSemanticsNode = delegate(
ClickableSemanticsNode(
enabled = enabled,
role = role,
@@ -591,9 +591,9 @@
onLongClickLabel = onLongClickLabel,
onLongClick = onLongClick
)
- }
+ )
- override val clickablePointerInputNode = delegated {
+ override val clickablePointerInputNode = delegate(
CombinedClickablePointerInputNode(
enabled = enabled,
interactionSource = interactionSource,
@@ -602,7 +602,7 @@
onLongClick,
onDoubleClick
)
- }
+ )
fun update(
interactionSource: MutableInteractionSource,
@@ -846,10 +846,7 @@
ModifierLocalScrollableContainer.current || isComposeRootInScrollableContainer()
}
- private val pointerInputNode = SuspendingPointerInputModifierNode { pointerInput() }
- // TODO: remove `.node` after aosp/2462416 lands and merge everything into one delegated
- // block
- .also { delegated { it.node } }
+ private val pointerInputNode = delegate(SuspendingPointerInputModifierNode { pointerInput() })
protected abstract suspend fun PointerInputScope.pointerInput()
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 19838a9..c909c05 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
@@ -199,12 +199,11 @@
private var focusState: FocusState? = null
- private val focusableSemanticsNode = delegated { FocusableSemanticsNode() }
-
+ private val focusableSemanticsNode = delegate(FocusableSemanticsNode())
// (lpf) could we remove this if interactionsource is null?
- private val focusableInteractionNode = delegated { FocusableInteractionNode(interactionSource) }
- private val focusablePinnableContainer = delegated { FocusablePinnableContainerNode() }
- private val focusedBoundsNode = delegated { FocusedBoundsNode() }
+ private val focusableInteractionNode = delegate(FocusableInteractionNode(interactionSource))
+ private val focusablePinnableContainer = delegate(FocusablePinnableContainerNode())
+ private val focusedBoundsNode = delegate(FocusedBoundsNode())
// Focusables have a few different cases where they need to make sure they stay visible:
//
@@ -218,9 +217,9 @@
// See aosp/1964580.
private val bringIntoViewRequester = BringIntoViewRequester()
- private val bringIntoViewRequesterNode = delegated {
+ private val bringIntoViewRequesterNode = delegate(
BringIntoViewRequesterNode(bringIntoViewRequester)
- }
+ )
// TODO(levima) Remove this once delegation can propagate this events on its own
override fun onPlaced(coordinates: LayoutCoordinates) =
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 994d58c..01fdca8 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
@@ -38,7 +38,6 @@
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
-import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.layout.IntrinsicMeasurable
@@ -95,16 +94,16 @@
/**
* current scroll position value in pixels
*/
- var value: Int by mutableStateOf(initial, structuralEqualityPolicy())
+ var value: Int by mutableStateOf(initial)
private set
/**
* maximum bound for [value], or [Int.MAX_VALUE] if still unknown
*/
var maxValue: Int
- get() = _maxValueState.value
+ get() = _maxValueState.intValue
internal set(newMax) {
- _maxValueState.value = newMax
+ _maxValueState.intValue = newMax
if (value > newMax) {
value = newMax
}
@@ -113,7 +112,7 @@
/**
* Size of the viewport on the scrollable axis, or 0 if still unknown.
*/
- internal var viewportSize: Int by mutableStateOf(0, structuralEqualityPolicy())
+ internal var viewportSize: Int by mutableStateOf(0)
/**
* [InteractionSource] that will be used to dispatch drag events when this
@@ -124,7 +123,7 @@
internal val internalInteractionSource: MutableInteractionSource = MutableInteractionSource()
- private var _maxValueState = mutableStateOf(Int.MAX_VALUE, structuralEqualityPolicy())
+ private var _maxValueState = mutableStateOf(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/gestures/Draggable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
index 99bfce38..50f23b6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
@@ -26,36 +26,37 @@
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.internal.JvmDefaultWithCompatibility
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.State
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
+import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed
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.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import kotlin.coroutines.cancellation.CancellationException
import kotlin.math.sign
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
/**
* State of [draggable]. Allows for a granular control of how deltas are consumed by the user as
@@ -183,7 +184,7 @@
onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {},
onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = {},
reverseDirection: Boolean = false
-): Modifier = draggable(
+): Modifier = this then DraggableElement(
state = state,
orientation = orientation,
enabled = enabled,
@@ -195,18 +196,76 @@
canDrag = { true }
)
-internal fun Modifier.draggable(
- state: DraggableState,
- canDrag: (PointerInputChange) -> Boolean,
- orientation: Orientation,
- enabled: Boolean = true,
- interactionSource: MutableInteractionSource? = null,
- startDragImmediately: () -> Boolean,
- onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {},
- onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit = {},
- reverseDirection: Boolean = false
-): Modifier = composed(
- inspectorInfo = debugInspectorInfo {
+internal class DraggableElement(
+ private val state: DraggableState,
+ private val canDrag: (PointerInputChange) -> Boolean,
+ private val orientation: Orientation,
+ private val enabled: Boolean,
+ private val interactionSource: MutableInteractionSource?,
+ private val startDragImmediately: () -> Boolean,
+ private val onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit,
+ private val onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit,
+ private val reverseDirection: Boolean
+) : ModifierNodeElement<DraggableNode>() {
+ override fun create(): DraggableNode = DraggableNode(
+ state,
+ canDrag,
+ orientation,
+ enabled,
+ interactionSource,
+ startDragImmediately,
+ onDragStarted,
+ onDragStopped,
+ reverseDirection
+ )
+
+ override fun update(node: DraggableNode): DraggableNode = node.also {
+ it.update(
+ state,
+ canDrag,
+ orientation,
+ enabled,
+ interactionSource,
+ startDragImmediately,
+ onDragStarted,
+ onDragStopped,
+ reverseDirection
+ )
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as DraggableElement
+
+ if (state != other.state) return false
+ if (canDrag != other.canDrag) return false
+ if (orientation != other.orientation) return false
+ if (enabled != other.enabled) return false
+ if (interactionSource != other.interactionSource) return false
+ if (startDragImmediately != other.startDragImmediately) return false
+ if (onDragStarted != other.onDragStarted) return false
+ if (onDragStopped != other.onDragStopped) return false
+ if (reverseDirection != other.reverseDirection) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = state.hashCode()
+ result = 31 * result + canDrag.hashCode()
+ result = 31 * result + orientation.hashCode()
+ result = 31 * result + enabled.hashCode()
+ result = 31 * result + (interactionSource?.hashCode() ?: 0)
+ result = 31 * result + startDragImmediately.hashCode()
+ result = 31 * result + onDragStarted.hashCode()
+ result = 31 * result + onDragStopped.hashCode()
+ result = 31 * result + reverseDirection.hashCode()
+ return result
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
name = "draggable"
properties["canDrag"] = canDrag
properties["orientation"] = orientation
@@ -218,56 +277,33 @@
properties["onDragStopped"] = onDragStopped
properties["state"] = state
}
-) {
- val draggedInteraction = remember { mutableStateOf<DragInteraction.Start?>(null) }
- DisposableEffect(interactionSource) {
- onDispose {
- draggedInteraction.value?.let { interaction ->
- interactionSource?.tryEmit(DragInteraction.Cancel(interaction))
- draggedInteraction.value = null
- }
- }
- }
- val channel = remember { Channel<DragEvent>(capacity = Channel.UNLIMITED) }
- val startImmediatelyState = rememberUpdatedState(startDragImmediately)
- val canDragState = rememberUpdatedState(canDrag)
- val dragLogic by rememberUpdatedState(
- DragLogic(onDragStarted, onDragStopped, draggedInteraction, interactionSource)
- )
- LaunchedEffect(state) {
- while (isActive) {
- var event = channel.receive()
- if (event !is DragStarted) continue
- with(dragLogic) { processDragStart(event as DragStarted) }
- try {
- state.drag(MutatePriority.UserInput) {
- while (event !is DragStopped && event !is DragCancelled) {
- (event as? DragDelta)?.let { dragBy(it.delta.toFloat(orientation)) }
- event = channel.receive()
- }
- }
- with(dragLogic) {
- if (event is DragStopped) {
- processDragStop(event as DragStopped)
- } else if (event is DragCancelled) {
- processDragCancel()
- }
- }
- } catch (c: CancellationException) {
- with(dragLogic) { processDragCancel() }
- }
- }
- }
- Modifier.pointerInput(state, orientation, enabled, reverseDirection) {
- if (!enabled) return@pointerInput
+}
+
+internal class DraggableNode(
+ private var state: DraggableState,
+ private var canDrag: (PointerInputChange) -> Boolean,
+ private var orientation: Orientation,
+ private var enabled: Boolean,
+ private var interactionSource: MutableInteractionSource?,
+ private var startDragImmediately: () -> Boolean,
+ private var onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit,
+ private var onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit,
+ private var reverseDirection: Boolean
+) : DelegatingNode(), PointerInputModifierNode {
+ private val pointerInputNode = delegate(SuspendingPointerInputModifierNode {
+ // TODO: conditionally undelegate when aosp/2462416 lands?
+ if (!enabled) return@SuspendingPointerInputModifierNode
coroutineScope {
try {
awaitPointerEventScope {
while (isActive) {
val velocityTracker = VelocityTracker()
+ @Suppress("UnnecessaryLambdaCreation")
awaitDownAndSlop(
- canDragState,
- startImmediatelyState,
+ // Use lambdas here to make sure that if these properties are updated
+ // while we suspend, we point to the new reference when we invoke them.
+ { canDrag(it) },
+ { startDragImmediately() },
velocityTracker,
orientation
)?.let {
@@ -303,20 +339,148 @@
}
}
}
+ })
+
+ private val channel = Channel<DragEvent>(capacity = Channel.UNLIMITED)
+ private var observeChannelJob: Job? = null
+ private var dragInteraction: DragInteraction.Start? = null
+
+ override fun onAttach() {
+ observeChannel()
+ }
+
+ override fun onDetach() {
+ disposeInteractionSource()
+ }
+
+ override fun onPointerEvent(
+ pointerEvent: PointerEvent,
+ pass: PointerEventPass,
+ bounds: IntSize
+ ) {
+ pointerInputNode.onPointerEvent(pointerEvent, pass, bounds)
+ }
+
+ override fun onCancelPointerInput() {
+ pointerInputNode.onCancelPointerInput()
+ }
+
+ fun update(
+ state: DraggableState,
+ canDrag: (PointerInputChange) -> Boolean,
+ orientation: Orientation,
+ enabled: Boolean,
+ interactionSource: MutableInteractionSource?,
+ startDragImmediately: () -> Boolean,
+ onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit,
+ onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit,
+ reverseDirection: Boolean
+ ) {
+ var resetPointerInputHandling = false
+ if (this.state != state) {
+ // Reset observation when the state changes
+ observeChannel()
+ this.state = state
+ }
+ this.canDrag = canDrag
+ if (this.orientation != orientation) {
+ this.orientation = orientation
+ resetPointerInputHandling = true
+ }
+ if (this.enabled != enabled) {
+ this.enabled = enabled
+ if (!enabled) {
+ disposeInteractionSource()
+ }
+ resetPointerInputHandling = true
+ }
+ if (this.interactionSource != interactionSource) {
+ disposeInteractionSource()
+ this.interactionSource = interactionSource
+ }
+ this.startDragImmediately = startDragImmediately
+ this.onDragStarted = onDragStarted
+ this.onDragStopped = onDragStopped
+ if (this.reverseDirection != reverseDirection) {
+ this.reverseDirection = reverseDirection
+ resetPointerInputHandling = true
+ }
+ if (resetPointerInputHandling) {
+ pointerInputNode.resetPointerInputHandler()
+ }
+ }
+
+ private fun observeChannel() {
+ observeChannelJob?.cancel()
+ observeChannelJob = coroutineScope.launch {
+ while (isActive) {
+ var event = channel.receive()
+ if (event !is DragStarted) continue
+ processDragStart(event)
+ try {
+ state.drag(MutatePriority.UserInput) {
+ while (event !is DragStopped && event !is DragCancelled) {
+ (event as? DragDelta)?.let { dragBy(it.delta.toFloat(orientation)) }
+ event = channel.receive()
+ }
+ }
+ if (event is DragStopped) {
+ processDragStop(event as DragStopped)
+ } else if (event is DragCancelled) {
+ processDragCancel()
+ }
+ } catch (c: CancellationException) {
+ processDragCancel()
+ }
+ }
+ }
+ }
+
+ private suspend fun CoroutineScope.processDragStart(event: DragStarted) {
+ dragInteraction?.let { oldInteraction ->
+ interactionSource?.emit(DragInteraction.Cancel(oldInteraction))
+ }
+ val interaction = DragInteraction.Start()
+ interactionSource?.emit(interaction)
+ dragInteraction = interaction
+ onDragStarted.invoke(this, event.startPoint)
+ }
+
+ private suspend fun CoroutineScope.processDragStop(event: DragStopped) {
+ dragInteraction?.let { interaction ->
+ interactionSource?.emit(DragInteraction.Stop(interaction))
+ dragInteraction = null
+ }
+ onDragStopped.invoke(this, event.velocity)
+ }
+
+ private suspend fun CoroutineScope.processDragCancel() {
+ dragInteraction?.let { interaction ->
+ interactionSource?.emit(DragInteraction.Cancel(interaction))
+ dragInteraction = null
+ }
+ onDragStopped.invoke(this, Velocity.Zero)
+ }
+
+ private fun disposeInteractionSource() {
+ dragInteraction?.let { interaction ->
+ interactionSource?.tryEmit(DragInteraction.Cancel(interaction))
+ dragInteraction = null
+ }
}
}
private suspend fun AwaitPointerEventScope.awaitDownAndSlop(
- canDrag: State<(PointerInputChange) -> Boolean>,
- startDragImmediately: State<() -> Boolean>,
+ canDrag: (PointerInputChange) -> Boolean,
+ startDragImmediately: () -> Boolean,
velocityTracker: VelocityTracker,
orientation: Orientation
): Pair<PointerInputChange, Offset>? {
val initialDown =
awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Initial)
- return if (!canDrag.value.invoke(initialDown)) {
+ return if (!canDrag(initialDown)) {
null
- } else if (startDragImmediately.value.invoke()) {
+ } else if (startDragImmediately()) {
initialDown.consume()
velocityTracker.addPointerInputChange(initialDown)
// since we start immediately we don't wait for slop and the initial delta is 0
@@ -392,40 +556,6 @@
)?.let(onDrag) != null
}
-private class DragLogic(
- val onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit,
- val onDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit,
- val dragStartInteraction: MutableState<DragInteraction.Start?>,
- val interactionSource: MutableInteractionSource?
-) {
-
- suspend fun CoroutineScope.processDragStart(event: DragStarted) {
- dragStartInteraction.value?.let { oldInteraction ->
- interactionSource?.emit(DragInteraction.Cancel(oldInteraction))
- }
- val interaction = DragInteraction.Start()
- interactionSource?.emit(interaction)
- dragStartInteraction.value = interaction
- onDragStarted.invoke(this, event.startPoint)
- }
-
- suspend fun CoroutineScope.processDragStop(event: DragStopped) {
- dragStartInteraction.value?.let { interaction ->
- interactionSource?.emit(DragInteraction.Stop(interaction))
- dragStartInteraction.value = null
- }
- onDragStopped.invoke(this, event.velocity)
- }
-
- suspend fun CoroutineScope.processDragCancel() {
- dragStartInteraction.value?.let { interaction ->
- interactionSource?.emit(DragInteraction.Cancel(interaction))
- dragStartInteraction.value = null
- }
- onDragStopped.invoke(this, Velocity.Zero)
- }
-}
-
private class DefaultDraggableState(val onDelta: (Float) -> Unit) : DraggableState {
private val dragScope: DragScope = object : DragScope {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index ccc1466..0c8fb8a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -64,6 +64,7 @@
import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastForEach
import kotlin.math.abs
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -271,24 +272,29 @@
val draggableState = remember { ScrollDraggableState(scrollLogic) }
val scrollConfig = platformScrollConfig()
- return draggable(
- draggableState,
- orientation = orientation,
- enabled = enabled,
- interactionSource = interactionSource,
- reverseDirection = false,
- startDragImmediately = { scrollLogic.value.shouldScrollImmediately() },
- onDragStopped = { velocity ->
- nestedScrollDispatcher.value.coroutineScope.launch {
- scrollLogic.value.onDragStopped(velocity)
- }
- },
- canDrag = { down -> down.type != PointerType.Mouse }
- )
+ return this
+ .then(DraggableElement(
+ state = draggableState,
+ orientation = orientation,
+ enabled = enabled,
+ interactionSource = interactionSource,
+ reverseDirection = false,
+ startDragImmediately = { scrollLogic.value.shouldScrollImmediately() },
+ onDragStarted = NoOpOnDragStarted,
+ onDragStopped = { velocity ->
+ nestedScrollDispatcher.value.coroutineScope.launch {
+ scrollLogic.value.onDragStopped(velocity)
+ }
+ },
+ canDrag = { down -> down.type != PointerType.Mouse }
+ ))
.then(MouseWheelScrollElement(scrollLogic, scrollConfig))
.nestedScroll(nestedScrollConnection, nestedScrollDispatcher.value)
}
+// {} isn't being memoized for us, so extract this to make sure we compare equally on recomposition.
+private val NoOpOnDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {}
+
private class MouseWheelScrollElement(
val scrollingLogicState: State<ScrollingLogic>,
val mouseWheelScrollConfig: ScrollConfig
@@ -326,7 +332,7 @@
var mouseWheelScrollConfig: ScrollConfig
) : DelegatingNode(), PointerInputModifierNode {
- private val pointerInputNode = SuspendingPointerInputModifierNode {
+ private val pointerInputNode = delegate(SuspendingPointerInputModifierNode {
awaitPointerEventScope {
while (true) {
val event = awaitScrollEvent()
@@ -344,10 +350,7 @@
}
}
}
- }
- // TODO: remove `.node` after aosp/2462416 lands and merge everything into one delegated
- // block
- .also { delegated { it.node } }
+ })
override fun onPointerEvent(
pointerEvent: PointerEvent,
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 ff2dc1a..0734088 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
@@ -19,7 +19,6 @@
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.lazy.layout.LazyLayoutAnimateItemModifierNode
-import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
@@ -38,12 +37,12 @@
internal class LazyItemScopeImpl : LazyItemScope {
- private var maxWidthState: MutableState<Int> = mutableStateOf(Int.MAX_VALUE)
- private var maxHeightState: MutableState<Int> = mutableStateOf(Int.MAX_VALUE)
+ private var maxWidthState = mutableStateOf(Int.MAX_VALUE)
+ private var maxHeightState = mutableStateOf(Int.MAX_VALUE)
fun setMaxSize(width: Int, height: Int) {
- maxWidthState.value = width
- maxHeightState.value = height
+ maxWidthState.intValue = width
+ maxHeightState.intValue = height
}
override fun Modifier.fillParentMaxSize(fraction: Float) = then(
@@ -185,7 +184,7 @@
animationSpec: FiniteAnimationSpec<IntOffset>
) : DelegatingNode(), ParentDataModifierNode {
- val delegatingNode = delegated { LazyLayoutAnimateItemModifierNode(animationSpec) }
+ val delegatingNode = delegate(LazyLayoutAnimateItemModifierNode(animationSpec))
override fun Density.modifyParentData(parentData: Any?): Any = delegatingNode
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
index e606387..e761a7d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
@@ -29,7 +29,7 @@
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.lazy.layout.LazyLayout
import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
-import androidx.compose.foundation.lazy.layout.findIndexByKey
+import androidx.compose.foundation.lazy.layout.calculateLazyLayoutPinnedIndices
import androidx.compose.foundation.lazy.layout.lazyLayoutSemantics
import androidx.compose.foundation.overscroll
import androidx.compose.runtime.Composable
@@ -44,8 +44,6 @@
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.constrainWidth
import androidx.compose.ui.unit.offset
-import androidx.compose.ui.util.fastForEach
-import kotlin.math.min
@OptIn(ExperimentalFoundationApi::class)
@Composable
@@ -80,12 +78,10 @@
val itemProvider = rememberLazyListItemProvider(state, content)
val semanticState = rememberLazyListSemanticState(state, isVertical)
- val beyondBoundsInfo = remember { LazyListBeyondBoundsInfo() }
val measurePolicy = rememberLazyListMeasurePolicy(
itemProvider,
state,
- beyondBoundsInfo,
contentPadding,
reverseLayout,
isVertical,
@@ -114,7 +110,6 @@
.clipScrollableContainer(orientation)
.lazyListBeyondBoundsModifier(
state,
- beyondBoundsInfo,
beyondBoundsItemCount,
reverseLayout,
orientation
@@ -158,8 +153,6 @@
itemProvider: LazyListItemProvider,
/** The state of the list. */
state: LazyListState,
- /** Keeps track of the number of items we measure and place that are beyond visible bounds. */
- beyondBoundsInfo: LazyListBeyondBoundsInfo,
/** The inner padding to be added for the whole content(nor for each individual item) */
contentPadding: PaddingValues,
/** reverse the direction of scrolling and layout */
@@ -178,7 +171,6 @@
verticalArrangement: Arrangement.Vertical? = null,
) = remember<LazyLayoutMeasureScope.(Constraints) -> MeasureResult>(
state,
- beyondBoundsInfo,
contentPadding,
reverseLayout,
isVertical,
@@ -295,26 +287,10 @@
firstVisibleScrollOffset = state.firstVisibleItemScrollOffset
}
- val pinnedItems = if (!beyondBoundsInfo.hasIntervals() && state.pinnedItems.isEmpty()) {
- emptyList()
- } else {
- val pinnedItems = mutableListOf<Int>()
- val beyondBoundsRange = if (beyondBoundsInfo.hasIntervals()) {
- beyondBoundsInfo.start..min(beyondBoundsInfo.end, itemsCount - 1)
- } else {
- IntRange.EMPTY
- }
- state.pinnedItems.fastForEach {
- val index = itemProvider.findIndexByKey(it.key, it.index)
- if (index in beyondBoundsRange) return@fastForEach
- if (index !in 0 until itemsCount) return@fastForEach
- pinnedItems.add(index)
- }
- for (i in beyondBoundsRange) {
- pinnedItems.add(i)
- }
- pinnedItems
- }
+ val pinnedItems = itemProvider.calculateLazyLayoutPinnedIndices(
+ pinnedItemList = state.pinnedItems,
+ beyondBoundsInfo = state.beyondBoundsInfo
+ )
measureLazyList(
itemsCount = itemsCount,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt
index c048b9b..24e8bd2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt
@@ -50,8 +50,10 @@
}
override fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
- val visibleItems = state.layoutInfo.visibleItemsInfo
- val averageSize = visibleItems.fastSumBy { it.size } / visibleItems.size
+ val layoutInfo = state.layoutInfo
+ val visibleItems = layoutInfo.visibleItemsInfo
+ val averageSize =
+ visibleItems.fastSumBy { it.size } / visibleItems.size + layoutInfo.mainAxisItemSpacing
val indexesDiff = index - firstVisibleItemIndex
var coercedOffset = minOf(abs(targetScrollOffset), averageSize)
if (targetScrollOffset < 0) coercedOffset *= -1
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsModifier.kt
index c52f75b..6def944 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsModifier.kt
@@ -17,7 +17,7 @@
package androidx.compose.foundation.lazy
import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.lazy.layout.BeyondBoundsState
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsState
import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@@ -32,7 +32,6 @@
@Composable
internal fun Modifier.lazyListBeyondBoundsModifier(
state: LazyListState,
- beyondBoundsInfo: LazyListBeyondBoundsInfo,
beyondBoundsItemCount: Int,
reverseLayout: Boolean,
orientation: Orientation
@@ -41,6 +40,7 @@
val beyondBoundsState = remember(state, beyondBoundsItemCount) {
LazyListBeyondBoundsState(state, beyondBoundsItemCount)
}
+ val beyondBoundsInfo = state.beyondBoundsInfo
return this then remember(
beyondBoundsState,
beyondBoundsInfo,
@@ -61,7 +61,7 @@
internal class LazyListBeyondBoundsState(
val state: LazyListState,
val beyondBoundsItemCount: Int
-) : BeyondBoundsState {
+) : LazyLayoutBeyondBoundsState {
override fun remeasure() {
state.remeasurement?.forceRemeasure()
@@ -71,9 +71,9 @@
get() = state.layoutInfo.totalItemsCount
override val hasVisibleItems: Boolean
get() = state.layoutInfo.visibleItemsInfo.isNotEmpty()
- override val firstVisibleIndex: Int
+ override val firstPlacedIndex: Int
get() = maxOf(0, state.firstVisibleItemIndex - beyondBoundsItemCount)
- override val lastVisibleIndex: Int
+ override val lastPlacedIndex: Int
get() = minOf(
itemCount - 1,
state.layoutInfo.visibleItemsInfo.last().index + beyondBoundsItemCount
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
index 6e1f924..15ebac4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
@@ -102,11 +102,9 @@
)
}
} else {
- repeat(item.placeablesCount) { placeableIndex ->
- item.getParentData(placeableIndex).node?.apply {
- if (rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
- rawOffset += scrollOffset
- }
+ item.forEachNode { _, node ->
+ if (node.rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
+ node.rawOffset += scrollOffset
}
}
startAnimationsIfNeeded(item)
@@ -117,19 +115,19 @@
}
}
- var currentMainAxisOffset = 0
+ var accumulatedOffset = 0
movingInFromStartBound.sortByDescending { previousKeyToIndexMap[it.key] }
movingInFromStartBound.fastForEach { item ->
- val mainAxisOffset = 0 - currentMainAxisOffset - item.size
- currentMainAxisOffset += item.size
+ accumulatedOffset += item.size
+ val mainAxisOffset = 0 - accumulatedOffset
initializeNode(item, mainAxisOffset)
startAnimationsIfNeeded(item)
}
- currentMainAxisOffset = 0
+ accumulatedOffset = 0
movingInFromEndBound.sortBy { previousKeyToIndexMap[it.key] }
movingInFromEndBound.fastForEach { item ->
- val mainAxisOffset = mainAxisLayoutSize + currentMainAxisOffset
- currentMainAxisOffset += item.size
+ val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
+ accumulatedOffset += item.size
initializeNode(item, mainAxisOffset)
startAnimationsIfNeeded(item)
}
@@ -163,21 +161,21 @@
}
}
- currentMainAxisOffset = 0
+ accumulatedOffset = 0
movingAwayToStartBound.sortByDescending { keyToIndexMap[it.key] }
movingAwayToStartBound.fastForEach { item ->
- val mainAxisOffset = 0 - currentMainAxisOffset - item.size
- currentMainAxisOffset += item.size
+ accumulatedOffset += item.size
+ val mainAxisOffset = 0 - accumulatedOffset
val positionedItem = item.position(mainAxisOffset, layoutWidth, layoutHeight)
positionedItems.add(positionedItem)
startAnimationsIfNeeded(positionedItem)
}
- currentMainAxisOffset = 0
+ accumulatedOffset = 0
movingAwayToEndBound.sortBy { keyToIndexMap[it.key] }
movingAwayToEndBound.fastForEach { item ->
- val mainAxisOffset = mainAxisLayoutSize + currentMainAxisOffset
- currentMainAxisOffset += item.size
+ val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
+ accumulatedOffset += item.size
val positionedItem = item.position(mainAxisOffset, layoutWidth, layoutHeight)
positionedItems.add(positionedItem)
@@ -214,29 +212,23 @@
}
// initialize offsets
- repeat(item.placeablesCount) { placeableIndex ->
- val node = item.getParentData(placeableIndex).node
- if (node != null) {
- val diffToFirstPlaceableOffset =
- item.getOffset(placeableIndex) - firstPlaceableOffset
- node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
- }
+ item.forEachNode { placeableIndex, node ->
+ val diffToFirstPlaceableOffset =
+ item.getOffset(placeableIndex) - firstPlaceableOffset
+ node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
}
}
private fun startAnimationsIfNeeded(item: LazyListPositionedItem) {
- repeat(item.placeablesCount) { placeableIndex ->
- val node = item.getParentData(placeableIndex).node
- if (node != null) {
- val newTarget = item.getOffset(placeableIndex)
- val currentTarget = node.rawOffset
- if (currentTarget == LazyLayoutAnimateItemModifierNode.NotInitialized) {
- node.rawOffset = newTarget
- } else if (currentTarget != newTarget) {
- node.rawOffset = newTarget
- node.animatePlacementDelta(newTarget - currentTarget)
- }
+ item.forEachNode { placeableIndex, node ->
+ val newTarget = item.getOffset(placeableIndex)
+ val currentTarget = node.rawOffset
+ if (currentTarget != LazyLayoutAnimateItemModifierNode.NotInitialized &&
+ currentTarget != newTarget
+ ) {
+ node.animatePlacementDelta(newTarget - currentTarget)
}
+ node.rawOffset = newTarget
}
}
@@ -244,11 +236,15 @@
private val LazyListPositionedItem.hasAnimations: Boolean
get() {
- repeat(placeablesCount) { index ->
- if (getParentData(index).node != null) {
- return true
- }
- }
+ forEachNode { _, _ -> return true }
return false
}
+
+ private inline fun LazyListPositionedItem.forEachNode(
+ block: (placeableIndex: Int, node: LazyLayoutAnimateItemModifierNode) -> Unit
+ ) {
+ repeat(placeablesCount) { index ->
+ getParentData(index).node?.let { block(index, it) }
+ }
+ }
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
index f3d19f1b..760f9fa 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
@@ -23,6 +23,7 @@
import androidx.compose.foundation.gestures.ScrollableState
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
import androidx.compose.foundation.lazy.layout.animateScrollToItem
@@ -216,6 +217,8 @@
internal val placementAnimator = LazyListItemPlacementAnimator()
+ internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
+
/**
* Constraints passed to the prefetcher for premeasuring the prefetched items.
*/
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
index ec71a7b..b60f6ed 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
@@ -29,6 +29,7 @@
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.lazy.layout.LazyLayout
import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.foundation.lazy.layout.calculateLazyLayoutPinnedIndices
import androidx.compose.foundation.lazy.layout.lazyLayoutSemantics
import androidx.compose.foundation.overscroll
import androidx.compose.runtime.Composable
@@ -86,7 +87,7 @@
reverseLayout,
isVertical,
horizontalArrangement,
- verticalArrangement
+ verticalArrangement,
)
state.isVertical = isVertical
@@ -106,6 +107,11 @@
reverseScrolling = reverseLayout
)
.clipScrollableContainer(orientation)
+ .lazyGridBeyondBoundsModifier(
+ state,
+ reverseLayout,
+ orientation
+ )
.overscroll(overscrollEffect)
.scrollable(
orientation = orientation,
@@ -170,7 +176,7 @@
reverseLayout,
isVertical,
horizontalArrangement,
- verticalArrangement
+ verticalArrangement,
) {
{ containerConstraints ->
checkScrollableContainerConstraints(
@@ -314,9 +320,14 @@
firstVisibleLineScrollOffset = 0
}
}
+
+ val pinnedItems = itemProvider.calculateLazyLayoutPinnedIndices(
+ state.pinnedItems,
+ state.beyondBoundsInfo
+ )
+
measureLazyGrid(
itemsCount = itemsCount,
- itemProvider = itemProvider,
measuredLineProvider = measuredLineProvider,
measuredItemProvider = measuredItemProvider,
mainAxisAvailableSize = mainAxisAvailableSize,
@@ -334,7 +345,7 @@
density = this,
placementAnimator = state.placementAnimator,
spanLayoutProvider = spanLayoutProvider,
- pinnedItems = state.pinnedItems,
+ pinnedItems = pinnedItems,
layout = { width, height, placement ->
layout(
containerConstraints.constrainWidth(width + totalHorizontalPadding),
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt
index 174036d..b29a04f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt
@@ -54,10 +54,9 @@
}
override fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
- val visibleItems = state.layoutInfo.visibleItemsInfo
val slotsPerLine = state.slotsPerLine
val averageLineMainAxisSize = calculateLineAverageMainAxisSize(
- visibleItems,
+ state.layoutInfo,
state.isVertical
)
val before = index < firstVisibleItemIndex
@@ -74,9 +73,10 @@
override val numOfItemsForTeleport: Int get() = 100 * state.slotsPerLine
private fun calculateLineAverageMainAxisSize(
- visibleItems: List<LazyGridItemInfo>,
+ layoutInfo: LazyGridLayoutInfo,
isVertical: Boolean
): Int {
+ val visibleItems = layoutInfo.visibleItemsInfo
val lineOf: (Int) -> Int = {
if (isVertical) visibleItems[it].row else visibleItems[it].column
}
@@ -113,7 +113,7 @@
lineStartIndex = lineEndIndex
}
- return totalLinesMainAxisSize / linesCount
+ return totalLinesMainAxisSize / linesCount + layoutInfo.mainAxisItemSpacing
}
override suspend fun scroll(block: suspend ScrollScope.() -> Unit) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsModifier.kt
new file mode 100644
index 0000000..816d08d
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridBeyondBoundsModifier.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.lazy.grid
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalLayoutDirection
+
+/**
+ * This modifier is used to measure and place additional items when the lazyList receives a
+ * request to layout items beyond the visible bounds.
+ */
+@Suppress("ComposableModifierFactory")
+@Composable
+internal fun Modifier.lazyGridBeyondBoundsModifier(
+ state: LazyGridState,
+ reverseLayout: Boolean,
+ orientation: Orientation
+): Modifier {
+ val layoutDirection = LocalLayoutDirection.current
+ val beyondBoundsState = remember(state) {
+ LazyGridBeyondBoundsState(state)
+ }
+ return this then remember(
+ state,
+ beyondBoundsState,
+ reverseLayout,
+ layoutDirection,
+ orientation
+ ) {
+ LazyLayoutBeyondBoundsModifierLocal(
+ beyondBoundsState,
+ state.beyondBoundsInfo,
+ reverseLayout,
+ layoutDirection,
+ orientation
+ )
+ }
+}
+
+internal class LazyGridBeyondBoundsState(
+ val state: LazyGridState,
+) : LazyLayoutBeyondBoundsState {
+
+ override fun remeasure() {
+ state.remeasurement?.forceRemeasure()
+ }
+
+ override val itemCount: Int
+ get() = state.layoutInfo.totalItemsCount
+ override val hasVisibleItems: Boolean
+ get() = state.layoutInfo.visibleItemsInfo.isNotEmpty()
+ override val firstPlacedIndex: Int
+ get() = state.firstVisibleItemIndex
+ override val lastPlacedIndex: Int
+ get() = state.layoutInfo.visibleItemsInfo.last().index
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridDsl.kt
index 5492a12..f54d52e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridDsl.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.lazy.grid
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Arrangement
@@ -327,7 +326,6 @@
* If the screen is 88.dp wide tne there will be 4 columns 20.dp each with remaining 8.dp
* distributed through [Arrangement.Horizontal].
*/
- @ExperimentalFoundationApi
class FixedSize(private val size: Dp) : GridCells {
init {
require(size > 0.dp) { "Provided size $size should be larger than zero." }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
index 5bdc836..2f36dec 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
@@ -107,11 +107,9 @@
)
}
} else {
- repeat(item.placeablesCount) { placeableIndex ->
- item.getParentData(placeableIndex).node?.apply {
- if (rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
- rawOffset += scrollOffset
- }
+ item.forEachNode {
+ if (it.rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
+ it.rawOffset += scrollOffset
}
}
itemInfo.crossAxisSize = item.getCrossAxisSize()
@@ -124,7 +122,7 @@
}
}
- var currentMainAxisOffset = 0
+ var accumulatedOffset = 0
var previousLine = -1
var previousLineMainAxisSize = 0
movingInFromStartBound.sortByDescending { previousKeyToIndexMap[it.key] }
@@ -133,15 +131,15 @@
if (line != -1 && line == previousLine) {
previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.getMainAxisSize())
} else {
- currentMainAxisOffset += previousLineMainAxisSize
+ accumulatedOffset += previousLineMainAxisSize
previousLineMainAxisSize = item.getMainAxisSize()
previousLine = line
}
- val mainAxisOffset = 0 - currentMainAxisOffset - item.getMainAxisSize()
+ val mainAxisOffset = 0 - accumulatedOffset - item.getMainAxisSize()
initializeNode(item, mainAxisOffset)
startAnimationsIfNeeded(item)
}
- currentMainAxisOffset = 0
+ accumulatedOffset = 0
previousLine = -1
previousLineMainAxisSize = 0
movingInFromEndBound.sortBy { previousKeyToIndexMap[it.key] }
@@ -150,11 +148,11 @@
if (line != -1 && line == previousLine) {
previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.getMainAxisSize())
} else {
- currentMainAxisOffset += previousLineMainAxisSize
+ accumulatedOffset += previousLineMainAxisSize
previousLineMainAxisSize = item.getMainAxisSize()
previousLine = line
}
- val mainAxisOffset = mainAxisLayoutSize + currentMainAxisOffset
+ val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
initializeNode(item, mainAxisOffset)
startAnimationsIfNeeded(item)
}
@@ -196,7 +194,7 @@
}
}
- currentMainAxisOffset = 0
+ accumulatedOffset = 0
previousLine = -1
previousLineMainAxisSize = 0
movingAwayToStartBound.sortByDescending { keyToIndexMap[it.key] }
@@ -205,11 +203,11 @@
if (line != -1 && line == previousLine) {
previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.mainAxisSize)
} else {
- currentMainAxisOffset += previousLineMainAxisSize
+ accumulatedOffset += previousLineMainAxisSize
previousLineMainAxisSize = item.mainAxisSize
previousLine = line
}
- val mainAxisOffset = 0 - currentMainAxisOffset - item.mainAxisSize
+ val mainAxisOffset = 0 - accumulatedOffset - item.mainAxisSize
val itemInfo = keyToItemInfoMap.getValue(item.key)
@@ -224,7 +222,7 @@
positionedItems.add(positionedItem)
startAnimationsIfNeeded(positionedItem)
}
- currentMainAxisOffset = 0
+ accumulatedOffset = 0
previousLine = -1
previousLineMainAxisSize = 0
movingAwayToEndBound.sortBy { keyToIndexMap[it.key] }
@@ -233,11 +231,11 @@
if (line != -1 && line == previousLine) {
previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.mainAxisSize)
} else {
- currentMainAxisOffset += previousLineMainAxisSize
+ accumulatedOffset += previousLineMainAxisSize
previousLineMainAxisSize = item.mainAxisSize
previousLine = line
}
- val mainAxisOffset = mainAxisLayoutSize + currentMainAxisOffset
+ val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
val itemInfo = keyToItemInfoMap.getValue(item.key)
val positionedItem = item.position(
@@ -283,29 +281,23 @@
}
// initialize offsets
- repeat(item.placeablesCount) { placeableIndex ->
- val node = item.getParentData(placeableIndex).node
- if (node != null) {
- val diffToFirstPlaceableOffset =
- item.offset - firstPlaceableOffset
- node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
- }
+ item.forEachNode { node ->
+ val diffToFirstPlaceableOffset =
+ item.offset - firstPlaceableOffset
+ node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
}
}
private fun startAnimationsIfNeeded(item: LazyGridPositionedItem) {
- repeat(item.placeablesCount) { placeableIndex ->
- val node = item.getParentData(placeableIndex).node
- if (node != null) {
- val newTarget = item.offset
- val currentTarget = node.rawOffset
- if (currentTarget == LazyLayoutAnimateItemModifierNode.NotInitialized) {
- node.rawOffset = item.offset
- } else if (currentTarget != newTarget) {
- node.rawOffset = newTarget
- node.animatePlacementDelta(item.offset - currentTarget)
- }
+ item.forEachNode { node ->
+ val newTarget = item.offset
+ val currentTarget = node.rawOffset
+ if (currentTarget != LazyLayoutAnimateItemModifierNode.NotInitialized &&
+ currentTarget != newTarget
+ ) {
+ node.animatePlacementDelta(newTarget - currentTarget)
}
+ node.rawOffset = newTarget
}
}
@@ -313,13 +305,17 @@
private val LazyGridPositionedItem.hasAnimations: Boolean
get() {
- repeat(placeablesCount) { index ->
- if (getParentData(index).node != null) {
- return true
- }
- }
+ forEachNode { return true }
return false
}
+
+ private inline fun LazyGridPositionedItem.forEachNode(
+ block: (LazyLayoutAnimateItemModifierNode) -> Unit
+ ) {
+ repeat(placeablesCount) { index ->
+ getParentData(index).node?.let(block)
+ }
+ }
}
private class ItemInfo(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScope.kt
index 794b9216..208ee3f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScope.kt
@@ -36,7 +36,7 @@
*
* When you provide a key via [LazyGridScope.item]/[LazyGridScope.items] this modifier will
* enable item reordering animations. Aside from item reordering all other position changes
- * caused by events like arrangement or alignment changes will also be animated.
+ * caused by events like arrangement changes will also be animated.
*
* @param animationSpec a finite animation that will be used to animate the item placement.
*/
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScopeImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScopeImpl.kt
index f8dda36..7e28d2f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScopeImpl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemScopeImpl.kt
@@ -64,7 +64,7 @@
animationSpec: FiniteAnimationSpec<IntOffset>
) : DelegatingNode(), ParentDataModifierNode {
- val delegatingNode = delegated { LazyLayoutAnimateItemModifierNode(animationSpec) }
+ val delegatingNode = delegate(LazyLayoutAnimateItemModifierNode(animationSpec))
override fun Density.modifyParentData(parentData: Any?): Any = delegatingNode
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
index b22522a..a2a11fe 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
@@ -20,8 +20,6 @@
import androidx.compose.foundation.fastFilter
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
-import androidx.compose.foundation.lazy.layout.findIndexByKey
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.unit.Constraints
@@ -43,7 +41,6 @@
@OptIn(ExperimentalFoundationApi::class)
internal fun measureLazyGrid(
itemsCount: Int,
- itemProvider: LazyGridItemProvider,
measuredLineProvider: LazyGridMeasuredLineProvider,
measuredItemProvider: LazyGridMeasuredItemProvider,
mainAxisAvailableSize: Int,
@@ -61,7 +58,7 @@
density: Density,
placementAnimator: LazyGridItemPlacementAnimator,
spanLayoutProvider: LazyGridSpanLayoutProvider,
- pinnedItems: LazyLayoutPinnedItemList,
+ pinnedItems: List<Int>,
layout: (Int, Int, Placeable.PlacementScope.() -> Unit) -> MeasureResult
): LazyGridMeasureResult {
require(beforeContentPadding >= 0)
@@ -213,7 +210,6 @@
val extraItemsBefore = calculateExtraItems(
pinnedItems,
measuredItemProvider,
- itemProvider,
itemConstraints = { measuredLineProvider.itemConstraints(it) },
filter = { it in 0 until firstItemIndex }
)
@@ -221,7 +217,6 @@
val extraItemsAfter = calculateExtraItems(
pinnedItems,
measuredItemProvider,
- itemProvider,
itemConstraints = { measuredLineProvider.itemConstraints(it) },
filter = { it in (lastItemIndex + 1) until itemsCount }
)
@@ -307,16 +302,14 @@
@ExperimentalFoundationApi
private inline fun calculateExtraItems(
- pinnedItems: LazyLayoutPinnedItemList,
+ pinnedItems: List<Int>,
measuredItemProvider: LazyGridMeasuredItemProvider,
- itemProvider: LazyGridItemProvider,
itemConstraints: (ItemIndex) -> Constraints,
filter: (Int) -> Boolean
): List<LazyGridMeasuredItem> {
var items: MutableList<LazyGridMeasuredItem>? = null
- pinnedItems.fastForEach { item ->
- val index = itemProvider.findIndexByKey(item.key, item.index)
+ pinnedItems.fastForEach { index ->
if (filter(index)) {
val itemIndex = ItemIndex(index)
val constraints = itemConstraints(itemIndex)
@@ -430,6 +423,6 @@
crossAxisOffset = 0,
layoutWidth = layoutWidth,
layoutHeight = layoutHeight,
- row = 0,
- column = 0
+ row = -1,
+ column = -1
)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
index 87934a4..2127ee7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
@@ -24,6 +24,7 @@
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.lazy.AwaitFirstLayoutModifier
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
import androidx.compose.foundation.lazy.layout.animateScrollToItem
@@ -200,7 +201,7 @@
* The [Remeasurement] object associated with our layout. It allows us to remeasure
* synchronously during scroll.
*/
- private var remeasurement: Remeasurement? by mutableStateOf(null)
+ internal var remeasurement: Remeasurement? by mutableStateOf(null)
/**
* The modifier which provides [remeasurement].
@@ -225,6 +226,8 @@
internal val placementAnimator = LazyGridItemPlacementAnimator()
+ internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
+
private val animateScrollScope = LazyGridAnimateScrollScope(this)
/**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyAnimateScroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyAnimateScroll.kt
index 3548bcd..78fd421 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyAnimateScroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyAnimateScroll.kt
@@ -208,7 +208,11 @@
// We don't throw ItemFoundInScroll when we snap, because once we've snapped to
// the final position, there's no need to animate to it.
if (isOvershot()) {
- debugLog { "Overshot" }
+ debugLog {
+ "Overshot, " +
+ "item $firstVisibleItemIndex at $firstVisibleItemScrollOffset, " +
+ "target is $scrollOffset"
+ }
snapToItem(index = index, scrollOffset = scrollOffset)
loop = false
cancelAnimation()
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsInfo.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsInfo.kt
similarity index 79%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsInfo.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsInfo.kt
index ee872fd..34d2918 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListBeyondBoundsInfo.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsInfo.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 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,24 +14,25 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.layout
import androidx.compose.runtime.collection.mutableVectorOf
/**
* This data structure is used to save information about the number of "beyond bounds items"
- * that we want to compose. These items are not within the visible bounds of the lazylist,
+ * that we want to compose. These items are not within the visible bounds of the lazy layout,
* but we compose them because they are explicitly requested through the
* [beyond bounds layout API][androidx.compose.ui.layout.BeyondBoundsLayout].
*
- * When the LazyList receives a
- * [searchBeyondBounds][androidx.compose.ui.layout.BeyondBoundsLayout.searchBeyondBounds] request to
- * layout items beyond visible bounds, it creates an instance of [LazyListBeyondBoundsInfo] by using
- * the [addInterval] function. This returns the interval of items that are currently composed,
- * and we can edit this interval to control the number of beyond bounds items.
+ * When the lazy layout receives a
+ * [layout][androidx.compose.ui.layout.BeyondBoundsLayout.layout] request to layout items beyond
+ * visible bounds, it creates an instance of [LazyLayoutBeyondBoundsInfo.Interval] by using the
+ * [addInterval] function.
+ * This returns the interval of items that are currently composed, and we can request other
+ * intervals to control the number of beyond bounds items.
*
- * There can be multiple intervals created at the same time, and LazyList merges all the
- * intervals to calculate the effective beyond bounds items.
+ * There can be multiple intervals created at the same time, and [LazyLayoutBeyondBoundsInfo] merges
+ * all the intervals to calculate the effective beyond bounds items.
*
* The [beyond bounds layout API][androidx.compose.ui.layout.BeyondBoundsLayout] is designed to be
* synchronous, so once you are done using the items, call [removeInterval] to remove
@@ -43,11 +44,10 @@
*
* 1. To allow items to be pinned while they are being scrolled into view.
*
- * 2. To allow users to call
- * [searchBeyondBounds][androidx.compose.ui.layout.BeyondBoundsLayout.searchBeyondBounds]
- * from within the completion block of another searchBeyondBounds call.
+ * 2. To allow users to call [layout][androidx.compose.ui.layout.BeyondBoundsLayout.layout] from
+ * within the completion block of another layout call.
*/
-internal class LazyListBeyondBoundsInfo {
+internal class LazyLayoutBeyondBoundsInfo {
private val beyondBoundsItems = mutableVectorOf<Interval>()
/**
@@ -108,7 +108,7 @@
}
/**
- * The Interval used to implement [LazyListBeyondBoundsInfo].
+ * The Interval used to implement [LazyLayoutBeyondBoundsInfo].
*/
internal data class Interval(
/** The start index for the interval. */
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsModifierLocal.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsModifierLocal.kt
index 822a91c..854dee2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsModifierLocal.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsModifierLocal.kt
@@ -17,8 +17,7 @@
package androidx.compose.foundation.lazy.layout
import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo.Interval
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo.Interval
import androidx.compose.ui.layout.BeyondBoundsLayout
import androidx.compose.ui.layout.BeyondBoundsLayout.BeyondBoundsScope
import androidx.compose.ui.layout.BeyondBoundsLayout.LayoutDirection.Companion.Above
@@ -35,8 +34,8 @@
import androidx.compose.ui.unit.LayoutDirection.Rtl
internal class LazyLayoutBeyondBoundsModifierLocal(
- private val state: BeyondBoundsState,
- private val beyondBoundsInfo: LazyListBeyondBoundsInfo,
+ private val state: LazyLayoutBeyondBoundsState,
+ private val beyondBoundsInfo: LazyLayoutBeyondBoundsInfo,
private val reverseLayout: Boolean,
private val layoutDirection: LayoutDirection,
private val orientation: Orientation
@@ -63,9 +62,9 @@
// We use a new interval each time because this function is re-entrant.
val startIndex = if (direction.isForward()) {
- state.lastVisibleIndex
+ state.lastPlacedIndex
} else {
- state.firstVisibleIndex
+ state.firstPlacedIndex
}
var interval = beyondBoundsInfo.addInterval(startIndex, startIndex)
var found: T? = null
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsState.kt
new file mode 100644
index 0000000..2996035
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutBeyondBoundsState.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.lazy.layout
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.ui.util.fastForEach
+import kotlin.math.min
+
+internal interface LazyLayoutBeyondBoundsState {
+
+ fun remeasure()
+
+ val itemCount: Int
+
+ val hasVisibleItems: Boolean
+
+ val firstPlacedIndex: Int
+
+ val lastPlacedIndex: Int
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal fun LazyLayoutItemProvider.calculateLazyLayoutPinnedIndices(
+ pinnedItemList: LazyLayoutPinnedItemList,
+ beyondBoundsInfo: LazyLayoutBeyondBoundsInfo,
+): List<Int> {
+ if (!beyondBoundsInfo.hasIntervals() && pinnedItemList.isEmpty()) {
+ return emptyList()
+ } else {
+ val pinnedItems = mutableListOf<Int>()
+ val beyondBoundsRange = if (beyondBoundsInfo.hasIntervals()) {
+ beyondBoundsInfo.start..min(beyondBoundsInfo.end, itemCount - 1)
+ } else {
+ IntRange.EMPTY
+ }
+ pinnedItemList.fastForEach {
+ val index = findIndexByKey(it.key, it.index)
+ if (index in beyondBoundsRange) return@fastForEach
+ if (index !in 0 until itemCount) return@fastForEach
+ pinnedItems.add(index)
+ }
+ for (i in beyondBoundsRange) {
+ pinnedItems.add(i)
+ }
+ return pinnedItems
+ }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
index 1f8aa80..7faf82e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
@@ -72,7 +72,7 @@
orientation,
mainAxisSpacing,
crossAxisSpacing,
- slots
+ slots,
)
val semanticState = rememberLazyStaggeredGridSemanticState(state, reverseLayout)
@@ -81,7 +81,19 @@
LazyLayout(
modifier = modifier
.then(state.remeasurementModifier)
+ .lazyLayoutSemantics(
+ itemProvider = itemProvider,
+ state = semanticState,
+ orientation = orientation,
+ userScrollEnabled = userScrollEnabled,
+ reverseScrolling = reverseLayout
+ )
.clipScrollableContainer(orientation)
+ .lazyStaggeredGridBeyondBoundsModifier(
+ state = state,
+ reverseLayout = reverseLayout,
+ orientation = orientation
+ )
.overscroll(overscrollEffect)
.scrollable(
orientation = orientation,
@@ -95,13 +107,6 @@
state = state,
overscrollEffect = overscrollEffect,
enabled = userScrollEnabled
- )
- .lazyLayoutSemantics(
- itemProvider = itemProvider,
- state = semanticState,
- orientation = orientation,
- userScrollEnabled = userScrollEnabled,
- reverseScrolling = reverseLayout
),
prefetchState = state.prefetchState,
itemProvider = itemProvider,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt
index 57f75d2..eb7fc8c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt
@@ -50,15 +50,18 @@
}
override fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
- val visibleItems = state.layoutInfo.visibleItemsInfo
- val itemSizeSum = visibleItems
- .fastSumBy { if (state.isVertical) it.size.height else it.size.width }
- val averageMainAxisItemSize = itemSizeSum / (visibleItems.size * state.laneCount)
+ val layoutInfo = state.layoutInfo
+ val visibleItems = layoutInfo.visibleItemsInfo
+ val itemSizeSum = visibleItems.fastSumBy {
+ if (state.isVertical) it.size.height else it.size.width
+ }
+ val averageMainAxisItemSize =
+ itemSizeSum / visibleItems.size + layoutInfo.mainAxisItemSpacing
- val indexesDiff = index - firstVisibleItemIndex
+ val lineDiff = index / state.laneCount - firstVisibleItemIndex / state.laneCount
var coercedOffset = minOf(abs(targetScrollOffset), averageMainAxisItemSize)
if (targetScrollOffset < 0) coercedOffset *= -1
- return (averageMainAxisItemSize * indexesDiff).toFloat() +
+ return averageMainAxisItemSize * lineDiff.toFloat() +
coercedOffset - firstVisibleItemScrollOffset
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsModifier.kt
new file mode 100644
index 0000000..445098f
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridBeyondBoundsModifier.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.lazy.staggeredgrid
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalLayoutDirection
+
+/**
+ * This modifier is used to measure and place additional items when the lazyList receives a
+ * request to layout items beyond the visible bounds.
+ */
+@Suppress("ComposableModifierFactory")
+@Composable
+internal fun Modifier.lazyStaggeredGridBeyondBoundsModifier(
+ state: LazyStaggeredGridState,
+ reverseLayout: Boolean,
+ orientation: Orientation
+): Modifier {
+ val layoutDirection = LocalLayoutDirection.current
+ val beyondBoundsState = remember(state) {
+ LazyStaggeredGridBeyondBoundsState(state)
+ }
+ return this then remember(
+ beyondBoundsState,
+ state,
+ reverseLayout,
+ layoutDirection,
+ orientation
+ ) {
+ LazyLayoutBeyondBoundsModifierLocal(
+ beyondBoundsState,
+ state.beyondBoundsInfo,
+ reverseLayout,
+ layoutDirection,
+ orientation
+ )
+ }
+}
+
+internal class LazyStaggeredGridBeyondBoundsState(
+ val state: LazyStaggeredGridState,
+) : LazyLayoutBeyondBoundsState {
+
+ override fun remeasure() {
+ state.remeasurement?.forceRemeasure()
+ }
+
+ override val itemCount: Int
+ get() = state.layoutInfo.totalItemsCount
+ override val hasVisibleItems: Boolean
+ get() = state.layoutInfo.visibleItemsInfo.isNotEmpty()
+ override val firstPlacedIndex: Int
+ get() = state.firstVisibleItemIndex
+ override val lastPlacedIndex: Int
+ get() = state.layoutInfo.visibleItemsInfo.last().index
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt
index 8941796..cca7a11 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt
@@ -241,11 +241,6 @@
internal annotation class LazyStaggeredGridScopeMarker
/**
- * Receiver scope for itemContent in [LazyStaggeredGridScope.item]
- */
-sealed interface LazyStaggeredGridItemScope
-
-/**
* Receiver scope for [LazyVerticalStaggeredGrid] and [LazyHorizontalStaggeredGrid]
*/
@LazyStaggeredGridScopeMarker
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridIntervalContent.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridIntervalContent.kt
index 4db8415..dc7406d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridIntervalContent.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridIntervalContent.kt
@@ -69,9 +69,6 @@
}
@OptIn(ExperimentalFoundationApi::class)
-internal object LazyStaggeredGridItemScopeImpl : LazyStaggeredGridItemScope
-
-@OptIn(ExperimentalFoundationApi::class)
internal class LazyStaggeredGridInterval(
override val key: ((index: Int) -> Any)?,
override val type: ((index: Int) -> Any?),
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt
new file mode 100644
index 0000000..11895ed
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt
@@ -0,0 +1,280 @@
+/*
+ * 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.lazy.staggeredgrid
+
+import androidx.compose.foundation.lazy.layout.LazyLayoutAnimateItemModifierNode
+import androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.util.fastAny
+import androidx.compose.ui.util.fastForEach
+
+/**
+ * Handles the item placement animations when it is set via
+ * [LazyStaggeredGridItemScope.animateItemPlacement].
+ *
+ * This class is responsible for detecting when item position changed, figuring our start/end
+ * offsets and starting the animations.
+ */
+internal class LazyStaggeredGridItemPlacementAnimator {
+ // state containing relevant info for active items.
+ private val keyToItemInfoMap = mutableMapOf<Any, ItemInfo>()
+
+ // snapshot of the key to index map used for the last measuring.
+ private var keyToIndexMap: LazyLayoutKeyIndexMap = LazyLayoutKeyIndexMap
+
+ // keeps the index of the first visible item index.
+ private var firstVisibleIndex = 0
+
+ // stored to not allocate it every pass.
+ private val movingAwayKeys = LinkedHashSet<Any>()
+ private val movingInFromStartBound = mutableListOf<LazyStaggeredGridPositionedItem>()
+ private val movingInFromEndBound = mutableListOf<LazyStaggeredGridPositionedItem>()
+ private val movingAwayToStartBound = mutableListOf<LazyStaggeredGridMeasuredItem>()
+ private val movingAwayToEndBound = mutableListOf<LazyStaggeredGridMeasuredItem>()
+
+ /**
+ * Should be called after the measuring so we can detect position changes and start animations.
+ *
+ * Note that this method can compose new item and add it into the [positionedItems] list.
+ */
+ fun onMeasured(
+ consumedScroll: Int,
+ layoutWidth: Int,
+ layoutHeight: Int,
+ positionedItems: MutableList<LazyStaggeredGridPositionedItem>,
+ itemProvider: LazyStaggeredGridMeasureProvider,
+ isVertical: Boolean,
+ laneCount: Int
+ ) {
+ if (!positionedItems.fastAny { it.hasAnimations } && keyToItemInfoMap.isEmpty()) {
+ // no animations specified - no work needed
+ reset()
+ return
+ }
+
+ val previousFirstVisibleIndex = firstVisibleIndex
+ firstVisibleIndex = positionedItems.firstOrNull()?.index ?: 0
+ val previousKeyToIndexMap = keyToIndexMap
+ keyToIndexMap = itemProvider.keyToIndexMap
+
+ val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
+
+ // the consumed scroll is considered as a delta we don't need to animate
+ val scrollOffset = if (isVertical) {
+ IntOffset(0, consumedScroll)
+ } else {
+ IntOffset(consumedScroll, 0)
+ }
+
+ // first add all items we had in the previous run
+ movingAwayKeys.addAll(keyToItemInfoMap.keys)
+ // iterate through the items which are visible (without animated offsets)
+ positionedItems.fastForEach { item ->
+ // remove items we have in the current one as they are still visible.
+ movingAwayKeys.remove(item.key)
+ if (item.hasAnimations) {
+ val itemInfo = keyToItemInfoMap[item.key]
+ // there is no state associated with this item yet
+ if (itemInfo == null) {
+ keyToItemInfoMap[item.key] =
+ ItemInfo(item.lane, item.span, item.crossAxisOffset)
+ val previousIndex = previousKeyToIndexMap[item.key]
+ if (previousIndex != -1 && item.index != previousIndex) {
+ if (previousIndex < previousFirstVisibleIndex) {
+ // the larger index will be in the start of the list
+ movingInFromStartBound.add(item)
+ } else {
+ movingInFromEndBound.add(item)
+ }
+ } else {
+ initializeNode(
+ item,
+ item.offset.let { if (item.isVertical) it.y else it.x }
+ )
+ }
+ } else {
+ item.forEachNode {
+ if (it.rawOffset != LazyLayoutAnimateItemModifierNode.NotInitialized) {
+ it.rawOffset += scrollOffset
+ }
+ }
+ itemInfo.lane = item.lane
+ itemInfo.span = item.span
+ itemInfo.crossAxisOffset = item.crossAxisOffset
+ startAnimationsIfNeeded(item)
+ }
+ } else {
+ // no animation, clean up if needed
+ keyToItemInfoMap.remove(item.key)
+ }
+ }
+
+ val accumulatedOffsetPerLane = IntArray(laneCount) { 0 }
+ if (movingInFromStartBound.isNotEmpty()) {
+ movingInFromStartBound.sortByDescending { previousKeyToIndexMap[it.key] }
+ movingInFromStartBound.fastForEach { item ->
+ accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
+ val mainAxisOffset = 0 - accumulatedOffsetPerLane[item.lane]
+ initializeNode(item, mainAxisOffset)
+ startAnimationsIfNeeded(item)
+ }
+ accumulatedOffsetPerLane.fill(0)
+ }
+ if (movingInFromEndBound.isNotEmpty()) {
+ movingInFromEndBound.sortBy { previousKeyToIndexMap[it.key] }
+ movingInFromEndBound.fastForEach { item ->
+ val mainAxisOffset = mainAxisLayoutSize + accumulatedOffsetPerLane[item.lane]
+ accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
+ initializeNode(item, mainAxisOffset)
+ startAnimationsIfNeeded(item)
+ }
+ accumulatedOffsetPerLane.fill(0)
+ }
+
+ movingAwayKeys.forEach { key ->
+ // found an item which was in our map previously but is not a part of the
+ // positionedItems now
+ val itemInfo = keyToItemInfoMap.getValue(key)
+ val newIndex = keyToIndexMap[key]
+
+ if (newIndex == -1) {
+ keyToItemInfoMap.remove(key)
+ } else {
+ val item = itemProvider.getAndMeasure(
+ newIndex,
+ SpanRange(itemInfo.lane, itemInfo.span)
+ )
+ // check if we have any active placement animation on the item
+ var inProgress = false
+ repeat(item.placeablesCount) {
+ if (item.getParentData(it).node?.isAnimationInProgress == true) {
+ inProgress = true
+ return@repeat
+ }
+ }
+ if ((!inProgress && newIndex == previousKeyToIndexMap[key])) {
+ keyToItemInfoMap.remove(key)
+ } else {
+ if (newIndex < firstVisibleIndex) {
+ movingAwayToStartBound.add(item)
+ } else {
+ movingAwayToEndBound.add(item)
+ }
+ }
+ }
+ }
+
+ if (movingAwayToStartBound.isNotEmpty()) {
+ movingAwayToStartBound.sortByDescending { keyToIndexMap[it.key] }
+ movingAwayToStartBound.fastForEach { item ->
+ accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
+ val mainAxisOffset = 0 - accumulatedOffsetPerLane[item.lane]
+
+ val itemInfo = keyToItemInfoMap.getValue(item.key)
+ val positionedItem =
+ item.position(mainAxisOffset, itemInfo.crossAxisOffset, mainAxisLayoutSize)
+ positionedItems.add(positionedItem)
+ startAnimationsIfNeeded(positionedItem)
+ }
+ accumulatedOffsetPerLane.fill(0)
+ }
+ if (movingAwayToEndBound.isNotEmpty()) {
+ movingAwayToEndBound.sortBy { keyToIndexMap[it.key] }
+ movingAwayToEndBound.fastForEach { item ->
+ val mainAxisOffset = mainAxisLayoutSize + accumulatedOffsetPerLane[item.lane]
+ accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
+
+ val itemInfo = keyToItemInfoMap.getValue(item.key)
+ val positionedItem =
+ item.position(mainAxisOffset, itemInfo.crossAxisOffset, mainAxisLayoutSize)
+ positionedItems.add(positionedItem)
+ startAnimationsIfNeeded(positionedItem)
+ }
+ }
+
+ movingInFromStartBound.clear()
+ movingInFromEndBound.clear()
+ movingAwayToStartBound.clear()
+ movingAwayToEndBound.clear()
+ movingAwayKeys.clear()
+ }
+
+ /**
+ * Should be called when the animations are not needed for the next positions change,
+ * for example when we snap to a new position.
+ */
+ fun reset() {
+ keyToItemInfoMap.clear()
+ keyToIndexMap = LazyLayoutKeyIndexMap
+ firstVisibleIndex = -1
+ }
+
+ private fun initializeNode(
+ item: LazyStaggeredGridPositionedItem,
+ mainAxisOffset: Int
+ ) {
+ val firstPlaceableOffset = item.offset
+
+ val targetFirstPlaceableOffset = if (item.isVertical) {
+ firstPlaceableOffset.copy(y = mainAxisOffset)
+ } else {
+ firstPlaceableOffset.copy(x = mainAxisOffset)
+ }
+
+ // initialize offsets
+ item.forEachNode { node ->
+ val diffToFirstPlaceableOffset =
+ item.offset - firstPlaceableOffset
+ node.rawOffset = targetFirstPlaceableOffset + diffToFirstPlaceableOffset
+ }
+ }
+
+ private fun startAnimationsIfNeeded(item: LazyStaggeredGridPositionedItem) {
+ item.forEachNode { node ->
+ val newTarget = item.offset
+ val currentTarget = node.rawOffset
+ if (currentTarget != LazyLayoutAnimateItemModifierNode.NotInitialized &&
+ currentTarget != newTarget
+ ) {
+ node.animatePlacementDelta(newTarget - currentTarget)
+ }
+ node.rawOffset = newTarget
+ }
+ }
+
+ private val Any?.node get() = this as? LazyLayoutAnimateItemModifierNode
+
+ private val LazyStaggeredGridPositionedItem.hasAnimations: Boolean
+ get() {
+ forEachNode { return true }
+ return false
+ }
+
+ private inline fun LazyStaggeredGridPositionedItem.forEachNode(
+ block: (LazyLayoutAnimateItemModifierNode) -> Unit
+ ) {
+ repeat(placeablesCount) { index ->
+ getParentData(index).node?.let(block)
+ }
+ }
+}
+
+private class ItemInfo(
+ var lane: Int,
+ var span: Int,
+ var crossAxisOffset: Int
+)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemProvider.kt
index f9a8f36..b54bd72 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemProvider.kt
@@ -31,6 +31,7 @@
@OptIn(ExperimentalFoundationApi::class)
internal interface LazyStaggeredGridItemProvider : LazyLayoutItemProvider {
val spanProvider: LazyStaggeredGridSpanProvider
+ val keyToIndexMap: LazyLayoutKeyIndexMap
}
@Composable
@@ -56,7 +57,7 @@
LazyStaggeredGridIntervalContent(latestContent())
}
- private val keyToIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
+ override val keyToIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
firstVisibleItemIndex = { state.firstVisibleItemIndex },
slidingWindowSize = { 90 },
extraItemCount = { 200 },
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemScope.kt
new file mode 100644
index 0000000..1bc2737
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemScope.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.VisibilityThreshold
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.lazy.layout.LazyLayoutAnimateItemModifierNode
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ParentDataModifierNode
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
+
+/**
+ * Receiver scope for itemContent in [LazyStaggeredGridScope.item]
+ */
+@Stable
+@LazyStaggeredGridScopeMarker
+sealed interface LazyStaggeredGridItemScope {
+ /**
+ * This modifier animates the item placement within the grid.
+ *
+ * When you scroll backward staggered grids could move already visible items in order
+ * to correct the accumulated errors in previous item size estimations. This modifier
+ * can animate such moves.
+ *
+ * Aside from that when you provide a key via [LazyStaggeredGridScope.item] /
+ * [LazyStaggeredGridScope.items] this modifier will enable item reordering animations.
+ *
+ * @param animationSpec a finite animation that will be used to animate the item placement.
+ */
+ @ExperimentalFoundationApi
+ fun Modifier.animateItemPlacement(
+ animationSpec: FiniteAnimationSpec<IntOffset> = spring(
+ stiffness = Spring.StiffnessMediumLow,
+ visibilityThreshold = IntOffset.VisibilityThreshold
+ )
+ ): Modifier
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal object LazyStaggeredGridItemScopeImpl : LazyStaggeredGridItemScope {
+ @ExperimentalFoundationApi
+ override fun Modifier.animateItemPlacement(animationSpec: FiniteAnimationSpec<IntOffset>) =
+ this then AnimateItemPlacementElement(animationSpec)
+}
+
+private class AnimateItemPlacementElement(
+ val animationSpec: FiniteAnimationSpec<IntOffset>
+) : ModifierNodeElement<AnimateItemPlacementNode>() {
+
+ override fun create(): AnimateItemPlacementNode = AnimateItemPlacementNode(animationSpec)
+
+ override fun update(node: AnimateItemPlacementNode): AnimateItemPlacementNode = node.also {
+ it.delegatingNode.placementAnimationSpec = animationSpec
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is AnimateItemPlacementElement) return false
+ return animationSpec != other.animationSpec
+ }
+
+ override fun hashCode(): Int {
+ return animationSpec.hashCode()
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ name = "animateItemPlacement"
+ value = animationSpec
+ }
+}
+
+private class AnimateItemPlacementNode(
+ animationSpec: FiniteAnimationSpec<IntOffset>
+) : DelegatingNode(), ParentDataModifierNode {
+
+ val delegatingNode = delegate(LazyLayoutAnimateItemModifierNode(animationSpec))
+
+ override fun Density.modifyParentData(parentData: Any?): Any = delegatingNode
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
index fa9be08..b653fd7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
@@ -18,9 +18,9 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.fastMaxOfOrNull
-import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
+import androidx.compose.foundation.lazy.layout.LazyLayoutAnimateItemModifierNode
+import androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap
import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
-import androidx.compose.foundation.lazy.layout.findIndexByKey
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridLaneInfo.Companion.FullSpan
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridLaneInfo.Companion.Unset
import androidx.compose.runtime.snapshots.Snapshot
@@ -31,6 +31,7 @@
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.constrainWidth
import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEachIndexed
import androidx.compose.ui.util.packInts
import androidx.compose.ui.util.unpackInt1
import androidx.compose.ui.util.unpackInt2
@@ -76,6 +77,7 @@
@ExperimentalFoundationApi
internal fun LazyLayoutMeasureScope.measureStaggeredGrid(
state: LazyStaggeredGridState,
+ pinnedItems: List<Int>,
itemProvider: LazyStaggeredGridItemProvider,
resolvedSlots: LazyStaggeredGridSlots,
constraints: Constraints,
@@ -89,6 +91,7 @@
): LazyStaggeredGridMeasureResult {
val context = LazyStaggeredGridMeasureContext(
state = state,
+ pinnedItems = pinnedItems,
itemProvider = itemProvider,
resolvedSlots = resolvedSlots,
constraints = constraints,
@@ -163,8 +166,9 @@
}
@OptIn(ExperimentalFoundationApi::class)
-private class LazyStaggeredGridMeasureContext(
+internal class LazyStaggeredGridMeasureContext(
val state: LazyStaggeredGridState,
+ val pinnedItems: List<Int>,
val itemProvider: LazyStaggeredGridItemProvider,
val resolvedSlots: LazyStaggeredGridSlots,
val constraints: Constraints,
@@ -191,6 +195,8 @@
spacing = mainAxisSpacing,
lane = lane,
span = span,
+ beforeContentPadding = beforeContentPadding,
+ afterContentPadding = afterContentPadding
)
}
@@ -662,7 +668,7 @@
// even if we compose items to fill before content padding we should ignore items fully
// located there for the state's scroll position calculation (first item + first offset)
debugLog { "adjusting for content padding" }
- if (beforeContentPadding > 0) {
+ if (beforeContentPadding > mainAxisSpacing) {
for (laneIndex in measuredItems.indices) {
val laneItems = measuredItems[laneIndex]
for (i in laneItems.indices) {
@@ -674,7 +680,6 @@
firstItemOffsets[laneIndex] != 0 &&
firstItemOffsets[laneIndex] >= size
) {
-
firstItemOffsets[laneIndex] -= size
firstItemIndices[laneIndex] = laneItems[i + 1].index
} else {
@@ -706,13 +711,22 @@
}
val mainAxisLayoutSize =
- min(if (isVertical) layoutHeight else layoutWidth, mainAxisAvailableSize)
+ min(if (isVertical) layoutHeight else layoutWidth, mainAxisAvailableSize).let {
+ // The offsets are calculated in [-beforePad; size + afterPad] interval
+ // Ensure the layout size used for positioning (and reverse layout calculation)
+ // is in the same interval.
+ it - beforeContentPadding + afterContentPadding
+ }
var extraItemOffset = itemScrollOffsets[0]
val extraItemsBefore = calculateExtraItems(
position = {
extraItemOffset -= it.sizeWithSpacings
- it.position(0, extraItemOffset, 0, mainAxisLayoutSize)
+ it.position(
+ mainAxis = extraItemOffset,
+ crossAxis = 0,
+ mainAxisLayoutSize = mainAxisLayoutSize
+ )
},
filter = { itemIndex ->
val lane = laneInfo.getLane(itemIndex)
@@ -727,7 +741,7 @@
}
)
- val positionedItems = calculatePositionedItems(
+ val visibleItems = calculateVisibleItems(
measuredItems,
itemScrollOffsets,
mainAxisLayoutSize,
@@ -736,7 +750,11 @@
extraItemOffset = itemScrollOffsets[0]
val extraItemsAfter = calculateExtraItems(
position = {
- val positionedItem = it.position(0, extraItemOffset, 0, mainAxisLayoutSize)
+ val positionedItem = it.position(
+ mainAxis = extraItemOffset,
+ crossAxis = 0,
+ mainAxisLayoutSize = mainAxisLayoutSize
+ )
extraItemOffset += it.sizeWithSpacings
positionedItem
},
@@ -756,10 +774,25 @@
}
)
+ val positionedItems = mutableListOf<LazyStaggeredGridPositionedItem>()
+ positionedItems.addAll(extraItemsBefore)
+ positionedItems.addAll(visibleItems)
+ positionedItems.addAll(extraItemsAfter)
+
debugLog {
"positioned: $positionedItems"
}
+ state.placementAnimator.onMeasured(
+ consumedScroll = consumedScroll.toInt(),
+ layoutWidth = layoutWidth,
+ layoutHeight = layoutHeight,
+ positionedItems = positionedItems,
+ itemProvider = measuredItemProvider,
+ isVertical = isVertical,
+ laneCount = laneCount
+ )
+
// end placement
// only scroll backward if the first item is not on screen or fully visible
@@ -773,22 +806,14 @@
firstVisibleItemScrollOffsets = firstItemOffsets,
consumedScroll = consumedScroll,
measureResult = layout(layoutWidth, layoutHeight) {
- extraItemsBefore.fastForEach { item ->
- item.place(scope = this, context = this@measure)
- }
-
positionedItems.fastForEach { item ->
item.place(scope = this, context = this@measure)
}
-
- extraItemsAfter.fastForEach { item ->
- item.place(scope = this, context = this@measure)
- }
},
canScrollForward = canScrollForward,
canScrollBackward = canScrollBackward,
isVertical = isVertical,
- visibleItemsInfo = positionedItems,
+ visibleItemsInfo = visibleItems,
totalItemsCount = itemCount,
viewportSize = IntSize(layoutWidth, layoutHeight),
viewportStartOffset = minOffset,
@@ -800,7 +825,7 @@
}
}
-private fun LazyStaggeredGridMeasureContext.calculatePositionedItems(
+private fun LazyStaggeredGridMeasureContext.calculateVisibleItems(
measuredItems: Array<ArrayDeque<LazyStaggeredGridMeasuredItem>>,
itemScrollOffsets: IntArray,
mainAxisLayoutSize: Int,
@@ -823,13 +848,18 @@
val mainAxisOffset = itemScrollOffsets.maxInRange(spanRange)
val crossAxisOffset = resolvedSlots.positions[laneIndex]
- if (item.placeables.isEmpty()) {
+ if (item.placeablesCount == 0) {
// nothing to place, ignore spacings
continue
}
positionedItems +=
- item.position(laneIndex, mainAxisOffset, crossAxisOffset, mainAxisLayoutSize)
+ item.position(
+ mainAxis = mainAxisOffset,
+ crossAxis = crossAxisOffset,
+ mainAxisLayoutSize = mainAxisLayoutSize,
+ lane = laneIndex
+ )
spanRange.forEach { lane ->
itemScrollOffsets[lane] = mainAxisOffset + item.sizeWithSpacings
}
@@ -844,10 +874,7 @@
): List<LazyStaggeredGridPositionedItem> {
var result: MutableList<LazyStaggeredGridPositionedItem>? = null
- val pinnedItems = state.pinnedItems
- pinnedItems.fastForEach { item ->
- val index = itemProvider.findIndexByKey(item.key, item.index)
-
+ pinnedItems.fastForEach { index ->
if (filter(index)) {
val spanRange = itemProvider.getSpanRange(index, 0)
if (result == null) {
@@ -862,7 +889,7 @@
}
@JvmInline
-private value class SpanRange private constructor(val packedValue: Long) {
+internal value class SpanRange private constructor(val packedValue: Long) {
constructor(lane: Int, span: Int) : this(packInts(lane, lane + span))
inline val start get(): Int = unpackInt1(packedValue)
@@ -959,9 +986,9 @@
laneInfo.findPreviousItemIndex(item, lane)
@OptIn(ExperimentalFoundationApi::class)
-private class LazyStaggeredGridMeasureProvider(
+internal class LazyStaggeredGridMeasureProvider(
private val isVertical: Boolean,
- private val itemProvider: LazyLayoutItemProvider,
+ private val itemProvider: LazyStaggeredGridItemProvider,
private val measureScope: LazyLayoutMeasureScope,
private val resolvedSlots: LazyStaggeredGridSlots,
private val measuredItemFactory: MeasuredItemFactory,
@@ -989,10 +1016,12 @@
val placeables = measureScope.measure(index, childConstraints(span.start, span.size))
return measuredItemFactory.createItem(index, span.start, span.size, key, placeables)
}
+
+ val keyToIndexMap: LazyLayoutKeyIndexMap get() = itemProvider.keyToIndexMap
}
// This interface allows to avoid autoboxing on index param
-private fun interface MeasuredItemFactory {
+internal fun interface MeasuredItemFactory {
fun createItem(
index: Int,
lane: Int,
@@ -1002,17 +1031,23 @@
): LazyStaggeredGridMeasuredItem
}
-private class LazyStaggeredGridMeasuredItem(
+internal class LazyStaggeredGridMeasuredItem(
val index: Int,
val key: Any,
- val placeables: List<Placeable>,
- val isVertical: Boolean,
- val spacing: Int,
+ private val placeables: List<Placeable>,
+ private val isVertical: Boolean,
+ spacing: Int,
val lane: Int,
val span: Int,
+ private val beforeContentPadding: Int,
+ private val afterContentPadding: Int,
) {
var isVisible = true
+ val placeablesCount: Int get() = placeables.size
+
+ fun getParentData(index: Int) = placeables[index].parentData
+
val mainAxisSize: Int = placeables.fastMaxOfOrNull { placeable ->
if (isVertical) placeable.height else placeable.width
} ?: 0
@@ -1024,10 +1059,10 @@
} ?: 0
fun position(
- lane: Int,
mainAxis: Int,
crossAxis: Int,
mainAxisLayoutSize: Int,
+ lane: Int = 0
): LazyStaggeredGridPositionedItem =
LazyStaggeredGridPositionedItem(
offset = if (isVertical) {
@@ -1039,46 +1074,74 @@
index = index,
key = key,
size = if (isVertical) {
- IntSize(crossAxisSize, sizeWithSpacings)
+ IntSize(crossAxisSize, mainAxisSize)
} else {
- IntSize(sizeWithSpacings, crossAxisSize)
+ IntSize(mainAxisSize, crossAxisSize)
},
placeables = placeables,
isVertical = isVertical,
mainAxisLayoutSize = mainAxisLayoutSize,
+ minMainAxisOffset = -beforeContentPadding,
+ maxMainAxisOffset = mainAxisLayoutSize + afterContentPadding,
+ span = span
)
}
-@OptIn(ExperimentalFoundationApi::class)
-private class LazyStaggeredGridPositionedItem(
+internal class LazyStaggeredGridPositionedItem(
override val offset: IntOffset,
override val index: Int,
override val lane: Int,
override val key: Any,
override val size: IntSize,
private val placeables: List<Placeable>,
- private val isVertical: Boolean,
+ val isVertical: Boolean,
private val mainAxisLayoutSize: Int,
+ private val minMainAxisOffset: Int,
+ private val maxMainAxisOffset: Int,
+ val span: Int
) : LazyStaggeredGridItemInfo {
+
+ val placeablesCount: Int get() = placeables.size
+
+ val mainAxisSize get() = if (isVertical) size.height else size.width
+
+ val crossAxisOffset get() = if (isVertical) offset.x else offset.y
+
+ fun getParentData(index: Int) = placeables[index].parentData
+
fun place(
scope: Placeable.PlacementScope,
context: LazyStaggeredGridMeasureContext
) = with(context) {
with(scope) {
- placeables.fastForEach { placeable ->
- val reverseLayoutAwareOffset = if (reverseLayout) {
- offset.copy { mainAxisOffset ->
+ placeables.fastForEachIndexed { index, placeable ->
+ val minOffset = minMainAxisOffset - placeable.mainAxisSize
+ val maxOffset = maxMainAxisOffset
+
+ var offset = offset
+ val animateNode = getParentData(index) as? LazyLayoutAnimateItemModifierNode
+ if (animateNode != null) {
+ val animatedOffset = offset + animateNode.placementDelta
+ // cancel the animation if current and target offsets are both out of the bounds.
+ if ((offset.mainAxis <= minOffset && animatedOffset.mainAxis <= minOffset) ||
+ (offset.mainAxis >= maxOffset && animatedOffset.mainAxis >= maxOffset)
+ ) {
+ animateNode.cancelAnimation()
+ }
+ offset = animatedOffset
+ }
+ if (reverseLayout) {
+ offset = offset.copy { mainAxisOffset ->
mainAxisLayoutSize - mainAxisOffset - placeable.mainAxisSize
}
- } else {
- offset
}
-
- placeable.placeRelativeWithLayer(reverseLayoutAwareOffset + contentOffset)
+ offset += contentOffset
+ placeable.placeRelativeWithLayer(offset)
}
}
}
+ private val IntOffset.mainAxis get() = if (isVertical) y else x
private inline val Placeable.mainAxisSize get() = if (isVertical) height else width
private inline fun IntOffset.copy(mainAxisMap: (Int) -> Int): IntOffset =
IntOffset(if (isVertical) x else mainAxisMap(x), if (isVertical) mainAxisMap(y) else y)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
index 1f36802..c72c114 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
@@ -23,6 +23,7 @@
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.foundation.lazy.layout.calculateLazyLayoutPinnedIndices
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.unit.Constraints
@@ -96,8 +97,14 @@
calculateTopPadding() + calculateBottomPadding()
}.roundToPx()
+ val pinnedItems = itemProvider.calculateLazyLayoutPinnedIndices(
+ state.pinnedItems,
+ state.beyondBoundsInfo
+ )
+
measureStaggeredGrid(
state = state,
+ pinnedItems = pinnedItems,
itemProvider = itemProvider,
resolvedSlots = resolvedSlots,
constraints = constraints.copy(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
index 661a73c..a1b62d6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
@@ -23,6 +23,7 @@
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.lazy.layout.LazyAnimateScrollScope
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle
@@ -156,7 +157,8 @@
/** implementation of [LazyAnimateScrollScope] scope required for [animateScrollToItem] **/
private val animateScrollScope = LazyStaggeredGridAnimateScrollScope(this)
- private var remeasurement: Remeasurement? = null
+ internal var remeasurement: Remeasurement? = null
+ private set
internal val remeasurementModifier = object : RemeasurementModifier {
override fun onRemeasurementAvailable(remeasurement: Remeasurement) {
@@ -164,6 +166,8 @@
}
}
+ internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
+
/**
* Only used for testing to disable prefetching when needed to test the main logic.
*/
@@ -210,6 +214,8 @@
*/
internal val pinnedItems = LazyLayoutPinnedItemList()
+ internal val placementAnimator = LazyStaggeredGridItemPlacementAnimator()
+
/**
* Call this function to take control of scrolling and gain the ability to send scroll events
* via [ScrollScope.scrollBy]. All actions that change the logical scroll position must be
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
index a96b45f..5d8d4093 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
@@ -25,7 +25,6 @@
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
import androidx.compose.foundation.lazy.NearestItemsExtraItemCount
import androidx.compose.foundation.lazy.NearestItemsSlidingWindowSize
import androidx.compose.foundation.lazy.layout.IntervalList
@@ -90,7 +89,7 @@
/** The alignment to align pages vertically. Required when isVertical is false */
verticalAlignment: Alignment.Vertical,
/** The content of the list */
- pageContent: @Composable (page: Int) -> Unit
+ pageContent: @Composable PagerScope.(page: Int) -> Unit
) {
require(beyondBoundsPageCount >= 0) {
"beyondBoundsPageCount should be greater than or equal to 0, " +
@@ -105,8 +104,6 @@
key = key
) { state.pageCount }
- val beyondBoundsInfo = remember { LazyListBeyondBoundsInfo() }
-
val measurePolicy = rememberPagerMeasurePolicy(
state = state,
contentPadding = contentPadding,
@@ -119,7 +116,6 @@
verticalAlignment = verticalAlignment,
itemProvider = pagerItemProvider,
pageCount = { state.pageCount },
- beyondBoundsInfo = beyondBoundsInfo
)
val pagerFlingBehavior = remember(flingBehavior, state) {
@@ -152,7 +148,12 @@
reverseScrolling = reverseLayout
)
.clipScrollableContainer(orientation)
- .pagerBeyondBoundsModifier(state, beyondBoundsInfo, reverseLayout, orientation)
+ .pagerBeyondBoundsModifier(
+ state,
+ beyondBoundsPageCount,
+ reverseLayout,
+ orientation
+ )
.overscroll(overscrollEffect)
.scrollable(
orientation = orientation,
@@ -178,7 +179,7 @@
@ExperimentalFoundationApi
internal class PagerLazyLayoutItemProvider(
val state: PagerState,
- latestContent: () -> (@Composable (page: Int) -> Unit),
+ latestContent: () -> (@Composable PagerScope.(page: Int) -> Unit),
key: ((index: Int) -> Any)?,
pageCount: () -> Int
) : LazyLayoutItemProvider {
@@ -191,13 +192,16 @@
extraItemCount = { NearestItemsExtraItemCount },
content = { pagerContent }
)
+
+ private val pagerScopeImpl = PagerScopeImpl
+
override val itemCount: Int
get() = pagerContent.itemCount
@Composable
override fun Item(index: Int) {
pagerContent.PinnableItem(index, state.pinnedPages) { localIndex ->
- item(localIndex)
+ item(pagerScopeImpl, localIndex)
}
}
@@ -208,7 +212,7 @@
@OptIn(ExperimentalFoundationApi::class)
private class PagerLayoutIntervalContent(
- val pageContent: @Composable (page: Int) -> Unit,
+ val pageContent: @Composable PagerScope.(page: Int) -> Unit,
val key: ((index: Int) -> Any)?,
val pageCount: Int
) : LazyLayoutIntervalContent<PagerIntervalContent>() {
@@ -221,14 +225,14 @@
@OptIn(ExperimentalFoundationApi::class)
internal class PagerIntervalContent(
override val key: ((page: Int) -> Any)?,
- val item: @Composable (page: Int) -> Unit
+ val item: @Composable PagerScope.(page: Int) -> Unit
) : LazyLayoutIntervalContent.Interval
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun rememberPagerItemProvider(
state: PagerState,
- pageContent: @Composable (page: Int) -> Unit,
+ pageContent: @Composable PagerScope.(page: Int) -> Unit,
key: ((index: Int) -> Any)?,
pageCount: () -> Int
): PagerLazyLayoutItemProvider {
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 95c13f5..025ea33 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
@@ -116,7 +116,7 @@
pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
Orientation.Horizontal
),
- pageContent: @Composable (page: Int) -> Unit
+ pageContent: @Composable PagerScope.(page: Int) -> Unit
) {
Pager(
state = state,
@@ -222,7 +222,7 @@
pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
Orientation.Horizontal
),
- pageContent: @Composable (page: Int) -> Unit
+ pageContent: @Composable PagerScope.(page: Int) -> Unit
) {
Pager(
state = state,
@@ -300,7 +300,7 @@
pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
Orientation.Vertical
),
- pageContent: @Composable (page: Int) -> Unit
+ pageContent: @Composable PagerScope.(page: Int) -> Unit
) {
Pager(
state = state,
@@ -406,7 +406,7 @@
pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
Orientation.Vertical
),
- pageContent: @Composable (page: Int) -> Unit
+ pageContent: @Composable PagerScope.(page: Int) -> Unit
) {
Pager(
state = state,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
index bc26545..7c9deaf 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
@@ -17,8 +17,7 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
-import androidx.compose.foundation.lazy.layout.BeyondBoundsState
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsState
import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@@ -34,21 +33,21 @@
@Composable
internal fun Modifier.pagerBeyondBoundsModifier(
state: PagerState,
- beyondBoundsInfo: LazyListBeyondBoundsInfo,
+ beyondBoundsPageCount: Int,
reverseLayout: Boolean,
orientation: Orientation
): Modifier {
val layoutDirection = LocalLayoutDirection.current
return this then remember(
state,
- beyondBoundsInfo,
+ beyondBoundsPageCount,
reverseLayout,
layoutDirection,
orientation
) {
LazyLayoutBeyondBoundsModifierLocal(
- PagerBeyondBoundsState(state),
- beyondBoundsInfo,
+ PagerBeyondBoundsState(state, beyondBoundsPageCount),
+ state.beyondBoundsInfo,
reverseLayout,
layoutDirection,
orientation
@@ -57,7 +56,10 @@
}
@OptIn(ExperimentalFoundationApi::class)
-internal class PagerBeyondBoundsState(val state: PagerState) : BeyondBoundsState {
+internal class PagerBeyondBoundsState(
+ private val state: PagerState,
+ private val beyondBoundsPageCount: Int
+) : LazyLayoutBeyondBoundsState {
override fun remeasure() {
state.remeasurement?.forceRemeasure()
}
@@ -66,8 +68,11 @@
get() = state.layoutInfo.pagesCount
override val hasVisibleItems: Boolean
get() = state.layoutInfo.visiblePagesInfo.isNotEmpty()
- override val firstVisibleIndex: Int
- get() = state.firstVisiblePage
- override val lastVisibleIndex: Int
- get() = state.layoutInfo.visiblePagesInfo.last().index
+ override val firstPlacedIndex: Int
+ get() = maxOf(0, state.firstVisiblePage - beyondBoundsPageCount)
+ override val lastPlacedIndex: Int
+ get() = minOf(
+ itemCount - 1,
+ state.layoutInfo.visiblePagesInfo.last().index + beyondBoundsPageCount
+ )
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
index 6785dea..68693ac 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
@@ -20,9 +20,7 @@
import androidx.compose.foundation.fastFilter
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
-import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
import androidx.compose.ui.Alignment
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.Placeable
@@ -35,7 +33,6 @@
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMaxBy
import kotlin.math.abs
-import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.math.sign
@@ -58,8 +55,7 @@
visualPageOffset: IntOffset,
pageAvailableSize: Int,
beyondBoundsPageCount: Int,
- beyondBoundsInfo: LazyListBeyondBoundsInfo,
- pinnedPages: LazyLayoutPinnedItemList,
+ pinnedPages: List<Int>,
layout: (Int, Int, Placeable.PlacementScope.() -> Unit) -> MeasureResult
): PagerMeasureResult {
require(beforeContentPadding >= 0)
@@ -289,9 +285,7 @@
// Compose extra pages before
val extraPagesBefore = createPagesBeforeList(
- beyondBoundsInfo = beyondBoundsInfo,
currentFirstPage = currentFirstPage,
- pagesCount = pageCount,
beyondBoundsPageCount = beyondBoundsPageCount,
pinnedPages = pinnedPages
) {
@@ -318,8 +312,7 @@
// Compose pages after last page
val extraPagesAfter = createPagesAfterList(
- beyondBoundsInfo = beyondBoundsInfo,
- visiblePages = visiblePages,
+ currentLastPage = visiblePages.last().index,
pagesCount = pageCount,
beyondBoundsPageCount = beyondBoundsPageCount,
pinnedPages = pinnedPages
@@ -440,59 +433,44 @@
return itemCurrentPosition - desiredDistance
}
-@OptIn(ExperimentalFoundationApi::class)
private fun createPagesAfterList(
- beyondBoundsInfo: LazyListBeyondBoundsInfo,
- visiblePages: MutableList<MeasuredPage>,
+ currentLastPage: Int,
pagesCount: Int,
beyondBoundsPageCount: Int,
- pinnedPages: LazyLayoutPinnedItemList,
+ pinnedPages: List<Int>,
getAndMeasure: (Int) -> MeasuredPage
): List<MeasuredPage> {
- fun LazyListBeyondBoundsInfo.endIndex() = min(end, pagesCount - 1)
-
var list: MutableList<MeasuredPage>? = null
- var end = visiblePages.last().index
+ val end = minOf(currentLastPage + beyondBoundsPageCount, pagesCount - 1)
fun addPage(index: Int) {
if (list == null) list = mutableListOf()
requireNotNull(list).add(getAndMeasure(index))
}
- if (beyondBoundsInfo.hasIntervals()) {
- end = maxOf(beyondBoundsInfo.endIndex(), end)
- }
-
- end = minOf(end + beyondBoundsPageCount, pagesCount - 1)
-
- for (i in visiblePages.last().index + 1..end) {
+ for (i in currentLastPage + 1..end) {
addPage(i)
}
- pinnedPages.fastForEach { page ->
- if (page.index in (end + 1) until pagesCount) {
- addPage(page.index)
+ pinnedPages.fastForEach { pageIndex ->
+ if (pageIndex in (end + 1) until pagesCount) {
+ addPage(pageIndex)
}
}
return list ?: emptyList()
}
-@OptIn(ExperimentalFoundationApi::class)
private fun createPagesBeforeList(
- beyondBoundsInfo: LazyListBeyondBoundsInfo,
currentFirstPage: Int,
- pagesCount: Int,
beyondBoundsPageCount: Int,
- pinnedPages: LazyLayoutPinnedItemList,
+ pinnedPages: List<Int>,
getAndMeasure: (Int) -> MeasuredPage
): List<MeasuredPage> {
- fun LazyListBeyondBoundsInfo.startIndex() = min(start, pagesCount - 1)
-
var list: MutableList<MeasuredPage>? = null
- var start = currentFirstPage
+ val start = maxOf(0, currentFirstPage - beyondBoundsPageCount)
fun addPage(index: Int) {
if (list == null) list = mutableListOf()
@@ -501,19 +479,13 @@
)
}
- if (beyondBoundsInfo.hasIntervals()) {
- start = minOf(beyondBoundsInfo.startIndex(), start)
- }
-
- start = maxOf(0, start - beyondBoundsPageCount)
-
for (i in currentFirstPage - 1 downTo start) {
addPage(i)
}
- pinnedPages.fastForEach { page ->
- if (page.index < start) {
- addPage(page.index)
+ pinnedPages.fastForEach { pageIndex ->
+ if (pageIndex < start) {
+ addPage(pageIndex)
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
index 0c56d50..2049609 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
@@ -22,8 +22,8 @@
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
-import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.foundation.lazy.layout.calculateLazyLayoutPinnedIndices
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.Snapshot
@@ -51,7 +51,6 @@
horizontalAlignment: Alignment.Horizontal?,
verticalAlignment: Alignment.Vertical?,
pageCount: () -> Int,
- beyondBoundsInfo: LazyListBeyondBoundsInfo
) = remember<LazyLayoutMeasureScope.(Constraints) -> MeasureResult>(
contentPadding,
pageSpacing,
@@ -63,7 +62,6 @@
horizontalAlignment,
verticalAlignment,
pageCount,
- beyondBoundsInfo
) {
{ containerConstraints ->
val isVertical = orientation == Orientation.Vertical
@@ -152,6 +150,11 @@
}
}
+ val pinnedPages = itemProvider.calculateLazyLayoutPinnedIndices(
+ pinnedItemList = state.pinnedPages,
+ beyondBoundsInfo = state.beyondBoundsInfo
+ )
+
measurePager(
beforeContentPadding = beforeContentPadding,
afterContentPadding = afterContentPadding,
@@ -170,8 +173,7 @@
pagerItemProvider = itemProvider,
reverseLayout = reverseLayout,
scrollToBeConsumed = state.scrollToBeConsumed,
- beyondBoundsInfo = beyondBoundsInfo,
- pinnedPages = state.pinnedPages,
+ pinnedPages = pinnedPages,
layout = { width, height, placement ->
layout(
containerConstraints.constrainWidth(width + totalHorizontalPadding),
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/BeyondBoundsState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScope.kt
similarity index 68%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/BeyondBoundsState.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScope.kt
index 3407f2c..7eeae76 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/BeyondBoundsState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScope.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy.layout
+package androidx.compose.foundation.pager
-internal interface BeyondBoundsState {
+import androidx.compose.foundation.ExperimentalFoundationApi
- fun remeasure()
+/**
+ * Receiver scope for [Pager].
+ */
+@ExperimentalFoundationApi
+sealed interface PagerScope
- val itemCount: Int
-
- val hasVisibleItems: Boolean
-
- val firstVisibleIndex: Int
-
- val lastVisibleIndex: Int
-}
\ No newline at end of file
+@OptIn(ExperimentalFoundationApi::class)
+internal object PagerScopeImpl : PagerScope
\ No newline at end of file
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 c6184a6..23e45b8 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
@@ -28,6 +28,7 @@
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.lazy.AwaitFirstLayoutModifier
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsInfo
import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
import androidx.compose.runtime.Composable
@@ -361,6 +362,8 @@
internal val prefetchState = LazyLayoutPrefetchState()
+ internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
+
/**
* Provides a modifier which allows to delay some interactions (e.g. scroll)
* until layout is ready.
@@ -604,6 +607,24 @@
}
}
}
+
+ /**
+ * An utility function to help to calculate a given page's offset. Since this is based off
+ * [currentPageOffsetFraction] the same concept applies: a fraction offset that represents
+ * how far [page] is from the settled position (represented by [currentPage] offset). The
+ * difference here is that [currentPageOffsetFraction] is a value between -0.5 and 0.5 and
+ * the value calculate by this function can be larger than these numbers if [page] is different
+ * than [currentPage].
+ *
+ * @param page The page to calculate the offset from. This should be between 0 and [pageCount].
+ * @return The offset of [page] with respect to [currentPage].
+ */
+ fun getOffsetFractionForPage(page: Int): Float {
+ require(page in 0..pageCount) {
+ "page $page is not within the range 0 to pageCount"
+ }
+ return (currentPage - page) + currentPageOffsetFraction
+ }
}
@OptIn(ExperimentalFoundationApi::class)
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 9680f3b..cac5e39 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
@@ -30,7 +30,7 @@
import androidx.compose.ui.node.GlobalPositionAwareModifierNode
import androidx.compose.ui.node.LayoutModifierNode
import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateMeasurement
import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.Placeholder
@@ -60,7 +60,7 @@
) : DelegatingNode(), LayoutModifierNode, DrawModifierNode, GlobalPositionAwareModifierNode,
SemanticsModifierNode {
- private val delegate = delegated {
+ private val delegate = delegate(
TextAnnotatedStringNode(
text = text,
style = style,
@@ -74,7 +74,7 @@
onPlaceholderLayout = onPlaceholderLayout,
selectionController = selectionController
)
- }
+ )
init {
requireNotNull(selectionController) {
@@ -149,6 +149,6 @@
)
)
// we always relayout when we're selectable
- invalidateMeasurements()
+ invalidateMeasurement()
}
}
\ No newline at end of file
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 f948a2f..7d4e8d4 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
@@ -34,7 +34,7 @@
import androidx.compose.ui.node.SemanticsModifierNode
import androidx.compose.ui.node.invalidateDraw
import androidx.compose.ui.node.invalidateLayer
-import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateMeasurement
import androidx.compose.ui.node.invalidateSemantics
import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.semantics.getTextLayoutResult
@@ -200,7 +200,7 @@
minLines = minLines,
placeholders = placeholders
)
- invalidateMeasurements()
+ invalidateMeasurement()
invalidateDraw()
}
}
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 93c1391..ca2ea56 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
@@ -37,7 +37,7 @@
import androidx.compose.ui.node.LayoutModifierNode
import androidx.compose.ui.node.SemanticsModifierNode
import androidx.compose.ui.node.invalidateLayer
-import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateMeasurement
import androidx.compose.ui.node.invalidateSemantics
import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.semantics.getTextLayoutResult
@@ -168,7 +168,7 @@
maxLines = maxLines,
minLines = minLines
)
- invalidateMeasurements()
+ invalidateMeasurement()
invalidateLayer()
}
}
diff --git a/compose/material/material-icons-extended/build.gradle b/compose/material/material-icons-extended/build.gradle
index 4a58d27..0903147 100644
--- a/compose/material/material-icons-extended/build.gradle
+++ b/compose/material/material-icons-extended/build.gradle
@@ -86,8 +86,6 @@
}
}
-def allThemes = ["filled", "outlined", "rounded", "sharp", "twotone"]
-
configurations {
embedThemesDebug {
attributes {
@@ -105,10 +103,17 @@
}
if (!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
- for (themeName in allThemes) {
- def otherProject = project(":compose:material:material-icons-extended-" + themeName)
- project.dependencies.add("embedThemesDebug", otherProject)
- project.dependencies.add("embedThemesRelease", otherProject)
+ def allThemeProjects = [
+ project(":compose:material:material-icons-extended-filled"),
+ project(":compose:material:material-icons-extended-outlined"),
+ project(":compose:material:material-icons-extended-rounded"),
+ project(":compose:material:material-icons-extended-sharp"),
+ project(":compose:material:material-icons-extended-twotone")
+ ]
+
+ for (themeProject in allThemeProjects) {
+ project.dependencies.add("embedThemesDebug", themeProject)
+ project.dependencies.add("embedThemesRelease", themeProject)
}
// Compiling all of the icons in this project takes a while,
// so when possible, we compile each theme in its own project and merge them here.
diff --git a/compose/material/material/lint-baseline.xml b/compose/material/material/lint-baseline.xml
index 0ae57a1..fd09ac4 100644
--- a/compose/material/material/lint-baseline.xml
+++ b/compose/material/material/lint-baseline.xml
@@ -136,31 +136,4 @@
file="src/androidAndroidTest/kotlin/androidx/compose/material/TabScreenshotTest.kt"/>
</issue>
- <issue
- id="ExperimentalPropertyAnnotation"
- message="This property does not have all required annotations to correctly mark it as experimental."
- errorLine1=" @ExperimentalMaterialApi"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt"/>
- </issue>
-
- <issue
- id="ExperimentalPropertyAnnotation"
- message="This property does not have all required annotations to correctly mark it as experimental."
- errorLine1=" @ExperimentalMaterialApi"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt"/>
- </issue>
-
- <issue
- id="ExperimentalPropertyAnnotation"
- message="This property does not have all required annotations to correctly mark it as experimental."
- errorLine1=" @ExperimentalMaterialApi"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt"/>
- </issue>
-
</issues>
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
index d72ea8f..1405786 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
@@ -1442,9 +1442,9 @@
}
assertThat(drawerState.currentValue).isEqualTo(BottomDrawerValue.Closed)
- assertThat(drawerState.swipeableState.hasAnchorForValue(BottomDrawerValue.Open))
+ assertThat(drawerState.anchoredDraggableState.hasAnchorForValue(BottomDrawerValue.Open))
.isFalse()
- assertThat(drawerState.swipeableState.hasAnchorForValue(BottomDrawerValue.Expanded))
+ assertThat(drawerState.anchoredDraggableState.hasAnchorForValue(BottomDrawerValue.Expanded))
.isFalse()
scope.launch { drawerState.open() }
@@ -1478,9 +1478,9 @@
}
assertThat(drawerState.currentValue).isEqualTo(BottomDrawerValue.Closed)
- assertThat(drawerState.swipeableState.hasAnchorForValue(BottomDrawerValue.Open))
+ assertThat(drawerState.anchoredDraggableState.hasAnchorForValue(BottomDrawerValue.Open))
.isFalse()
- assertThat(drawerState.swipeableState.hasAnchorForValue(BottomDrawerValue.Expanded))
+ assertThat(drawerState.anchoredDraggableState.hasAnchorForValue(BottomDrawerValue.Expanded))
.isFalse()
scope.launch { drawerState.open() }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
index ec5503d..9043b15 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
@@ -963,17 +963,20 @@
rule.waitForIdle()
assertThat(state.currentValue).isEqualTo(ModalBottomSheetValue.HalfExpanded) // We should
// retain the current value if possible
- assertThat(state.swipeableState.anchors).containsKey(ModalBottomSheetValue.Hidden)
- assertThat(state.swipeableState.anchors).containsKey(ModalBottomSheetValue.HalfExpanded)
- assertThat(state.swipeableState.anchors).containsKey(ModalBottomSheetValue.Expanded)
+ assertThat(state.anchoredDraggableState.anchors)
+ .containsKey(ModalBottomSheetValue.Hidden)
+ assertThat(state.anchoredDraggableState.anchors)
+ .containsKey(ModalBottomSheetValue.HalfExpanded)
+ assertThat(state.anchoredDraggableState.anchors).containsKey(ModalBottomSheetValue.Expanded)
amountOfItems = 0 // When the sheet height is 0, we should only have a hidden anchor
rule.waitForIdle()
assertThat(state.currentValue).isEqualTo(ModalBottomSheetValue.Hidden)
- assertThat(state.swipeableState.anchors).containsKey(ModalBottomSheetValue.Hidden)
- assertThat(state.swipeableState.anchors)
+ assertThat(state.anchoredDraggableState.anchors).containsKey(ModalBottomSheetValue.Hidden)
+ assertThat(state.anchoredDraggableState.anchors)
.doesNotContainKey(ModalBottomSheetValue.HalfExpanded)
- assertThat(state.swipeableState.anchors).doesNotContainKey(ModalBottomSheetValue.Expanded)
+ assertThat(state.anchoredDraggableState.anchors)
+ .doesNotContainKey(ModalBottomSheetValue.Expanded)
}
@Test
@@ -990,11 +993,12 @@
)
}
- assertThat(state.swipeableState.currentValue).isEqualTo(ModalBottomSheetValue.Hidden)
+ assertThat(state.currentValue).isEqualTo(ModalBottomSheetValue.Hidden)
scope.launch { state.expand() }
rule.waitForIdle()
- assertThat(state.swipeableState.currentValue).isEqualTo(ModalBottomSheetValue.Hidden)
+ assertThat(state.currentValue)
+ .isEqualTo(ModalBottomSheetValue.Hidden)
}
@Test
@@ -1178,10 +1182,12 @@
}
assertThat(sheetState.currentValue).isEqualTo(ModalBottomSheetValue.Hidden)
- assertThat(sheetState.swipeableState.hasAnchorForValue(ModalBottomSheetValue.HalfExpanded))
- .isFalse()
- assertThat(sheetState.swipeableState.hasAnchorForValue(ModalBottomSheetValue.Expanded))
- .isFalse()
+ assertThat(sheetState.anchoredDraggableState.hasAnchorForValue(
+ ModalBottomSheetValue.HalfExpanded
+ )).isFalse()
+ assertThat(sheetState.anchoredDraggableState.hasAnchorForValue(
+ ModalBottomSheetValue.Expanded
+ )).isFalse()
scope.launch { sheetState.show() }
rule.waitForIdle()
@@ -1213,9 +1219,11 @@
}
assertThat(sheetState.currentValue).isEqualTo(ModalBottomSheetValue.Hidden)
- assertThat(sheetState.swipeableState.hasAnchorForValue(ModalBottomSheetValue.HalfExpanded))
+ assertThat(sheetState.anchoredDraggableState
+ .hasAnchorForValue(ModalBottomSheetValue.HalfExpanded))
.isFalse()
- assertThat(sheetState.swipeableState.hasAnchorForValue(ModalBottomSheetValue.Expanded))
+ assertThat(sheetState.anchoredDraggableState
+ .hasAnchorForValue(ModalBottomSheetValue.Expanded))
.isFalse()
scope.launch { sheetState.show() }
@@ -1257,8 +1265,8 @@
sheetState =
ModalBottomSheetState(ModalBottomSheetValue.HalfExpanded, density = rule.density)
- assertThat(sheetState.swipeableState.anchors).isEmpty()
- assertThat(sheetState.swipeableState.offset).isNull()
+ assertThat(sheetState.anchoredDraggableState.anchors).isEmpty()
+ assertThat(sheetState.anchoredDraggableState.offset).isNull()
stateRestorationTester.emulateSavedInstanceStateRestore()
rule.waitForIdle()
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2AnchorTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableAnchorsTest.kt
similarity index 81%
rename from compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2AnchorTest.kt
rename to compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableAnchorsTest.kt
index 3285fcc..ea63b84 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2AnchorTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableAnchorsTest.kt
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package androidx.compose.material.swipeable
+package androidx.compose.material.anchoredDraggable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
-import androidx.compose.material.SwipeableV2State.AnchorChangedCallback
+import androidx.compose.material.AnchoredDraggableState.AnchorChangedCallback
import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.SwipeableV2Defaults.ReconcileAnimationOnAnchorChangedCallback
-import androidx.compose.material.SwipeableV2State
-import androidx.compose.material.swipeable.SwipeableTestValue.A
-import androidx.compose.material.swipeable.SwipeableTestValue.B
-import androidx.compose.material.swipeable.SwipeableTestValue.C
+import androidx.compose.material.AnchoredDraggableDefaults.ReconcileAnimationOnAnchorChangedCallback
+import androidx.compose.material.AnchoredDraggableState
+import androidx.compose.material.anchoredDraggable.AnchoredDraggableTestValue.A
+import androidx.compose.material.anchoredDraggable.AnchoredDraggableTestValue.B
+import androidx.compose.material.anchoredDraggable.AnchoredDraggableTestValue.C
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -51,15 +51,15 @@
@RunWith(AndroidJUnit4::class)
@LargeTest
@OptIn(ExperimentalMaterialApi::class)
-class SwipeableV2AnchorTest {
+class AnchoredDraggableAnchorsTest {
@get:Rule
val rule = createComposeRule()
@Test
- fun swipeable_reconcileAnchorChangeHandler_retargetsAnimationWhenOffsetChanged() {
+ fun anchoredDraggable_reconcileAnchorChangeHandler_retargetsAnimationWhenOffsetChanged() {
val animationDurationMillis = 2000
- lateinit var state: SwipeableV2State<SwipeableTestValue>
+ lateinit var state: AnchoredDraggableState<AnchoredDraggableTestValue>
lateinit var scope: CoroutineScope
val firstAnchors = mapOf(A to 0f, B to 100f, C to 200f)
@@ -71,7 +71,7 @@
rule.setContent {
state = remember {
- SwipeableV2State(
+ AnchoredDraggableState(
initialValue = A,
animationSpec = tween(animationDurationMillis, easing = LinearEasing),
positionalThreshold = defaultPositionalThreshold(),
@@ -107,8 +107,8 @@
}
@Test
- fun swipeable_reconcileAnchorChangeHandler_snapsWhenPreviousAnchorRemoved() {
- val state = SwipeableV2State(
+ fun anchoredDraggable_reconcileAnchorChangeHandler_snapsWhenPreviousAnchorRemoved() {
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = defaultPositionalThreshold(),
velocityThreshold = defaultVelocityThreshold()
@@ -141,16 +141,16 @@
}
@Test
- fun swipeable_anchorChangeHandler_calledWithUpdatedAnchorsWhenChanged() {
+ fun anchoredDraggable_anchorChangeHandler_calledWithUpdatedAnchorsWhenChanged() {
var anchorChangeHandlerInvocationCount = 0
- var actualPreviousAnchors: Map<SwipeableTestValue, Float>? = null
- var actualNewAnchors: Map<SwipeableTestValue, Float>? = null
+ var actualPreviousAnchors: Map<AnchoredDraggableTestValue, Float>? = null
+ var actualNewAnchors: Map<AnchoredDraggableTestValue, Float>? = null
val testChangeHandler = AnchorChangedCallback { _, previousAnchors, newAnchors ->
anchorChangeHandlerInvocationCount++
actualPreviousAnchors = previousAnchors
actualNewAnchors = newAnchors
}
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = defaultPositionalThreshold(),
velocityThreshold = defaultVelocityThreshold()
@@ -194,13 +194,13 @@
}
@Test
- fun swipeable_anchorChangeHandler_invokedWithPreviousTarget() {
- var recordedPreviousTargetValue: SwipeableTestValue? = null
+ fun anchoredDraggable_anchorChangeHandler_invokedWithPreviousTarget() {
+ var recordedPreviousTargetValue: AnchoredDraggableTestValue? = null
val testChangeHandler =
- AnchorChangedCallback<SwipeableTestValue> { previousTarget, _, _ ->
+ AnchorChangedCallback<AnchoredDraggableTestValue> { previousTarget, _, _ ->
recordedPreviousTargetValue = previousTarget
}
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = { totalDistance -> totalDistance * 0.5f },
velocityThreshold = defaultVelocityThreshold()
@@ -220,12 +220,12 @@
}
@Test
- fun swipeable_anchorChangeHandler_invokedIfInitialValueNotInInitialAnchors() {
+ fun anchoredDraggable_anchorChangeHandler_invokedIfInitialValueNotInInitialAnchors() {
var anchorChangeHandlerInvocationCount = 0
- val testChangeHandler = AnchorChangedCallback<SwipeableTestValue> { _, _, _ ->
+ val testChangeHandler = AnchorChangedCallback<AnchoredDraggableTestValue> { _, _, _ ->
anchorChangeHandlerInvocationCount++
}
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = defaultPositionalThreshold(),
velocityThreshold = defaultVelocityThreshold()
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2GestureTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableGestureTest.kt
similarity index 84%
rename from compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2GestureTest.kt
rename to compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableGestureTest.kt
index cf2c3f5..d27b292 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2GestureTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableGestureTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 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,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.material.swipeable
+package androidx.compose.material.anchoredDraggable
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
@@ -24,11 +24,11 @@
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.material.AutoTestFrameClock
import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.SwipeableV2State
-import androidx.compose.material.swipeable.SwipeableTestValue.A
-import androidx.compose.material.swipeable.SwipeableTestValue.B
-import androidx.compose.material.swipeable.SwipeableTestValue.C
-import androidx.compose.material.swipeableV2
+import androidx.compose.material.AnchoredDraggableState
+import androidx.compose.material.anchoredDraggable.AnchoredDraggableTestValue.A
+import androidx.compose.material.anchoredDraggable.AnchoredDraggableTestValue.B
+import androidx.compose.material.anchoredDraggable.AnchoredDraggableTestValue.C
+import androidx.compose.material.anchoredDraggable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.testutils.WithTouchSlop
@@ -64,17 +64,17 @@
@RunWith(AndroidJUnit4::class)
@LargeTest
@OptIn(ExperimentalMaterialApi::class)
-class SwipeableV2GestureTest {
+class AnchoredDraggableGestureTest {
@get:Rule
val rule = createComposeRule()
- private val SwipeableTestTag = "swipebox"
- private val SwipeableBoxSize = 200.dp
+ private val AnchoredDraggableTestTag = "dragbox"
+ private val AnchoredDraggableBoxSize = 200.dp
@Test
- fun swipeable_swipe_horizontal() {
- val state = SwipeableV2State(
+ fun anchoredDraggable_swipe_horizontal() {
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = DefaultPositionalThreshold,
velocityThreshold = DefaultVelocityThreshold
@@ -92,9 +92,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -114,27 +114,27 @@
assertThat(state.currentValue).isEqualTo(A)
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeRight(endX = right / 2) }
rule.waitForIdle()
assertThat(state.currentValue).isEqualTo(B)
assertThat(state.offset).isEqualTo(anchors.getValue(B))
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeRight(startX = right / 2, endX = right) }
rule.waitForIdle()
assertThat(state.currentValue).isEqualTo(C)
assertThat(state.offset).isEqualTo(anchors.getValue(C))
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeLeft(endX = right / 2) }
rule.waitForIdle()
assertThat(state.currentValue).isEqualTo(B)
assertThat(state.offset).isEqualTo(anchors.getValue(B))
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeLeft(startX = right / 2) }
rule.waitForIdle()
@@ -143,8 +143,8 @@
}
@Test
- fun swipeable_swipe_vertical() {
- val state = SwipeableV2State(
+ fun anchoredDraggable_swipe_vertical() {
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = DefaultPositionalThreshold,
velocityThreshold = DefaultVelocityThreshold
@@ -162,9 +162,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Vertical
)
@@ -184,27 +184,27 @@
assertThat(state.currentValue).isEqualTo(A)
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeDown(startY = top, endY = bottom / 2) }
rule.waitForIdle()
assertThat(state.currentValue).isEqualTo(B)
assertThat(state.offset).isEqualTo(anchors.getValue(B))
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeDown(startY = bottom / 2, endY = bottom) }
rule.waitForIdle()
assertThat(state.currentValue).isEqualTo(C)
assertThat(state.offset).isEqualTo(anchors.getValue(C))
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeUp(startY = bottom, endY = bottom / 2) }
rule.waitForIdle()
assertThat(state.currentValue).isEqualTo(B)
assertThat(state.offset).isEqualTo(anchors.getValue(B))
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeUp(startY = bottom / 2, endY = top) }
rule.waitForIdle()
@@ -213,8 +213,8 @@
}
@Test
- fun swipeable_swipe_disabled_horizontal() {
- val state = SwipeableV2State(
+ fun anchoredDraggable_swipe_disabled_horizontal() {
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = DefaultPositionalThreshold,
velocityThreshold = DefaultVelocityThreshold
@@ -232,9 +232,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal,
enabled = false
@@ -255,7 +255,7 @@
assertThat(state.currentValue).isEqualTo(A)
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeRight(startX = left, endX = right) }
rule.waitForIdle()
@@ -264,8 +264,8 @@
}
@Test
- fun swipeable_swipe_disabled_vertical() {
- val state = SwipeableV2State(
+ fun anchoredDraggable_swipe_disabled_vertical() {
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = DefaultPositionalThreshold,
velocityThreshold = DefaultVelocityThreshold
@@ -283,9 +283,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Vertical,
enabled = false
@@ -306,7 +306,7 @@
assertThat(state.currentValue).isEqualTo(A)
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeDown(startY = top, endY = bottom) }
rule.waitForIdle()
@@ -315,10 +315,10 @@
}
@Test
- fun swipeable_positionalThresholds_fractional_targetState() {
+ fun anchoredDraggable_positionalThresholds_fractional_targetState() {
val positionalThreshold = 0.5f
val absThreshold = abs(positionalThreshold)
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = { totalDistance -> totalDistance * positionalThreshold },
velocityThreshold = DefaultVelocityThreshold
@@ -327,9 +327,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -394,10 +394,10 @@
}
@Test
- fun swipeable_positionalThresholds_fractional_negativeThreshold_targetState() {
+ fun anchoredDraggable_positionalThresholds_fractional_negativeThreshold_targetState() {
val positionalThreshold = -0.5f
val absThreshold = abs(positionalThreshold)
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = { totalDistance -> totalDistance * positionalThreshold },
velocityThreshold = DefaultVelocityThreshold
@@ -406,9 +406,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -473,11 +473,11 @@
}
@Test
- fun swipeable_positionalThresholds_fixed_targetState() {
+ fun anchoredDraggable_positionalThresholds_fixed_targetState() {
val positionalThreshold = 56.dp
val positionalThresholdPx = with(rule.density) { positionalThreshold.toPx() }
val absThreshold = abs(positionalThresholdPx)
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = { positionalThresholdPx },
velocityThreshold = DefaultVelocityThreshold
@@ -486,9 +486,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -556,11 +556,11 @@
}
@Test
- fun swipeable_positionalThresholds_fixed_negativeThreshold_targetState() {
+ fun anchoredDraggable_positionalThresholds_fixed_negativeThreshold_targetState() {
val positionalThreshold = (-56).dp
val positionalThresholdPx = with(rule.density) { positionalThreshold.toPx() }
val absThreshold = abs(positionalThresholdPx)
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = { positionalThresholdPx },
velocityThreshold = DefaultVelocityThreshold
@@ -569,9 +569,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -639,11 +639,11 @@
}
@Test
- fun swipeable_velocityThreshold_settle_velocityHigherThanThreshold_advances() =
+ fun anchoredDraggable_velocityThreshold_settle_velocityHigherThanThreshold_advances() =
runBlocking(AutoTestFrameClock()) {
val velocity = 100.dp
val velocityPx = with(rule.density) { velocity.toPx() }
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = DefaultPositionalThreshold,
velocityThreshold = { velocityPx / 2f }
@@ -662,11 +662,11 @@
}
@Test
- fun swipeable_velocityThreshold_settle_velocityLowerThanThreshold_doesntAdvance() =
+ fun anchoredDraggable_velocityThreshold_settle_velocityLowerThanThreshold_doesntAdvance() =
runBlocking(AutoTestFrameClock()) {
val velocity = 100.dp
val velocityPx = with(rule.density) { velocity.toPx() }
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
velocityThreshold = { velocityPx },
positionalThreshold = { Float.POSITIVE_INFINITY }
@@ -684,9 +684,9 @@
}
@Test
- fun swipeable_velocityThreshold_swipe_velocityHigherThanThreshold_advances() {
+ fun anchoredDraggable_velocityThreshold_swipe_velocityHigherThanThreshold_advances() {
val velocityThreshold = 100.dp
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = DefaultPositionalThreshold,
velocityThreshold = { with(rule.density) { velocityThreshold.toPx() } }
@@ -695,9 +695,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -721,7 +721,7 @@
}
}
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput {
swipeWithVelocity(
start = Offset(left, 0f),
@@ -735,9 +735,9 @@
}
@Test
- fun swipeable_velocityThreshold_swipe_velocityLowerThanThreshold_doesntAdvance() {
+ fun anchoredDraggable_velocityThreshold_swipe_velocityLowerThanThreshold_doesntAdvance() {
val velocityThreshold = 100.dp
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
velocityThreshold = { with(rule.density) { velocityThreshold.toPx() } },
positionalThreshold = { Float.POSITIVE_INFINITY }
@@ -746,9 +746,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -772,7 +772,7 @@
}
}
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput {
swipeWithVelocity(
start = Offset(left, 0f),
@@ -786,12 +786,12 @@
}
@Test
- fun swipeable_dragBeyondBounds_clampsAndSwipesBack() {
+ fun anchoredDraggable_dragBeyondBounds_clampsAndSwipesBack() {
val anchors = mapOf(
A to 0f,
C to 500f
)
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = DefaultPositionalThreshold,
velocityThreshold = { 0f }
@@ -801,9 +801,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -822,7 +822,7 @@
val overdrag = 100f
val maxBound = state.anchors.getValue(C)
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput {
down(Offset(0f, 0f))
moveBy(Offset(x = maxBound + overdrag, y = 0f))
@@ -839,14 +839,14 @@
}
@Test
- fun swipeable_animationCancelledByDrag_resetsTargetValueToClosest() {
+ fun anchoredDraggable_animationCancelledByDrag_resetsTargetValueToClosest() {
rule.mainClock.autoAdvance = false
val anchors = mapOf(
A to 0f,
B to 250f,
C to 500f
)
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = { totalDistance -> totalDistance * 0.5f },
velocityThreshold = DefaultVelocityThreshold
@@ -859,9 +859,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -888,7 +888,7 @@
} // Advance until our closest anchor is B
assertThat(state.targetValue).isEqualTo(C)
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput {
down(Offset.Zero)
}
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2StateTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableStateTest.kt
similarity index 77%
rename from compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2StateTest.kt
rename to compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableStateTest.kt
index 4bba7ee..e0f833e6 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2StateTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableStateTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 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,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.material.swipeable
+package androidx.compose.material.anchoredDraggable
import androidx.compose.animation.core.FloatSpringSpec
import androidx.compose.animation.core.LinearEasing
@@ -26,15 +26,15 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.requiredSize
-import androidx.compose.material.SwipeableV2State.AnchorChangedCallback
+import androidx.compose.material.AnchoredDraggableState.AnchorChangedCallback
import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.SwipeableV2Defaults
-import androidx.compose.material.SwipeableV2State
-import androidx.compose.material.rememberSwipeableV2State
-import androidx.compose.material.swipeable.SwipeableTestValue.A
-import androidx.compose.material.swipeable.SwipeableTestValue.B
-import androidx.compose.material.swipeable.SwipeableTestValue.C
-import androidx.compose.material.swipeableV2
+import androidx.compose.material.AnchoredDraggableDefaults
+import androidx.compose.material.AnchoredDraggableState
+import androidx.compose.material.rememberAnchoredDraggableState
+import androidx.compose.material.anchoredDraggable.AnchoredDraggableTestValue.A
+import androidx.compose.material.anchoredDraggable.AnchoredDraggableTestValue.B
+import androidx.compose.material.anchoredDraggable.AnchoredDraggableTestValue.C
+import androidx.compose.material.anchoredDraggable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MonotonicFrameClock
import androidx.compose.runtime.SideEffect
@@ -75,25 +75,25 @@
@RunWith(AndroidJUnit4::class)
@LargeTest
@OptIn(ExperimentalMaterialApi::class)
-class SwipeableV2StateTest {
+class AnchoredDraggableStateTest {
@get:Rule
val rule = createComposeRule()
- private val SwipeableTestTag = "swipebox"
- private val SwipeableBoxSize = 200.dp
+ private val AnchoredDraggableTestTag = "dragbox"
+ private val AnchoredDraggableBoxSize = 200.dp
@Test
- fun swipeable_state_canSkipStateByFling() {
- lateinit var state: SwipeableV2State<SwipeableTestValue>
+ fun anchoredDraggable_state_canSkipStateByFling() {
+ lateinit var state: AnchoredDraggableState<AnchoredDraggableTestValue>
rule.setContent {
- state = rememberSwipeableV2State(initialValue = A)
+ state = rememberAnchoredDraggableState(initialValue = A)
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Vertical
)
@@ -118,7 +118,7 @@
}
}
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeDown() }
rule.waitForIdle()
@@ -127,16 +127,16 @@
}
@Test
- fun swipeable_targetState_updatedOnSwipe() {
- lateinit var state: SwipeableV2State<SwipeableTestValue>
+ fun anchoredDraggable_targetState_updatedOnSwipe() {
+ lateinit var state: AnchoredDraggableState<AnchoredDraggableTestValue>
rule.setContent {
- state = rememberSwipeableV2State(initialValue = A)
+ state = rememberAnchoredDraggableState(initialValue = A)
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Vertical
)
@@ -161,45 +161,45 @@
}
}
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeDown(endY = bottom * 0.45f) }
rule.waitForIdle()
assertThat(state.targetValue).isEqualTo(B)
// Assert that swipe below threshold upward settles at current state
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeUp(endY = bottom * 0.95f, durationMillis = 1000) }
rule.waitForIdle()
assertThat(state.targetValue).isEqualTo(B)
// Assert that swipe below threshold downward settles at current state
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeDown(endY = bottom * 0.05f) }
rule.waitForIdle()
assertThat(state.targetValue).isEqualTo(B)
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeDown(endY = bottom * 0.9f) }
rule.waitForIdle()
assertThat(state.targetValue).isEqualTo(C)
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeUp(endY = top * 1.1f) }
rule.waitForIdle()
assertThat(state.targetValue).isEqualTo(A)
}
@Test
- fun swipeable_targetState_updatedWithAnimation() {
+ fun anchoredDraggable_targetState_updatedWithAnimation() {
rule.mainClock.autoAdvance = false
val animationDuration = 300
val frameLengthMillis = 16L
- lateinit var state: SwipeableV2State<SwipeableTestValue>
+ lateinit var state: AnchoredDraggableState<AnchoredDraggableTestValue>
lateinit var scope: CoroutineScope
rule.setContent {
- val velocityThreshold = SwipeableV2Defaults.velocityThreshold
+ val velocityThreshold = AnchoredDraggableDefaults.velocityThreshold
state = remember(velocityThreshold) {
- SwipeableV2State(
+ AnchoredDraggableState(
initialValue = A,
animationSpec = tween(animationDuration, easing = LinearEasing),
positionalThreshold = { distance -> distance * 0.5f },
@@ -210,9 +210,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Vertical
)
@@ -261,17 +261,17 @@
}
@Test
- fun swipeable_progress_matchesSwipePosition() {
- lateinit var state: SwipeableV2State<SwipeableTestValue>
+ fun anchoredDraggable_progress_matchesSwipePosition() {
+ lateinit var state: AnchoredDraggableState<AnchoredDraggableTestValue>
rule.setContent {
- state = rememberSwipeableV2State(initialValue = A)
+ state = rememberAnchoredDraggableState(initialValue = A)
WithTouchSlop(touchSlop = 0f) {
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Vertical
)
@@ -302,7 +302,7 @@
val almostAnchorB = anchorB * 0.9f
var expectedProgress = almostAnchorB / (anchorB - anchorA)
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeDown(endY = almostAnchorB) }
assertThat(state.targetValue).isEqualTo(B)
@@ -311,7 +311,7 @@
val almostAnchorA = anchorA + ((anchorB - anchorA) * 0.1f)
expectedProgress = 1 - (almostAnchorA / (anchorB - anchorA))
- rule.onNodeWithTag(SwipeableTestTag)
+ rule.onNodeWithTag(AnchoredDraggableTestTag)
.performTouchInput { swipeUp(startY = anchorB, endY = almostAnchorA) }
assertThat(state.targetValue).isEqualTo(A)
@@ -319,16 +319,16 @@
}
@Test
- fun swipeable_snapTo_updatesImmediately() = runBlocking {
- lateinit var state: SwipeableV2State<SwipeableTestValue>
+ fun anchoredDraggable_snapTo_updatesImmediately() = runBlocking {
+ lateinit var state: AnchoredDraggableState<AnchoredDraggableTestValue>
rule.setContent {
- state = rememberSwipeableV2State(initialValue = A)
+ state = rememberAnchoredDraggableState(initialValue = A)
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Vertical
)
@@ -359,16 +359,16 @@
}
@Test
- fun swipeable_rememberSwipeableState_restored() {
+ fun anchoredDraggable_rememberanchoredDraggableState_restored() {
val restorationTester = StateRestorationTester(rule)
val initialState = C
val animationSpec = tween<Float>(durationMillis = 1000)
- lateinit var state: SwipeableV2State<SwipeableTestValue>
+ lateinit var state: AnchoredDraggableState<AnchoredDraggableTestValue>
lateinit var scope: CoroutineScope
restorationTester.setContent {
- state = rememberSwipeableV2State(initialState, animationSpec)
+ state = rememberAnchoredDraggableState(initialState, animationSpec)
SideEffect {
state.updateAnchors(mapOf(A to 0f, B to 100f, C to 200f))
}
@@ -391,19 +391,19 @@
}
@Test
- fun swipeable_targetState_accessedInInitialComposition() {
- lateinit var targetState: SwipeableTestValue
+ fun anchoredDraggable_targetState_accessedInInitialComposition() {
+ lateinit var targetState: AnchoredDraggableTestValue
rule.setContent {
- val state = rememberSwipeableV2State(initialValue = B)
+ val state = rememberAnchoredDraggableState(initialValue = B)
LaunchedEffect(state.targetValue) {
targetState = state.targetValue
}
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -432,19 +432,19 @@
}
@Test
- fun swipeable_progress_accessedInInitialComposition() {
+ fun anchoredDraggable_progress_accessedInInitialComposition() {
var progress = Float.NaN
rule.setContent {
- val state = rememberSwipeableV2State(initialValue = B)
+ val state = rememberAnchoredDraggableState(initialValue = B)
LaunchedEffect(state.progress) {
progress = state.progress
}
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -474,18 +474,18 @@
@Test
@Ignore("Todo: Fix differences between tests and real code - this shouldn't work :)")
- fun swipeable_requireOffset_accessedInInitialComposition_throws() {
+ fun anchoredDraggable_requireOffset_accessedInInitialComposition_throws() {
var exception: Throwable? = null
- lateinit var state: SwipeableV2State<SwipeableTestValue>
+ lateinit var state: AnchoredDraggableState<AnchoredDraggableTestValue>
var offset: Float? = null
rule.setContent {
- state = rememberSwipeableV2State(initialValue = B)
+ state = rememberAnchoredDraggableState(initialValue = B)
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Horizontal
)
@@ -520,10 +520,10 @@
@Test
@Ignore("LaunchedEffects execute instantly in tests. How can we delay?")
- fun swipeable_requireOffset_accessedInEffect_doesntThrow() {
+ fun anchoredDraggable_requireOffset_accessedInEffect_doesntThrow() {
var exception: Throwable? = null
rule.setContent {
- val state = rememberSwipeableV2State(initialValue = B)
+ val state = rememberAnchoredDraggableState(initialValue = B)
LaunchedEffect(Unit) {
exception = runCatching { state.requireOffset() }.exceptionOrNull()
}
@@ -533,7 +533,7 @@
}
@Test
- fun swipeable_animateTo_animatesBeyondBounds() {
+ fun anchoredDraggable_animateTo_animatesBeyondBounds() {
rule.mainClock.autoAdvance = false
val minBound = 0f
val maxBound = 500f
@@ -549,12 +549,12 @@
initialVelocity = 0f
).let { TimeUnit.NANOSECONDS.toMillis(it) }
- lateinit var state: SwipeableV2State<SwipeableTestValue>
+ lateinit var state: AnchoredDraggableState<AnchoredDraggableTestValue>
lateinit var scope: CoroutineScope
rule.setContent {
scope = rememberCoroutineScope()
- state = rememberSwipeableV2State(
+ state = rememberAnchoredDraggableState(
initialValue = A,
animationSpec = animationSpec
)
@@ -564,9 +564,9 @@
Box(Modifier.fillMaxSize()) {
Box(
Modifier
- .requiredSize(SwipeableBoxSize)
- .testTag(SwipeableTestTag)
- .swipeableV2(
+ .requiredSize(AnchoredDraggableBoxSize)
+ .testTag(AnchoredDraggableTestTag)
+ .anchoredDraggable(
state = state,
orientation = Orientation.Vertical
)
@@ -594,10 +594,10 @@
}
@Test
- fun swipeable_bounds_minBoundIsSmallestAnchor() {
+ fun anchoredDraggable_bounds_minBoundIsSmallestAnchor() {
var minBound = 0f
var maxBound = 500f
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = defaultPositionalThreshold,
velocityThreshold = defaultVelocityThreshold
@@ -630,8 +630,8 @@
}
@Test
- fun swipeable_targetNotInAnchors_animateTo_updatesCurrentValue() {
- val state = SwipeableV2State(
+ fun anchoredDraggable_targetNotInAnchors_animateTo_updatesCurrentValue() {
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = defaultPositionalThreshold,
velocityThreshold = defaultVelocityThreshold
@@ -643,8 +643,8 @@
}
@Test
- fun swipeable_targetNotInAnchors_snapTo_updatesCurrentValue() {
- val state = SwipeableV2State(
+ fun anchoredDraggable_targetNotInAnchors_snapTo_updatesCurrentValue() {
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = defaultPositionalThreshold,
velocityThreshold = defaultVelocityThreshold
@@ -656,12 +656,13 @@
}
@Test
- fun swipeable_updateAnchors_initialUpdate_initialValueInAnchors_shouldntUpdate() {
+ fun anchoredDraggable_updateAnchors_initialUpdate_initialValueInAnchors_shouldntUpdate() {
var anchorChangeHandlerInvoked = false
- val testAnchorChangeHandler = AnchorChangedCallback<SwipeableTestValue> { _, _, _ ->
+ val testAnchorChangeHandler =
+ AnchorChangedCallback<AnchoredDraggableTestValue> { _, _, _ ->
anchorChangeHandlerInvoked = true
}
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = defaultPositionalThreshold,
velocityThreshold = defaultVelocityThreshold
@@ -672,12 +673,13 @@
}
@Test
- fun swipeable_updateAnchors_initialUpdate_initialValueNotInAnchors_shouldUpdate() {
+ fun anchoredDraggable_updateAnchors_initialUpdate_initialValueNotInAnchors_shouldUpdate() {
var anchorChangeHandlerInvoked = false
- val testAnchorChangedCallback = AnchorChangedCallback<SwipeableTestValue> { _, _, _ ->
+ val testAnchorChangedCallback =
+ AnchorChangedCallback<AnchoredDraggableTestValue> { _, _, _ ->
anchorChangeHandlerInvoked = true
}
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = defaultPositionalThreshold,
velocityThreshold = defaultVelocityThreshold
@@ -688,12 +690,13 @@
}
@Test
- fun swipeable_updateAnchors_updateExistingAnchors_shouldUpdate() {
+ fun anchoredDraggable_updateAnchors_updateExistingAnchors_shouldUpdate() {
var anchorChangeHandlerInvoked = false
- val testAnchorChangedCallback = AnchorChangedCallback<SwipeableTestValue> { _, _, _ ->
+ val testAnchorChangedCallback =
+ AnchorChangedCallback<AnchoredDraggableTestValue> { _, _, _ ->
anchorChangeHandlerInvoked = true
}
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
positionalThreshold = defaultPositionalThreshold,
velocityThreshold = defaultVelocityThreshold
@@ -708,16 +711,17 @@
}
@Test
- fun swipeable_updateAnchors_ongoingOffsetMutation_shouldNotUpdate() = runBlocking {
+ fun anchoredDraggable_updateAnchors_ongoingOffsetMutation_shouldNotUpdate() = runBlocking {
val clock = HandPumpTestFrameClock()
val animationScope = CoroutineScope(clock)
val animationDuration = 2000
var anchorChangeHandlerInvoked = false
- val testAnchorChangedCallback = AnchorChangedCallback<SwipeableTestValue> { _, _, _ ->
+ val testAnchorChangedCallback =
+ AnchorChangedCallback<AnchoredDraggableTestValue> { _, _, _ ->
anchorChangeHandlerInvoked = true
}
- val state = SwipeableV2State(
+ val state = AnchoredDraggableState(
initialValue = A,
animationSpec = tween(animationDuration),
positionalThreshold = defaultPositionalThreshold,
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableTestValue.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableTestValue.kt
similarity index 86%
rename from compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableTestValue.kt
rename to compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableTestValue.kt
index e24c752..16bfc99 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableTestValue.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/anchoredDraggable/AnchoredDraggableTestValue.kt
@@ -16,8 +16,8 @@
@file:OptIn(ExperimentalMaterialApi::class)
-package androidx.compose.material.swipeable
+package androidx.compose.material.anchoredDraggable
import androidx.compose.material.ExperimentalMaterialApi
-internal enum class SwipeableTestValue { A, B, C }
\ No newline at end of file
+internal enum class AnchoredDraggableTestValue { A, B, C }
\ No newline at end of file
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
similarity index 84%
rename from compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt
rename to compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
index b467da0..b3a2bfe 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
@@ -26,7 +26,7 @@
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.offset
-import androidx.compose.material.SwipeableV2State.AnchorChangedCallback
+import androidx.compose.material.AnchoredDraggableState.AnchorChangedCallback
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
@@ -45,27 +45,27 @@
import kotlinx.coroutines.launch
/**
- * Enable swipe gestures between a set of predefined values.
+ * Enable drag gestures between a set of predefined values.
*
- * When a swipe is detected, the offset of the [SwipeableV2State] will be updated with the swipe
+ * When a drag is detected, the offset of the [AnchoredDraggableState] will be updated with the drag
* delta. You should use this offset to move your content accordingly (see [Modifier.offset]).
- * When the swipe ends, the offset will be animated to one of the anchors and when that anchor is
- * reached, the value of the [SwipeableV2State] will also be updated to the value corresponding to
- * the new anchor.
+ * When the drag ends, the offset will be animated to one of the anchors and when that anchor is
+ * reached, the value of the [AnchoredDraggableState] will also be updated to the value
+ * corresponding to the new anchor.
*
- * Swiping is constrained between the minimum and maximum anchors.
+ * Dragging is constrained between the minimum and maximum anchors.
*
- * @param state The associated [SwipeableV2State].
- * @param orientation The orientation in which the swipeable can be swiped.
- * @param enabled Whether this [swipeableV2] is enabled and should react to the user's input.
- * @param reverseDirection Whether to reverse the direction of the swipe, so a top to bottom
- * swipe will behave like bottom to top, and a left to right swipe will behave like right to left.
+ * @param state The associated [AnchoredDraggableState].
+ * @param orientation The orientation in which the [anchoredDraggable] can be dragged.
+ * @param enabled Whether this [anchoredDraggable] is enabled and should react to the user's input.
+ * @param reverseDirection Whether to reverse the direction of the drag, so a top to bottom
+ * drag will behave like bottom to top, and a left to right drag will behave like right to left.
* @param interactionSource Optional [MutableInteractionSource] that will passed on to
* the internal [Modifier.draggable].
*/
@ExperimentalMaterialApi
-internal fun <T> Modifier.swipeableV2(
- state: SwipeableV2State<T>,
+internal fun <T> Modifier.anchoredDraggable(
+ state: AnchoredDraggableState<T>,
orientation: Orientation,
enabled: Boolean = true,
reverseDirection: Boolean = false,
@@ -81,17 +81,17 @@
)
/**
- * State of the [swipeableV2] modifier.
+ * State of the [anchoredDraggable] modifier.
*
- * This contains necessary information about any ongoing swipe or animation and provides methods
+ * This contains necessary information about any ongoing drag or animation and provides methods
* to change the state either immediately or by starting an animation. To create and remember a
- * [SwipeableV2State] use [rememberSwipeableV2State].
+ * [AnchoredDraggableState] use [rememberAnchoredDraggableState].
*
* @param initialValue The initial value of the state.
* @param animationSpec The default animation that will be used to animate to a new state.
* @param confirmValueChange Optional callback invoked to confirm or veto a pending state change.
* @param positionalThreshold The positional threshold, in px, to be used when calculating the
- * target state while a swipe is in progress and when settling after the swipe ends. This is the
+ * target state while a drag is in progress and when settling after the drag ends. This is the
* distance from the start of a transition. It will be, depending on the direction of the
* interaction, added or subtracted from/to the origin offset. It should always be a positive value.
* @param velocityThreshold The velocity threshold (in px per second) that the end velocity has to
@@ -100,20 +100,20 @@
*/
@Stable
@ExperimentalMaterialApi
-internal class SwipeableV2State<T>(
+internal class AnchoredDraggableState<T>(
initialValue: T,
internal val positionalThreshold: (totalDistance: Float) -> Float,
internal val velocityThreshold: () -> Float,
- internal val animationSpec: AnimationSpec<Float> = SwipeableV2Defaults.AnimationSpec,
+ internal val animationSpec: AnimationSpec<Float> = AnchoredDraggableDefaults.AnimationSpec,
internal val confirmValueChange: (newValue: T) -> Boolean = { true }
) {
- private val swipeMutex = InternalMutatorMutex()
+ private val dragMutex = InternalMutatorMutex()
internal val swipeDraggableState = object : DraggableState {
private val dragScope = object : DragScope {
override fun dragBy(pixels: Float) {
- [email protected](pixels)
+ [email protected](pixels)
}
}
@@ -121,16 +121,16 @@
dragPriority: MutatePriority,
block: suspend DragScope.() -> Unit
) {
- swipe(dragPriority) { dragScope.block() }
+ [email protected](dragPriority) { dragScope.block() }
}
override fun dispatchRawDelta(delta: Float) {
- [email protected](delta)
+ [email protected](delta)
}
}
/**
- * The current value of the [SwipeableV2State].
+ * The current value of the [AnchoredDraggableState].
*/
var currentValue: T by mutableStateOf(initialValue)
private set
@@ -153,7 +153,7 @@
* The current offset, or null if it has not been initialized yet.
*
* The offset will be initialized during the first measurement phase of the node that the
- * [swipeableV2] modifier is attached to. These are the phases:
+ * [anchoredDraggable] modifier is attached to. These are the phases:
* Composition { -> Effects } -> Layout { Measurement -> Placement } -> Drawing
* During the first composition, the offset will be null. In subsequent compositions, the offset
* will be derived from the anchors of the previous pass.
@@ -277,7 +277,7 @@
* @param targetValue The target value of the animation
*/
suspend fun snapTo(targetValue: T) {
- swipe { snap(targetValue) }
+ drag { snap(targetValue) }
}
/**
@@ -298,7 +298,7 @@
val targetOffset = anchors[targetValue]
if (targetOffset != null) {
try {
- swipe {
+ drag {
animationTarget = targetValue
var prev = offset ?: 0f
animate(prev, targetOffset, velocity, animationSpec) { value, velocity ->
@@ -345,9 +345,9 @@
}
/**
- * Swipe by the [delta], coerce it in the bounds and dispatch it to the [SwipeableV2State].
+ * Drag by the [delta], coerce it in the bounds and dispatch it to the [AnchoredDraggableState].
*
- * @return The delta the consumed by the [SwipeableV2State]
+ * @return The delta the consumed by the [AnchoredDraggableState]
*/
fun dispatchRawDelta(delta: Float): Float {
val currentDragPosition = offset ?: 0f
@@ -401,19 +401,19 @@
}
}
- private suspend fun swipe(
- swipePriority: MutatePriority = MutatePriority.Default,
+ private suspend fun drag(
+ priority: MutatePriority = MutatePriority.Default,
action: suspend () -> Unit
- ): Unit = coroutineScope { swipeMutex.mutate(swipePriority, action) }
+ ): Unit = coroutineScope { dragMutex.mutate(priority, action) }
/**
- * Attempt to snap synchronously. Snapping can happen synchronously when there is no other swipe
+ * Attempt to snap synchronously. Snapping can happen synchronously when there is no other drag
* transaction like a drag or an animation is progress. If there is another interaction in
* progress, the suspending [snapTo] overload needs to be used.
*
* @return true if the synchronous snap was successful, or false if we couldn't snap synchronous
*/
- internal fun trySnapTo(targetValue: T): Boolean = swipeMutex.tryMutate { snap(targetValue) }
+ internal fun trySnapTo(targetValue: T): Boolean = dragMutex.tryMutate { snap(targetValue) }
private fun snap(targetValue: T) {
val targetOffset = anchors[targetValue]
@@ -428,7 +428,7 @@
companion object {
/**
- * The default [Saver] implementation for [SwipeableV2State].
+ * The default [Saver] implementation for [AnchoredDraggableState].
*/
@ExperimentalMaterialApi
fun <T : Any> Saver(
@@ -436,10 +436,10 @@
confirmValueChange: (T) -> Boolean,
positionalThreshold: (distance: Float) -> Float,
velocityThreshold: () -> Float
- ) = Saver<SwipeableV2State<T>, T>(
+ ) = Saver<AnchoredDraggableState<T>, T>(
save = { it.currentValue },
restore = {
- SwipeableV2State(
+ AnchoredDraggableState(
initialValue = it,
animationSpec = animationSpec,
confirmValueChange = confirmValueChange,
@@ -456,16 +456,16 @@
* Components with custom reconciliation logic should implement this callback, for example to
* re-target an in-progress animation when the anchors change.
*
- * @see SwipeableV2Defaults.ReconcileAnimationOnAnchorChangedCallback for a default
+ * @see AnchoredDraggableDefaults.ReconcileAnimationOnAnchorChangedCallback for a default
* implementation
*/
@ExperimentalMaterialApi
fun interface AnchorChangedCallback<T> {
/**
- * Callback that is invoked when the anchors have changed, after the [SwipeableV2State] has
- * been updated with them. Use this hook to re-launch animations or interrupt them if
- * needed.
+ * Callback that is invoked when the anchors have changed, after the
+ * [AnchoredDraggableState] has been updated with them. Use this hook to re-launch
+ * animations or interrupt them if needed.
*
* @param previousTargetValue The target value before the anchors were updated
* @param previousAnchors The previously set anchors
@@ -480,7 +480,7 @@
}
/**
- * Create and remember a [SwipeableV2State].
+ * Create and remember a [AnchoredDraggableState].
*
* @param initialValue The initial value.
* @param animationSpec The default animation that will be used to animate to a new value.
@@ -488,23 +488,23 @@
*/
@Composable
@ExperimentalMaterialApi
-internal fun <T : Any> rememberSwipeableV2State(
+internal fun <T : Any> rememberAnchoredDraggableState(
initialValue: T,
- animationSpec: AnimationSpec<Float> = SwipeableV2Defaults.AnimationSpec,
+ animationSpec: AnimationSpec<Float> = AnchoredDraggableDefaults.AnimationSpec,
confirmValueChange: (newValue: T) -> Boolean = { true }
-): SwipeableV2State<T> {
- val positionalThreshold = SwipeableV2Defaults.positionalThreshold
- val velocityThreshold = SwipeableV2Defaults.velocityThreshold
+): AnchoredDraggableState<T> {
+ val positionalThreshold = AnchoredDraggableDefaults.positionalThreshold
+ val velocityThreshold = AnchoredDraggableDefaults.velocityThreshold
return rememberSaveable(
initialValue, animationSpec, confirmValueChange, positionalThreshold, velocityThreshold,
- saver = SwipeableV2State.Saver(
+ saver = AnchoredDraggableState.Saver(
animationSpec = animationSpec,
confirmValueChange = confirmValueChange,
positionalThreshold = positionalThreshold,
velocityThreshold = velocityThreshold
),
) {
- SwipeableV2State(
+ AnchoredDraggableState(
initialValue = initialValue,
animationSpec = animationSpec,
confirmValueChange = confirmValueChange,
@@ -515,27 +515,34 @@
}
/**
- * Contains useful defaults for [swipeableV2] and [SwipeableV2State].
+ * Contains useful defaults for [anchoredDraggable] and [AnchoredDraggableState].
*/
@Stable
@ExperimentalMaterialApi
-internal object SwipeableV2Defaults {
+internal object AnchoredDraggableDefaults {
/**
- * The default animation used by [SwipeableV2State].
+ * The default animation used by [AnchoredDraggableState].
*/
+ @get:ExperimentalMaterialApi
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@ExperimentalMaterialApi
val AnimationSpec = SpringSpec<Float>()
/**
- * The default velocity threshold (1.8 dp per millisecond) used by [rememberSwipeableV2State].
+ * The default velocity threshold (1.8 dp per millisecond) used by
+ * [rememberAnchoredDraggableState].
*/
+ @get:ExperimentalMaterialApi
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@ExperimentalMaterialApi
val velocityThreshold: () -> Float
@Composable get() = with(LocalDensity.current) { { 125.dp.toPx() } }
/**
- * The default positional threshold (56 dp) used by [rememberSwipeableV2State]
+ * The default positional threshold (56 dp) used by [rememberAnchoredDraggableState]
*/
+ @get:ExperimentalMaterialApi
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@ExperimentalMaterialApi
val positionalThreshold: (totalDistance: Float) -> Float
@Composable get() = with(LocalDensity.current) {
@@ -552,7 +559,7 @@
*/
@ExperimentalMaterialApi
internal fun <T> ReconcileAnimationOnAnchorChangedCallback(
- state: SwipeableV2State<T>,
+ state: AnchoredDraggableState<T>,
scope: CoroutineScope
) = AnchorChangedCallback<T> { previousTarget, previousAnchors, newAnchors ->
val previousTargetOffset = previousAnchors[previousTarget]
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
index 93603b9..c674017 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
@@ -25,7 +25,7 @@
import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.material.BottomSheetValue.Collapsed
import androidx.compose.material.BottomSheetValue.Expanded
-import androidx.compose.material.SwipeableV2State.AnchorChangedCallback
+import androidx.compose.material.AnchoredDraggableState.AnchorChangedCallback
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
@@ -142,7 +142,7 @@
confirmValueChange: (BottomSheetValue) -> Boolean = { true }
) {
- internal val swipeableState = SwipeableV2State(
+ internal val anchoredDraggableState = AnchoredDraggableState(
initialValue = initialValue,
animationSpec = animationSpec,
confirmValueChange = confirmValueChange,
@@ -159,19 +159,19 @@
)
val currentValue: BottomSheetValue
- get() = swipeableState.currentValue
+ get() = anchoredDraggableState.currentValue
/**
* Whether the bottom sheet is expanded.
*/
val isExpanded: Boolean
- get() = swipeableState.currentValue == Expanded
+ get() = anchoredDraggableState.currentValue == Expanded
/**
* Whether the bottom sheet is collapsed.
*/
val isCollapsed: Boolean
- get() = swipeableState.currentValue == Collapsed
+ get() = anchoredDraggableState.currentValue == Collapsed
/**
* The fraction of the progress going from [currentValue] to the targetValue, within [0f..1f]
@@ -179,7 +179,7 @@
*/
/*@FloatRange(from = 0f, to = 1f)*/
val progress: Float
- get() = swipeableState.progress
+ get() = anchoredDraggableState.progress
/**
* Expand the bottom sheet with an animation and suspend until the animation finishes or is
@@ -190,8 +190,8 @@
* This method will throw [CancellationException] if the animation is interrupted.
*/
suspend fun expand() {
- val target = if (swipeableState.hasAnchorForValue(Expanded)) Expanded else Collapsed
- swipeableState.animateTo(target)
+ val target = if (anchoredDraggableState.hasAnchorForValue(Expanded)) Expanded else Collapsed
+ anchoredDraggableState.animateTo(target)
}
/**
@@ -199,7 +199,7 @@
* has been cancelled. This method will throw [CancellationException] if the animation is
* interrupted.
*/
- suspend fun collapse() = swipeableState.animateTo(Collapsed)
+ suspend fun collapse() = anchoredDraggableState.animateTo(Collapsed)
@Deprecated(
message = "Use requireOffset() to access the offset.",
@@ -212,18 +212,18 @@
*
* @throws IllegalStateException If the offset has not been initialized yet
*/
- fun requireOffset() = swipeableState.requireOffset()
+ fun requireOffset() = anchoredDraggableState.requireOffset()
internal suspend fun animateTo(
target: BottomSheetValue,
- velocity: Float = swipeableState.lastVelocity
- ) = swipeableState.animateTo(target, velocity)
+ velocity: Float = anchoredDraggableState.lastVelocity
+ ) = anchoredDraggableState.animateTo(target, velocity)
- internal suspend fun snapTo(target: BottomSheetValue) = swipeableState.snapTo(target)
+ internal suspend fun snapTo(target: BottomSheetValue) = anchoredDraggableState.snapTo(target)
- internal fun trySnapTo(target: BottomSheetValue) = swipeableState.trySnapTo(target)
+ internal fun trySnapTo(target: BottomSheetValue) = anchoredDraggableState.trySnapTo(target)
- internal val isAnimationRunning: Boolean get() = swipeableState.isAnimationRunning
+ internal val isAnimationRunning: Boolean get() = anchoredDraggableState.isAnimationRunning
internal var density: Density? = null
private fun requireDensity() = requireNotNull(density) {
@@ -231,7 +231,7 @@
"the BottomSheetScaffold composable?"
}
- internal val lastVelocity: Float get() = swipeableState.lastVelocity
+ internal val lastVelocity: Float get() = anchoredDraggableState.lastVelocity
companion object {
@@ -243,7 +243,7 @@
confirmStateChange: (BottomSheetValue) -> Boolean,
density: Density
): Saver<BottomSheetState, *> = Saver(
- save = { it.swipeableState.currentValue },
+ save = { it.anchoredDraggableState.currentValue },
restore = {
BottomSheetState(
initialValue = it,
@@ -269,7 +269,7 @@
animationSpec: AnimationSpec<Float>,
confirmStateChange: (BottomSheetValue) -> Boolean
): Saver<BottomSheetState, *> = Saver(
- save = { it.swipeableState.currentValue },
+ save = { it.anchoredDraggableState.currentValue },
restore = {
BottomSheetState(
initialValue = it,
@@ -443,9 +443,9 @@
val nestedScroll = if (sheetGesturesEnabled) {
Modifier
.nestedScroll(
- remember(scaffoldState.bottomSheetState.swipeableState) {
+ remember(scaffoldState.bottomSheetState.anchoredDraggableState) {
ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
- state = scaffoldState.bottomSheetState.swipeableState,
+ state = scaffoldState.bottomSheetState.anchoredDraggableState,
orientation = Orientation.Vertical
)
}
@@ -529,13 +529,13 @@
}
Surface(
modifier
- .swipeableV2(
- state = state.swipeableState,
+ .anchoredDraggable(
+ state = state.anchoredDraggableState,
orientation = Orientation.Vertical,
enabled = sheetGesturesEnabled,
)
.onSizeChanged { layoutSize ->
- state.swipeableState.updateAnchors(
+ state.anchoredDraggableState.updateAnchors(
newAnchors = calculateAnchors(layoutSize),
onAnchorsChanged = anchorChangeCallback
)
@@ -543,17 +543,17 @@
.semantics {
// If we don't have anchors yet, or have only one anchor we don't want any
// accessibility actions
- if (state.swipeableState.anchors.size > 1) {
+ if (state.anchoredDraggableState.anchors.size > 1) {
if (state.isCollapsed) {
expand {
- if (state.swipeableState.confirmValueChange(Expanded)) {
+ if (state.anchoredDraggableState.confirmValueChange(Expanded)) {
scope.launch { state.expand() }
}
true
}
} else {
collapse {
- if (state.swipeableState.confirmValueChange(Collapsed)) {
+ if (state.anchoredDraggableState.confirmValueChange(Collapsed)) {
scope.launch { state.collapse() }
}
true
@@ -656,7 +656,7 @@
@OptIn(ExperimentalMaterialApi::class)
private fun ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
- state: SwipeableV2State<*>,
+ state: AnchoredDraggableState<*>,
orientation: Orientation
): NestedScrollConnection = object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
index a47a55e..c791f90 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
@@ -33,7 +33,7 @@
import androidx.compose.material.BottomDrawerValue.Closed
import androidx.compose.material.BottomDrawerValue.Expanded
import androidx.compose.material.BottomDrawerValue.Open
-import androidx.compose.material.SwipeableV2State.AnchorChangedCallback
+import androidx.compose.material.AnchoredDraggableState.AnchorChangedCallback
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
@@ -121,7 +121,7 @@
confirmStateChange: (DrawerValue) -> Boolean = { true }
) {
- internal val swipeableState = SwipeableV2State(
+ internal val anchoredDraggableState = AnchoredDraggableState(
initialValue = initialValue,
animationSpec = AnimationSpec,
confirmValueChange = confirmStateChange,
@@ -150,7 +150,7 @@
*/
val currentValue: DrawerValue
get() {
- return swipeableState.currentValue
+ return anchoredDraggableState.currentValue
}
/**
@@ -158,7 +158,7 @@
*/
val isAnimationRunning: Boolean
get() {
- return swipeableState.isAnimationRunning
+ return anchoredDraggableState.isAnimationRunning
}
/**
@@ -168,7 +168,7 @@
*
* @return the reason the open animation ended
*/
- suspend fun open() = swipeableState.animateTo(DrawerValue.Open)
+ suspend fun open() = anchoredDraggableState.animateTo(DrawerValue.Open)
/**
* Close the drawer with animation and suspend until it if fully closed or animation has been
@@ -177,7 +177,7 @@
*
* @return the reason the close animation ended
*/
- suspend fun close() = swipeableState.animateTo(DrawerValue.Closed)
+ suspend fun close() = anchoredDraggableState.animateTo(DrawerValue.Closed)
/**
* Set the state of the drawer with specific animation
@@ -195,7 +195,7 @@
targetValue: DrawerValue,
@Suppress("UNUSED_PARAMETER") anim: AnimationSpec<Float>
) {
- swipeableState.animateTo(targetValue)
+ anchoredDraggableState.animateTo(targetValue)
}
/**
@@ -204,7 +204,7 @@
* @param targetValue The new target value
*/
suspend fun snapTo(targetValue: DrawerValue) {
- swipeableState.snapTo(targetValue)
+ anchoredDraggableState.snapTo(targetValue)
}
/**
@@ -218,21 +218,21 @@
@ExperimentalMaterialApi
@get:ExperimentalMaterialApi
val targetValue: DrawerValue
- get() = swipeableState.targetValue
+ get() = anchoredDraggableState.targetValue
/**
* The current position (in pixels) of the drawer sheet, or null before the offset is
* initialized.
- * @see [SwipeableV2State.offset] for more information.
+ * @see [AnchoredDraggableState.offset] for more information.
*/
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@get:Suppress("AutoBoxing")
@ExperimentalMaterialApi
@get:ExperimentalMaterialApi
val offset: Float?
- get() = swipeableState.offset
+ get() = anchoredDraggableState.offset
- internal fun requireOffset(): Float = swipeableState.requireOffset()
+ internal fun requireOffset(): Float = anchoredDraggableState.requireOffset()
internal var density: Density? = null
private fun requireDensity() = requireNotNull(density) {
@@ -281,8 +281,8 @@
@Suppress("NotCloseable")
@ExperimentalMaterialApi
class BottomDrawerState @Deprecated(
- "This constructor is deprecated. Density must be provided by the component. Please use " +
- "the constructor that provides a [Density].",
+ "This constructor is deprecated. Density must be provided by the component. Please " +
+ "use the constructor that provides a [Density].",
ReplaceWith(
"""
BottomDrawerState(
@@ -296,7 +296,7 @@
initialValue: BottomDrawerValue,
confirmStateChange: (BottomDrawerValue) -> Boolean = { true }
) {
- internal val swipeableState = SwipeableV2State(
+ internal val anchoredDraggableState = AnchoredDraggableState(
initialValue = initialValue,
animationSpec = AnimationSpec,
confirmValueChange = confirmStateChange,
@@ -310,7 +310,7 @@
* will be the current value.
*/
val targetValue: BottomDrawerValue
- get() = swipeableState.targetValue
+ get() = anchoredDraggableState.targetValue
/**
* The current offset, or null if it has not been initialized yet.
@@ -318,32 +318,32 @@
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@get:Suppress("AutoBoxing")
val offset: Float?
- get() = swipeableState.offset
+ get() = anchoredDraggableState.offset
- internal fun requireOffset(): Float = swipeableState.requireOffset()
+ internal fun requireOffset(): Float = anchoredDraggableState.requireOffset()
/**
* The current value of the [BottomDrawerState].
*/
- val currentValue: BottomDrawerValue get() = swipeableState.currentValue
+ val currentValue: BottomDrawerValue get() = anchoredDraggableState.currentValue
/**
* Whether the drawer is open, either in opened or expanded state.
*/
val isOpen: Boolean
- get() = swipeableState.currentValue != Closed
+ get() = anchoredDraggableState.currentValue != Closed
/**
* Whether the drawer is closed.
*/
val isClosed: Boolean
- get() = swipeableState.currentValue == Closed
+ get() = anchoredDraggableState.currentValue == Closed
/**
* Whether the drawer is expanded.
*/
val isExpanded: Boolean
- get() = swipeableState.currentValue == Expanded
+ get() = anchoredDraggableState.currentValue == Expanded
/**
* Open the drawer with animation and suspend until it if fully opened or animation has been
@@ -356,7 +356,7 @@
suspend fun open() {
val targetValue =
if (isOpenEnabled) Open else Expanded
- swipeableState.animateTo(targetValue)
+ anchoredDraggableState.animateTo(targetValue)
}
/**
@@ -366,7 +366,7 @@
* @throws [CancellationException] if the animation is interrupted
*
*/
- suspend fun close() = swipeableState.animateTo(Closed)
+ suspend fun close() = anchoredDraggableState.animateTo(Closed)
/**
* Expand the drawer with animation and suspend until it if fully expanded or animation has
@@ -375,25 +375,25 @@
* @throws [CancellationException] if the animation is interrupted
*
*/
- suspend fun expand() = swipeableState.animateTo(Expanded)
+ suspend fun expand() = anchoredDraggableState.animateTo(Expanded)
internal suspend fun animateTo(
target: BottomDrawerValue,
- velocity: Float = swipeableState.lastVelocity
- ) = swipeableState.animateTo(target, velocity)
+ velocity: Float = anchoredDraggableState.lastVelocity
+ ) = anchoredDraggableState.animateTo(target, velocity)
- internal suspend fun snapTo(target: BottomDrawerValue) = swipeableState.snapTo(target)
+ internal suspend fun snapTo(target: BottomDrawerValue) = anchoredDraggableState.snapTo(target)
- internal fun trySnapTo(target: BottomDrawerValue) = swipeableState.trySnapTo(target)
+ internal fun trySnapTo(target: BottomDrawerValue) = anchoredDraggableState.trySnapTo(target)
internal fun confirmStateChange(value: BottomDrawerValue): Boolean =
- swipeableState.confirmValueChange(value)
+ anchoredDraggableState.confirmValueChange(value)
private val isOpenEnabled: Boolean
- get() = swipeableState.hasAnchorForValue(Open)
+ get() = anchoredDraggableState.hasAnchorForValue(Open)
internal val nestedScrollConnection = ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
- swipeableState
+ anchoredDraggableState
)
internal var density: Density? = null
@@ -403,8 +403,8 @@
" with the BottomDrawer composable?"
}
- internal val isAnimationRunning: Boolean get() = swipeableState.isAnimationRunning
- internal val lastVelocity: Float get() = swipeableState.lastVelocity
+ internal val isAnimationRunning: Boolean get() = anchoredDraggableState.isAnimationRunning
+ internal val lastVelocity: Float get() = anchoredDraggableState.lastVelocity
companion object {
/**
@@ -412,7 +412,7 @@
*/
fun Saver(density: Density, confirmStateChange: (BottomDrawerValue) -> Boolean) =
Saver<BottomDrawerState, BottomDrawerValue>(
- save = { it.swipeableState.currentValue },
+ save = { it.anchoredDraggableState.currentValue },
restore = { BottomDrawerState(it, density, confirmStateChange) }
)
@@ -429,7 +429,7 @@
@Suppress("Deprecation")
fun Saver(confirmStateChange: (BottomDrawerValue) -> Boolean) =
Saver<BottomDrawerState, BottomDrawerValue>(
- save = { it.swipeableState.currentValue },
+ save = { it.anchoredDraggableState.currentValue },
restore = { BottomDrawerState(it, confirmStateChange) }
)
}
@@ -526,14 +526,14 @@
SideEffect {
drawerState.density = density
val anchors = mapOf(DrawerValue.Closed to minValue, DrawerValue.Open to maxValue)
- drawerState.swipeableState.updateAnchors(anchors)
+ drawerState.anchoredDraggableState.updateAnchors(anchors)
}
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
Box(
Modifier
- .swipeableV2(
- state = drawerState.swipeableState,
+ .anchoredDraggable(
+ state = drawerState.anchoredDraggableState,
orientation = Orientation.Horizontal,
enabled = gesturesEnabled,
reverseDirection = isRtl
@@ -547,7 +547,7 @@
onClose = {
if (
gesturesEnabled &&
- drawerState.swipeableState.confirmValueChange(DrawerValue.Closed)
+ drawerState.anchoredDraggableState.confirmValueChange(DrawerValue.Closed)
) {
scope.launch { drawerState.close() }
}
@@ -581,7 +581,7 @@
if (drawerState.isOpen) {
dismiss {
if (
- drawerState.swipeableState
+ drawerState.anchoredDraggableState
.confirmValueChange(DrawerValue.Closed)
) {
scope.launch { drawerState.close() }
@@ -672,8 +672,8 @@
val swipeable = Modifier
.then(nestedScroll)
- .swipeableV2(
- state = drawerState.swipeableState,
+ .anchoredDraggable(
+ state = drawerState.anchoredDraggableState,
orientation = Orientation.Vertical,
enabled = gesturesEnabled,
reverseDirection = isRtl
@@ -710,7 +710,9 @@
put(Expanded, max(0f, fullHeight - drawerHeight))
}
}
- drawerState.swipeableState.updateAnchors(anchors, anchorChangeCallback)
+ drawerState.anchoredDraggableState.updateAnchors(
+ anchors, anchorChangeCallback
+ )
}
.offset {
IntOffset(
@@ -862,7 +864,7 @@
@OptIn(ExperimentalMaterialApi::class)
private fun ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
- state: SwipeableV2State<*>
+ state: AnchoredDraggableState<*>
): NestedScrollConnection = object : NestedScrollConnection {
val orientation: Orientation = Orientation.Vertical
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
index 5807196..9d1da3f 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
@@ -30,11 +30,11 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.widthIn
+import androidx.compose.material.AnchoredDraggableState.AnchorChangedCallback
import androidx.compose.material.ModalBottomSheetState.Companion.Saver
import androidx.compose.material.ModalBottomSheetValue.Expanded
import androidx.compose.material.ModalBottomSheetValue.HalfExpanded
import androidx.compose.material.ModalBottomSheetValue.Hidden
-import androidx.compose.material.SwipeableV2State.AnchorChangedCallback
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
@@ -199,7 +199,7 @@
confirmStateChange: (ModalBottomSheetValue) -> Boolean
) {
- internal val swipeableState = SwipeableV2State(
+ internal val anchoredDraggableState = AnchoredDraggableState(
initialValue = initialValue,
animationSpec = animationSpec,
confirmValueChange = confirmStateChange,
@@ -212,19 +212,19 @@
)
val currentValue: ModalBottomSheetValue
- get() = swipeableState.currentValue
+ get() = anchoredDraggableState.currentValue
val targetValue: ModalBottomSheetValue
- get() = swipeableState.targetValue
+ get() = anchoredDraggableState.targetValue
/**
* Whether the bottom sheet is visible.
*/
val isVisible: Boolean
- get() = swipeableState.currentValue != Hidden
+ get() = anchoredDraggableState.currentValue != Hidden
internal val hasHalfExpandedState: Boolean
- get() = swipeableState.hasAnchorForValue(HalfExpanded)
+ get() = anchoredDraggableState.hasAnchorForValue(HalfExpanded)
@Deprecated(
message = "This constructor is deprecated. confirmStateChange has been renamed to " +
@@ -285,7 +285,7 @@
* @throws [CancellationException] if the animation is interrupted
*/
internal suspend fun expand() {
- if (!swipeableState.hasAnchorForValue(Expanded)) {
+ if (!anchoredDraggableState.hasAnchorForValue(Expanded)) {
return
}
animateTo(Expanded)
@@ -301,20 +301,21 @@
internal suspend fun animateTo(
target: ModalBottomSheetValue,
- velocity: Float = swipeableState.lastVelocity
- ) = swipeableState.animateTo(target, velocity)
+ velocity: Float = anchoredDraggableState.lastVelocity
+ ) = anchoredDraggableState.animateTo(target, velocity)
- internal suspend fun snapTo(target: ModalBottomSheetValue) = swipeableState.snapTo(target)
+ internal suspend fun snapTo(target: ModalBottomSheetValue) =
+ anchoredDraggableState.snapTo(target)
internal fun trySnapTo(target: ModalBottomSheetValue): Boolean {
- return swipeableState.trySnapTo(target)
+ return anchoredDraggableState.trySnapTo(target)
}
- internal fun requireOffset() = swipeableState.requireOffset()
+ internal fun requireOffset() = anchoredDraggableState.requireOffset()
- internal val lastVelocity: Float get() = swipeableState.lastVelocity
+ internal val lastVelocity: Float get() = anchoredDraggableState.lastVelocity
- internal val isAnimationRunning: Boolean get() = swipeableState.isAnimationRunning
+ internal val isAnimationRunning: Boolean get() = anchoredDraggableState.isAnimationRunning
internal var density: Density? = null
private fun requireDensity() = requireNotNull(density) {
@@ -572,11 +573,11 @@
Scrim(
color = scrimColor,
onDismiss = {
- if (sheetState.swipeableState.confirmValueChange(Hidden)) {
+ if (sheetState.anchoredDraggableState.confirmValueChange(Hidden)) {
scope.launch { sheetState.hide() }
}
},
- visible = sheetState.swipeableState.targetValue != Hidden
+ visible = sheetState.anchoredDraggableState.targetValue != Hidden
)
}
Surface(
@@ -585,9 +586,9 @@
.widthIn(max = MaxModalBottomSheetWidth)
.fillMaxWidth()
.nestedScroll(
- remember(sheetState.swipeableState, orientation) {
+ remember(sheetState.anchoredDraggableState, orientation) {
ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
- state = sheetState.swipeableState,
+ state = sheetState.anchoredDraggableState,
orientation = orientation
)
}
@@ -595,15 +596,15 @@
.offset {
IntOffset(
0,
- sheetState.swipeableState
+ sheetState.anchoredDraggableState
.requireOffset()
.roundToInt()
)
}
- .swipeableV2(
- state = sheetState.swipeableState,
+ .anchoredDraggable(
+ state = sheetState.anchoredDraggableState,
orientation = orientation,
- enabled = sheetState.swipeableState.currentValue != Hidden,
+ enabled = sheetState.anchoredDraggableState.currentValue != Hidden,
)
.onSizeChanged { sheetSize ->
val anchors = buildMap {
@@ -616,26 +617,32 @@
put(Expanded, max(0f, fullHeight - sheetSize.height))
}
}
- sheetState.swipeableState.updateAnchors(anchors, anchorChangeCallback)
+ sheetState.anchoredDraggableState.updateAnchors(anchors, anchorChangeCallback)
}
.semantics {
if (sheetState.isVisible) {
dismiss {
- if (sheetState.swipeableState.confirmValueChange(Hidden)) {
+ if (sheetState.anchoredDraggableState.confirmValueChange(Hidden)) {
scope.launch { sheetState.hide() }
}
true
}
- if (sheetState.swipeableState.currentValue == HalfExpanded) {
+ if (sheetState.anchoredDraggableState.currentValue == HalfExpanded) {
expand {
- if (sheetState.swipeableState.confirmValueChange(Expanded)) {
+ if (sheetState.anchoredDraggableState.confirmValueChange(
+ Expanded
+ )
+ ) {
scope.launch { sheetState.expand() }
}
true
}
} else if (sheetState.hasHalfExpandedState) {
collapse {
- if (sheetState.swipeableState.confirmValueChange(HalfExpanded)) {
+ if (sheetState.anchoredDraggableState.confirmValueChange(
+ HalfExpanded
+ )
+ ) {
scope.launch { sheetState.halfExpand() }
}
true
@@ -706,7 +713,7 @@
@OptIn(ExperimentalMaterialApi::class)
private fun ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
- state: SwipeableV2State<*>,
+ state: AnchoredDraggableState<*>,
orientation: Orientation
): NestedScrollConnection = object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
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 8d723c1..30641c4 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
@@ -195,9 +195,9 @@
val draggableState = remember(minPx, maxPx, valueRange) {
SliderDraggableState {
- rawOffset.value = (rawOffset.value + it + pressOffset.value)
- pressOffset.value = 0f
- val offsetInTrack = rawOffset.value.coerceIn(minPx, maxPx)
+ rawOffset.floatValue = (rawOffset.floatValue + it + pressOffset.floatValue)
+ pressOffset.floatValue = 0f
+ val offsetInTrack = rawOffset.floatValue.coerceIn(minPx, maxPx)
onValueChangeState.value.invoke(scaleToUserValue(offsetInTrack))
}
}
@@ -205,7 +205,7 @@
CorrectValueSideEffect(::scaleToOffset, valueRange, minPx..maxPx, rawOffset, value)
val gestureEndAction = rememberUpdatedState<(Float) -> Unit> { velocity: Float ->
- val current = rawOffset.value
+ val current = rawOffset.floatValue
val target = snapValueToTick(current, tickFractions, minPx, maxPx)
if (current != target) {
scope.launch {
@@ -348,7 +348,7 @@
val scope = rememberCoroutineScope()
val gestureEndAction = rememberUpdatedState<(Boolean) -> Unit> { isStart ->
- val current = (if (isStart) rawOffsetStart else rawOffsetEnd).value
+ val current = (if (isStart) rawOffsetStart else rawOffsetEnd).floatValue
// target is a closest anchor to the `current`, if exists
val target = snapValueToTick(current, tickFractions, minPx, maxPx)
if (current == target) {
@@ -361,9 +361,9 @@
target, SliderToTickAnimation,
0f
) {
- (if (isStart) rawOffsetStart else rawOffsetEnd).value = this.value
+ (if (isStart) rawOffsetStart else rawOffsetEnd).floatValue = this.value
onValueChangeState.value.invoke(
- scaleToUserValue(rawOffsetStart.value..rawOffsetEnd.value)
+ scaleToUserValue(rawOffsetStart.floatValue..rawOffsetEnd.floatValue)
)
}
@@ -373,16 +373,16 @@
val onDrag = rememberUpdatedState<(Boolean, Float) -> Unit> { isStart, offset ->
val offsetRange = if (isStart) {
- rawOffsetStart.value = (rawOffsetStart.value + offset)
- rawOffsetEnd.value = scaleToOffset(value.endInclusive)
- val offsetEnd = rawOffsetEnd.value
- val offsetStart = rawOffsetStart.value.coerceIn(minPx, offsetEnd)
+ rawOffsetStart.floatValue = (rawOffsetStart.floatValue + offset)
+ rawOffsetEnd.floatValue = scaleToOffset(value.endInclusive)
+ val offsetEnd = rawOffsetEnd.floatValue
+ val offsetStart = rawOffsetStart.floatValue.coerceIn(minPx, offsetEnd)
offsetStart..offsetEnd
} else {
- rawOffsetEnd.value = (rawOffsetEnd.value + offset)
- rawOffsetStart.value = scaleToOffset(value.start)
- val offsetStart = rawOffsetStart.value
- val offsetEnd = rawOffsetEnd.value.coerceIn(offsetStart, maxPx)
+ rawOffsetEnd.floatValue = (rawOffsetEnd.floatValue + offset)
+ rawOffsetStart.floatValue = scaleToOffset(value.start)
+ val offsetStart = rawOffsetStart.floatValue
+ val offsetEnd = rawOffsetEnd.floatValue.coerceIn(offsetStart, maxPx)
offsetStart..offsetEnd
}
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 56d0ccf..257e39c 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
@@ -138,8 +138,8 @@
requireNotNull(initialOffset) {
"The initial value must have an associated anchor."
}
- offsetState.value = initialOffset
- absoluteOffset.value = initialOffset
+ offsetState.floatValue = initialOffset
+ absoluteOffset.floatValue = initialOffset
}
}
@@ -199,24 +199,24 @@
internal var resistance: ResistanceConfig? by mutableStateOf(null)
internal val draggableState = DraggableState {
- val newAbsolute = absoluteOffset.value + it
+ val newAbsolute = absoluteOffset.floatValue + it
val clamped = newAbsolute.coerceIn(minBound, maxBound)
val overflow = newAbsolute - clamped
val resistanceDelta = resistance?.computeResistance(overflow) ?: 0f
- offsetState.value = clamped + resistanceDelta
- overflowState.value = overflow
- absoluteOffset.value = newAbsolute
+ offsetState.floatValue = clamped + resistanceDelta
+ overflowState.floatValue = overflow
+ absoluteOffset.floatValue = newAbsolute
}
private suspend fun snapInternalToOffset(target: Float) {
draggableState.drag {
- dragBy(target - absoluteOffset.value)
+ dragBy(target - absoluteOffset.floatValue)
}
}
private suspend fun animateInternalToOffset(target: Float, spec: AnimationSpec<Float>) {
draggableState.drag {
- var prevValue = absoluteOffset.value
+ var prevValue = absoluteOffset.floatValue
animationTarget.value = target
isAnimationRunning = true
try {
@@ -334,7 +334,7 @@
}
animateInternalToOffset(targetOffset, anim)
} finally {
- val endOffset = absoluteOffset.value
+ val endOffset = absoluteOffset.floatValue
val endValue = anchors
// fighting rounding error once again, anchor should be as close as 0.5 pixels
.filterKeys { anchorOffset -> abs(anchorOffset - endOffset) < 0.5f }
@@ -392,9 +392,9 @@
* @return the amount of [delta] consumed
*/
fun performDrag(delta: Float): Float {
- val potentiallyConsumed = absoluteOffset.value + delta
+ val potentiallyConsumed = absoluteOffset.floatValue + delta
val clamped = potentiallyConsumed.coerceIn(minBound, maxBound)
- val deltaToConsume = clamped - absoluteOffset.value
+ val deltaToConsume = clamped - absoluteOffset.floatValue
if (abs(deltaToConsume) > 0) {
draggableState.dispatchRawDelta(deltaToConsume)
}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
index 006f13f..4133929 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
@@ -108,8 +108,8 @@
// a new checked value.
var forceAnimationCheck by remember { mutableStateOf(false) }
val switchVelocityThresholdPx = with(LocalDensity.current) { SwitchVelocityThreshold.toPx() }
- val swipeableState = remember(switchVelocityThresholdPx) {
- SwipeableV2State(
+ val anchoredDraggableState = remember(switchVelocityThresholdPx) {
+ AnchoredDraggableState(
initialValue = checked,
animationSpec = AnimationSpec,
positionalThreshold = { distance -> distance * SwitchPositionalThreshold },
@@ -119,10 +119,10 @@
val currentOnCheckedChange by rememberUpdatedState(onCheckedChange)
val currentChecked by rememberUpdatedState(checked)
SideEffect {
- swipeableState.updateAnchors(mapOf(false to minBound, true to maxBound))
+ anchoredDraggableState.updateAnchors(mapOf(false to minBound, true to maxBound))
}
- LaunchedEffect(swipeableState) {
- snapshotFlow { swipeableState.currentValue }
+ LaunchedEffect(anchoredDraggableState) {
+ snapshotFlow { anchoredDraggableState.currentValue }
.collectLatest { newValue ->
if (currentChecked != newValue) {
currentOnCheckedChange?.invoke(newValue)
@@ -131,8 +131,8 @@
}
}
LaunchedEffect(checked, forceAnimationCheck) {
- if (checked != swipeableState.currentValue) {
- swipeableState.animateTo(checked)
+ if (checked != anchoredDraggableState.currentValue) {
+ anchoredDraggableState.animateTo(checked)
}
}
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
@@ -160,8 +160,8 @@
}
)
.then(toggleableModifier)
- .swipeableV2(
- state = swipeableState,
+ .anchoredDraggable(
+ state = anchoredDraggableState,
orientation = Orientation.Horizontal,
enabled = enabled && onCheckedChange != null,
reverseDirection = isRtl,
@@ -172,10 +172,10 @@
.requiredSize(SwitchWidth, SwitchHeight)
) {
SwitchImpl(
- checked = swipeableState.targetValue,
+ checked = anchoredDraggableState.targetValue,
enabled = enabled,
colors = colors,
- thumbValue = { swipeableState.requireOffset() },
+ thumbValue = { anchoredDraggableState.requireOffset() },
interactionSource = interactionSource
)
}
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 0901450..d2b53fb 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -818,7 +818,7 @@
public final class TextKt {
method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+ method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional androidx.compose.ui.text.TextStyle style);
method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, 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 kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index 7378d91..c580cf9 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -798,12 +798,7 @@
method @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,? extends kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
}
- @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class PlainTooltipState {
- ctor public PlainTooltipState();
- method public suspend Object? dismiss(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public boolean isVisible();
- method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public boolean isVisible;
+ @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface PlainTooltipState extends androidx.compose.material3.TooltipState {
}
public final class ProgressIndicatorDefaults {
@@ -863,12 +858,9 @@
property public final long titleContentColor;
}
- @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class RichTooltipState {
- ctor public RichTooltipState();
- method public suspend Object? dismiss(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public boolean isVisible();
- method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public boolean isVisible;
+ @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface RichTooltipState extends androidx.compose.material3.TooltipState {
+ method public boolean isPersistent();
+ property public abstract boolean isPersistent;
}
public final class ScaffoldDefaults {
@@ -1212,7 +1204,7 @@
public final class TextKt {
method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+ method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional androidx.compose.ui.text.TextStyle style);
method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, 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 kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
@@ -1263,15 +1255,17 @@
}
@androidx.compose.material3.ExperimentalMaterial3Api public interface TooltipBoxScope {
- method public androidx.compose.ui.Modifier tooltipAnchor(androidx.compose.ui.Modifier);
+ method public androidx.compose.ui.Modifier tooltipTrigger(androidx.compose.ui.Modifier);
}
@androidx.compose.material3.ExperimentalMaterial3Api public final class TooltipDefaults {
+ method public androidx.compose.foundation.MutatorMutex getGlobalMutatorMutex();
method @androidx.compose.runtime.Composable public long getPlainTooltipContainerColor();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getPlainTooltipContainerShape();
method @androidx.compose.runtime.Composable public long getPlainTooltipContentColor();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getRichTooltipContainerShape();
method @androidx.compose.runtime.Composable public androidx.compose.material3.RichTooltipColors richTooltipColors(optional long containerColor, optional long contentColor, optional long titleContentColor, optional long actionContentColor);
+ property public final androidx.compose.foundation.MutatorMutex GlobalMutatorMutex;
property @androidx.compose.runtime.Composable public final long plainTooltipContainerColor;
property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape plainTooltipContainerShape;
property @androidx.compose.runtime.Composable public final long plainTooltipContentColor;
@@ -1281,7 +1275,17 @@
public final class TooltipKt {
method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void PlainTooltipBox(kotlin.jvm.functions.Function0<kotlin.Unit> tooltip, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.PlainTooltipState tooltipState, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.material3.TooltipBoxScope,kotlin.Unit> content);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RichTooltipBox(kotlin.jvm.functions.Function0<kotlin.Unit> text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.RichTooltipState tooltipState, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? action, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.RichTooltipColors colors, kotlin.jvm.functions.Function1<? super androidx.compose.material3.TooltipBoxScope,kotlin.Unit> content);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RichTooltipBox(kotlin.jvm.functions.Function0<kotlin.Unit> text, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? action, optional androidx.compose.material3.RichTooltipState tooltipState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.RichTooltipColors colors, kotlin.jvm.functions.Function1<? super androidx.compose.material3.TooltipBoxScope,kotlin.Unit> content);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.PlainTooltipState rememberPlainTooltipState(optional androidx.compose.foundation.MutatorMutex mutatorMutex);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.RichTooltipState rememberRichTooltipState(boolean isPersistent, optional androidx.compose.foundation.MutatorMutex mutatorMutex);
+ }
+
+ @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface TooltipState {
+ method public void dismiss();
+ method public boolean isVisible();
+ method public void onDispose();
+ method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public abstract boolean isVisible;
}
@androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class TopAppBarColors {
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 0901450..d2b53fb 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -818,7 +818,7 @@
public final class TextKt {
method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+ method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional androidx.compose.ui.text.TextStyle style);
method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, 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 kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
diff --git a/compose/material3/material3/build.gradle b/compose/material3/material3/build.gradle
index 10979b6..1a631a7 100644
--- a/compose/material3/material3/build.gradle
+++ b/compose/material3/material3/build.gradle
@@ -40,7 +40,7 @@
implementation("androidx.compose.animation:animation-core:1.4.2")
implementation("androidx.compose.foundation:foundation-layout:1.4.2")
implementation("androidx.compose.ui:ui-util:1.4.2")
- api("androidx.compose.foundation:foundation:1.4.2")
+ api(project(":compose:foundation:foundation"))
api("androidx.compose.material:material-icons-core:1.4.2")
api("androidx.compose.material:material-ripple:1.4.2")
api("androidx.compose.runtime:runtime:1.4.2")
diff --git a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TooltipDemo.kt b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TooltipDemo.kt
index e70d753..741de3d 100644
--- a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TooltipDemo.kt
+++ b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/TooltipDemo.kt
@@ -16,6 +16,7 @@
package androidx.compose.material3.demos
+import androidx.compose.foundation.MutatorMutex
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -35,6 +36,8 @@
import androidx.compose.material3.PlainTooltipBox
import androidx.compose.material3.PlainTooltipState
import androidx.compose.material3.Text
+import androidx.compose.material3.TooltipDefaults
+import androidx.compose.material3.rememberPlainTooltipState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
@@ -44,7 +47,10 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withTimeout
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -61,8 +67,9 @@
) {
var textFieldValue by remember { mutableStateOf("") }
var textFieldTooltipText by remember { mutableStateOf("") }
- val textFieldTooltipState = remember { PlainTooltipState() }
+ val textFieldTooltipState = rememberPlainTooltipState()
val scope = rememberCoroutineScope()
+ val mutatorMutex = TooltipDefaults.GlobalMutatorMutex
PlainTooltipBox(
tooltip = {
Text(textFieldTooltipText)
@@ -86,7 +93,7 @@
textFieldTooltipState.show()
}
} else {
- val listItem = ItemInfo(textFieldValue, PlainTooltipState())
+ val listItem = ItemInfo(textFieldValue, DemoTooltipState(mutatorMutex))
listData.add(listItem)
textFieldValue = ""
scope.launch {
@@ -134,7 +141,7 @@
) {
IconButton(
onClick = onDelete,
- modifier = Modifier.tooltipAnchor()
+ modifier = Modifier.tooltipTrigger()
) {
Icon(
imageVector = Icons.Filled.Delete,
@@ -152,3 +159,37 @@
val itemName: String,
val addedTooltipState: PlainTooltipState
)
+
+@OptIn(ExperimentalMaterial3Api::class)
+class DemoTooltipState(private val mutatorMutex: MutatorMutex) : PlainTooltipState {
+ override var isVisible by mutableStateOf(false)
+
+ private var job: (CancellableContinuation<Unit>)? = null
+
+ override suspend fun show() {
+ mutatorMutex.mutate {
+ try {
+ withTimeout(TOOLTIP_DURATION) {
+ suspendCancellableCoroutine { continuation ->
+ isVisible = true
+ job = continuation
+ }
+ }
+ } finally {
+ // timeout or cancellation has occurred
+ // and we close out the current tooltip.
+ isVisible = false
+ }
+ }
+ }
+
+ override fun dismiss() {
+ isVisible = false
+ }
+
+ override fun onDispose() {
+ job?.cancel()
+ }
+}
+
+private const val TOOLTIP_DURATION = 1000L
\ No newline at end of file
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TooltipSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TooltipSamples.kt
index b3206b8..05630d7 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TooltipSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TooltipSamples.kt
@@ -29,13 +29,12 @@
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.PlainTooltipBox
-import androidx.compose.material3.PlainTooltipState
import androidx.compose.material3.RichTooltipBox
-import androidx.compose.material3.RichTooltipState
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
+import androidx.compose.material3.rememberPlainTooltipState
+import androidx.compose.material3.rememberRichTooltipState
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -53,7 +52,7 @@
) {
IconButton(
onClick = { /* Icon button's click event */ },
- modifier = Modifier.tooltipAnchor()
+ modifier = Modifier.tooltipTrigger()
) {
Icon(
imageVector = Icons.Filled.Favorite,
@@ -68,7 +67,7 @@
@Sampled
@Composable
fun PlainTooltipWithManualInvocationSample() {
- val tooltipState = remember { PlainTooltipState() }
+ val tooltipState = rememberPlainTooltipState()
val scope = rememberCoroutineScope()
Column(
horizontalAlignment = Alignment.CenterHorizontally
@@ -95,7 +94,7 @@
@Sampled
@Composable
fun RichTooltipSample() {
- val tooltipState = remember { RichTooltipState() }
+ val tooltipState = rememberRichTooltipState(isPersistent = true)
val scope = rememberCoroutineScope()
RichTooltipBox(
title = { Text(richTooltipSubheadText) },
@@ -109,7 +108,7 @@
) {
IconButton(
onClick = { /* Icon button's click event */ },
- modifier = Modifier.tooltipAnchor()
+ modifier = Modifier.tooltipTrigger()
) {
Icon(
imageVector = Icons.Filled.Info,
@@ -122,7 +121,7 @@
@Sampled
@Composable
fun RichTooltipWithManualInvocationSample() {
- val tooltipState = remember { RichTooltipState() }
+ val tooltipState = rememberRichTooltipState(isPersistent = true)
val scope = rememberCoroutineScope()
Column(
horizontalAlignment = Alignment.CenterHorizontally
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
index fd0d59b..9f0f5e4 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
@@ -20,7 +20,6 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.testutils.assertAgainstGolden
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
@@ -124,7 +123,7 @@
@Composable
private fun PlainTooltipTest() {
- val tooltipState = remember { PlainTooltipState() }
+ val tooltipState = rememberPlainTooltipState()
PlainTooltipBox(
tooltip = { Text("Tooltip Description") },
modifier = Modifier.testTag(TooltipTestTag),
@@ -135,14 +134,14 @@
contentDescription = null,
modifier = Modifier
.testTag(AnchorTestTag)
- .tooltipAnchor()
+ .tooltipTrigger()
)
}
}
@Composable
private fun RichTooltipTest() {
- val tooltipState = remember { RichTooltipState() }
+ val tooltipState = rememberRichTooltipState(isPersistent = true)
RichTooltipBox(
title = { Text("Title") },
text = {
@@ -160,7 +159,7 @@
contentDescription = null,
modifier = Modifier
.testTag(AnchorTestTag)
- .tooltipAnchor()
+ .tooltipTrigger()
)
}
}
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipTest.kt
index 848bfb6..d5729f1 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TooltipTest.kt
@@ -16,11 +16,11 @@
package androidx.compose.material3
+import androidx.compose.foundation.MutatorMutex
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
@@ -211,8 +211,9 @@
@Test
fun plainTooltip_behavior() {
- val tooltipState = PlainTooltipState()
+ lateinit var tooltipState: PlainTooltipState
rule.setMaterialContent(lightColorScheme()) {
+ tooltipState = rememberPlainTooltipState()
PlainTooltipTest(
tooltipContent = { Text(text = "Test", modifier = Modifier.testTag(TextTestTag)) },
tooltipState = tooltipState
@@ -244,8 +245,9 @@
@Test
fun richTooltip_behavior_noAction() {
- val tooltipState = RichTooltipState()
+ lateinit var tooltipState: RichTooltipState
rule.setMaterialContent(lightColorScheme()) {
+ tooltipState = rememberRichTooltipState(isPersistent = false)
RichTooltipTest(
title = { Text(text = "Subhead", modifier = Modifier.testTag(SubheadTestTag)) },
text = { Text(text = "Text", modifier = Modifier.testTag(TextTestTag)) },
@@ -278,8 +280,9 @@
@Test
fun richTooltip_behavior_persistent() {
- val tooltipState = RichTooltipState()
+ lateinit var tooltipState: RichTooltipState
rule.setMaterialContent(lightColorScheme()) {
+ tooltipState = rememberRichTooltipState(isPersistent = true)
val scope = rememberCoroutineScope()
RichTooltipTest(
title = { Text(text = "Subhead", modifier = Modifier.testTag(SubheadTestTag)) },
@@ -322,11 +325,129 @@
assertThat(tooltipState.isVisible).isFalse()
}
+ @Test
+ fun tooltipSync_global_onlyOneVisible() {
+ val topTooltipTag = "Top Tooltip"
+ val bottomTooltipTag = " Bottom Tooltip"
+ lateinit var topState: RichTooltipState
+ lateinit var bottomState: RichTooltipState
+ rule.setMaterialContent(lightColorScheme()) {
+ val scope = rememberCoroutineScope()
+ topState = rememberRichTooltipState(isPersistent = true)
+ bottomState = rememberRichTooltipState(isPersistent = true)
+
+ RichTooltipBox(
+ title = { Text(text = "Subhead", modifier = Modifier.testTag(SubheadTestTag)) },
+ text = { Text(text = "Text", modifier = Modifier.testTag(TextTestTag)) },
+ action = {
+ TextButton(
+ modifier = Modifier.testTag(ActionTestTag),
+ onClick = {}
+ ) {
+ Text(text = "Action")
+ }
+ },
+ tooltipState = topState,
+ modifier = Modifier.testTag(topTooltipTag)
+ ) {}
+
+ RichTooltipBox(
+ title = { Text(text = "Subhead", modifier = Modifier.testTag(SubheadTestTag)) },
+ text = { Text(text = "Text", modifier = Modifier.testTag(TextTestTag)) },
+ action = {
+ TextButton(
+ modifier = Modifier.testTag(ActionTestTag),
+ onClick = {}
+ ) {
+ Text(text = "Action")
+ }
+ },
+ tooltipState = bottomState,
+ modifier = Modifier.testTag(bottomTooltipTag)
+ ) {}
+
+ scope.launch { topState.show() }
+ scope.launch { bottomState.show() }
+ }
+
+ // Test will manually advance the time to check the timeout
+ rule.mainClock.autoAdvance = false
+
+ // Advance by the fade in time
+ rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+
+ // Check that only the tooltip associated with bottomState is visible
+ rule.waitForIdle()
+ assertThat(topState.isVisible).isFalse()
+ assertThat(bottomState.isVisible).isTrue()
+ }
+
+ @Test
+ fun tooltipSync_local_bothVisible() {
+ val topTooltipTag = "Top Tooltip"
+ val bottomTooltipTag = " Bottom Tooltip"
+ lateinit var topState: RichTooltipState
+ lateinit var bottomState: RichTooltipState
+ rule.setMaterialContent(lightColorScheme()) {
+ val scope = rememberCoroutineScope()
+ topState = rememberRichTooltipState(
+ isPersistent = true,
+ mutatorMutex = MutatorMutex()
+ )
+ RichTooltipBox(
+ title = { Text(text = "Subhead", modifier = Modifier.testTag(SubheadTestTag)) },
+ text = { Text(text = "Text", modifier = Modifier.testTag(TextTestTag)) },
+ action = {
+ TextButton(
+ modifier = Modifier.testTag(ActionTestTag),
+ onClick = {}
+ ) {
+ Text(text = "Action")
+ }
+ },
+ tooltipState = topState,
+ modifier = Modifier.testTag(topTooltipTag)
+ ) {}
+ scope.launch { topState.show() }
+
+ bottomState = rememberRichTooltipState(
+ isPersistent = true,
+ mutatorMutex = MutatorMutex()
+ )
+ RichTooltipBox(
+ title = { Text(text = "Subhead", modifier = Modifier.testTag(SubheadTestTag)) },
+ text = { Text(text = "Text", modifier = Modifier.testTag(TextTestTag)) },
+ action = {
+ TextButton(
+ modifier = Modifier.testTag(ActionTestTag),
+ onClick = {}
+ ) {
+ Text(text = "Action")
+ }
+ },
+ tooltipState = bottomState,
+ modifier = Modifier.testTag(bottomTooltipTag)
+ ) {}
+ scope.launch { bottomState.show() }
+ }
+
+ // Test will manually advance the time to check the timeout
+ rule.mainClock.autoAdvance = false
+
+ // Advance by the fade in time
+ rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+
+ // Check that both tooltips are now showing
+ rule.waitForIdle()
+ assertThat(topState.isVisible).isTrue()
+ assertThat(bottomState.isVisible).isTrue()
+ }
+
@Composable
private fun PlainTooltipTest(
modifier: Modifier = Modifier,
tooltipContent: @Composable () -> Unit = {},
- tooltipState: PlainTooltipState = remember { PlainTooltipState() },
+ tooltipState: PlainTooltipState = rememberPlainTooltipState(),
) {
PlainTooltipBox(
tooltip = tooltipContent,
@@ -338,7 +459,7 @@
contentDescription = null,
modifier = Modifier
.testTag(AnchorTestTag)
- .tooltipAnchor()
+ .tooltipTrigger()
)
}
}
@@ -346,10 +467,10 @@
@Composable
private fun RichTooltipTest(
modifier: Modifier = Modifier,
- tooltipState: RichTooltipState = remember { RichTooltipState() },
text: @Composable () -> Unit = {},
title: (@Composable () -> Unit)? = null,
- action: (@Composable () -> Unit)? = null
+ action: (@Composable () -> Unit)? = null,
+ tooltipState: RichTooltipState = rememberRichTooltipState(action != null),
) {
RichTooltipBox(
text = text,
@@ -363,7 +484,7 @@
contentDescription = null,
modifier = Modifier
.testTag(AnchorTestTag)
- .tooltipAnchor()
+ .tooltipTrigger()
)
}
}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Text.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Text.kt
index c418cf7..064586d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Text.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Text.kt
@@ -105,7 +105,7 @@
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
- onTextLayout: (TextLayoutResult) -> Unit = {},
+ onTextLayout: ((TextLayoutResult) -> Unit)? = null,
style: TextStyle = LocalTextStyle.current
) {
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
index ff8f7f7..38a673c 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
@@ -38,8 +38,8 @@
import androidx.compose.material3.tokens.RichTooltipTokens
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -66,9 +66,10 @@
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.PopupPositionProvider
-import kotlinx.coroutines.delay
+import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withTimeout
// TODO: add link to m3 doc once created by designer at the top
/**
@@ -95,7 +96,7 @@
fun PlainTooltipBox(
tooltip: @Composable () -> Unit,
modifier: Modifier = Modifier,
- tooltipState: PlainTooltipState = remember { PlainTooltipState() },
+ tooltipState: PlainTooltipState = rememberPlainTooltipState(),
shape: Shape = TooltipDefaults.plainTooltipContainerShape,
containerColor: Color = TooltipDefaults.plainTooltipContainerColor,
contentColor: Color = TooltipDefaults.plainTooltipContentColor,
@@ -149,9 +150,9 @@
fun RichTooltipBox(
text: @Composable () -> Unit,
modifier: Modifier = Modifier,
- tooltipState: RichTooltipState = remember { RichTooltipState() },
title: (@Composable () -> Unit)? = null,
action: (@Composable () -> Unit)? = null,
+ tooltipState: RichTooltipState = rememberRichTooltipState(action != null),
shape: Shape = TooltipDefaults.richTooltipContainerShape,
colors: RichTooltipColors = TooltipDefaults.richTooltipColors(),
content: @Composable TooltipBoxScope.() -> Unit
@@ -159,11 +160,6 @@
val tooltipAnchorPadding = with(LocalDensity.current) { TooltipAnchorPadding.roundToPx() }
val positionProvider = remember { RichTooltipPositionProvider(tooltipAnchorPadding) }
- SideEffect {
- // Make the rich tooltip persistent if an action is provided.
- tooltipState.isPersistent = (action != null)
- }
-
TooltipBox(
tooltipContent = {
RichTooltipImpl(
@@ -184,6 +180,9 @@
)
}
+/**
+ * TODO: Figure out what should live here vs. within foundation (b/262626721)
+ */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TooltipBox(
@@ -200,9 +199,9 @@
val coroutineScope = rememberCoroutineScope()
val longPressLabel = getString(string = Strings.TooltipLongPressLabel)
- val scope = remember {
+ val scope = remember(tooltipState) {
object : TooltipBoxScope {
- override fun Modifier.tooltipAnchor(): Modifier {
+ override fun Modifier.tooltipTrigger(): Modifier {
val onLongPress = {
coroutineScope.launch {
tooltipState.show()
@@ -275,6 +274,10 @@
scope.content()
}
+
+ DisposableEffect(tooltipState) {
+ onDispose { tooltipState.onDispose() }
+ }
}
@Composable
@@ -351,6 +354,11 @@
@ExperimentalMaterial3Api
object TooltipDefaults {
/**
+ * The global/default [MutatorMutex] used to sync Tooltips.
+ */
+ val GlobalMutatorMutex = MutatorMutex()
+
+ /**
* The default [Shape] for a [PlainTooltipBox]'s container.
*/
val plainTooltipContainerShape: Shape
@@ -433,30 +441,88 @@
* after long pressing the anchor composable is desired. It appends a long click to
* the composable that this modifier is chained with.
*/
- fun Modifier.tooltipAnchor(): Modifier
+ fun Modifier.tooltipTrigger(): Modifier
}
/**
+ * Create and remember the default [PlainTooltipState].
+ *
+ * @param mutatorMutex [MutatorMutex] used to ensure that for all of the tooltips associated
+ * with the mutator mutex, only one will be shown on the screen at any time.
+ */
+@Composable
+@ExperimentalMaterial3Api
+fun rememberPlainTooltipState(
+ mutatorMutex: MutatorMutex = TooltipDefaults.GlobalMutatorMutex
+): PlainTooltipState =
+ remember { PlainTooltipStateImpl(mutatorMutex) }
+
+/**
+ * Create and remember the default [RichTooltipState].
+ *
+ * @param isPersistent [Boolean] that determines if the tooltip associated with this
+ * [RichTooltipState] will be persistent or not. If isPersistent is true, then the tooltip will
+ * only be dismissed when the user clicks outside the bounds of the tooltip or if
+ * [TooltipState.dismiss] is called. When isPersistent is false, the tooltip will dismiss after
+ * a short duration. Ideally, this should be set to true when an action is provided to the
+ * [RichTooltipBox] that this [RichTooltipState] is associated with.
+ * @param mutatorMutex [MutatorMutex] used to ensure that for all of the tooltips associated
+ * with the mutator mutex, only one will be shown on the screen at any time.
+ */
+@Composable
+@ExperimentalMaterial3Api
+fun rememberRichTooltipState(
+ isPersistent: Boolean,
+ mutatorMutex: MutatorMutex = TooltipDefaults.GlobalMutatorMutex
+): RichTooltipState =
+ remember { RichTooltipStateImpl(isPersistent, mutatorMutex) }
+
+/**
* The [TooltipState] that should be used with [RichTooltipBox]
*/
@Stable
@ExperimentalMaterial3Api
-class RichTooltipState : TooltipState {
+interface PlainTooltipState : TooltipState
+
+/**
+ * The [TooltipState] that should be used with [RichTooltipBox]
+ */
+@Stable
+@ExperimentalMaterial3Api
+interface RichTooltipState : TooltipState {
+ val isPersistent: Boolean
+}
+
+/**
+ * The default implementation for [RichTooltipState]
+ *
+ * @param isPersistent [Boolean] that determines if the tooltip associated with this
+ * [RichTooltipState] will be persistent or not. If isPersistent is true, then the tooltip will
+ * only be dismissed when the user clicks outside the bounds of the tooltip or if
+ * [TooltipState.dismiss] is called. When isPersistent is false, the tooltip will dismiss after
+ * a short duration. Ideally, this should be set to true when an action is provided to the
+ * [RichTooltipBox] that this [RichTooltipState] is associated with.
+ * @param mutatorMutex [MutatorMutex] used to ensure that for all of the tooltips associated
+ * with the mutator mutex, only one will be shown on the screen at any time.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Stable
+internal class RichTooltipStateImpl(
+ override val isPersistent: Boolean,
+ private val mutatorMutex: MutatorMutex
+) : RichTooltipState {
+
/**
* [Boolean] that will be used to update the visibility
* state of the associated tooltip.
*/
override var isVisible: Boolean by mutableStateOf(false)
- internal set
+ private set
/**
- * If isPersistent is true, then the tooltip will only be dismissed when the user clicks
- * outside the bounds of the tooltip or if [TooltipState.dismiss] is called. When isPersistent
- * is false, the tooltip will dismiss after a short duration. If an action composable is
- * provided to the [RichTooltipBox] that the [RichTooltipState] is associated with, then the
- * isPersistent will be set to true.
+ * continuation used to clean up
*/
- internal var isPersistent: Boolean by mutableStateOf(false)
+ private var job: (CancellableContinuation<Unit>)? = null
/**
* Show the tooltip associated with the current [RichTooltipState].
@@ -465,33 +531,67 @@
* being shown will dismiss.
*/
override suspend fun show() {
- TooltipSync.show(
- state = this,
- persistent = isPersistent
- )
+ val cancellableShow: suspend () -> Unit = {
+ suspendCancellableCoroutine { continuation ->
+ isVisible = true
+ job = continuation
+ }
+ }
+
+ // Show associated tooltip for [TooltipDuration] amount of time
+ // or until tooltip is explicitly dismissed depending on [isPersistent].
+ mutatorMutex.mutate(MutatePriority.Default) {
+ try {
+ if (isPersistent) {
+ cancellableShow()
+ } else {
+ withTimeout(TooltipDuration) {
+ cancellableShow()
+ }
+ }
+ } finally {
+ // timeout or cancellation has occurred
+ // and we close out the current tooltip.
+ isVisible = false
+ }
+ }
}
/**
* Dismiss the tooltip associated with
* this [RichTooltipState] if it's currently being shown.
*/
- override suspend fun dismiss() {
- TooltipSync.dismissCurrentTooltip(this)
+ override fun dismiss() {
+ isVisible = false
+ }
+
+ /**
+ * Cleans up [MutatorMutex] when the tooltip associated
+ * with this state leaves Composition.
+ */
+ override fun onDispose() {
+ job?.cancel()
}
}
/**
- * The [TooltipState] that should be used with [RichTooltipBox]
+ * The default implementation for [PlainTooltipState]
*/
+@OptIn(ExperimentalMaterial3Api::class)
@Stable
-@ExperimentalMaterial3Api
-class PlainTooltipState : TooltipState {
+internal class PlainTooltipStateImpl(private val mutatorMutex: MutatorMutex) : PlainTooltipState {
+
/**
* [Boolean] that will be used to update the visibility
* state of the associated tooltip.
*/
override var isVisible by mutableStateOf(false)
- internal set
+ private set
+
+ /**
+ * continuation used to clean up
+ */
+ private var job: (CancellableContinuation<Unit>)? = null
/**
* Show the tooltip associated with the current [PlainTooltipState].
@@ -499,29 +599,47 @@
* all of the other tooltips currently being shown will dismiss.
*/
override suspend fun show() {
- TooltipSync.show(
- state = this,
- persistent = false
- )
+ // Show associated tooltip for [TooltipDuration] amount of time.
+ mutatorMutex.mutate {
+ try {
+ withTimeout(TooltipDuration) {
+ suspendCancellableCoroutine { continuation ->
+ isVisible = true
+ job = continuation
+ }
+ }
+ } finally {
+ // timeout or cancellation has occurred
+ // and we close out the current tooltip.
+ isVisible = false
+ }
+ }
}
/**
* Dismiss the tooltip associated with
* this [PlainTooltipState] if it's currently being shown.
*/
- override suspend fun dismiss() {
- TooltipSync.dismissCurrentTooltip(this)
+ override fun dismiss() {
+ isVisible = false
+ }
+
+ /**
+ * Cleans up [MutatorMutex] when the tooltip associated
+ * with this state leaves Composition.
+ */
+ override fun onDispose() {
+ job?.cancel()
}
}
/**
* The state that is associated with an instance of a tooltip.
- * Each instance of tooltips should have its own [TooltipState] it
- * will be used to synchronize the tooltips shown via [TooltipSync].
+ * Each instance of tooltips should have its own [TooltipState].
*/
@Stable
@ExperimentalMaterial3Api
-internal sealed interface TooltipState {
+interface TooltipState {
/**
* [Boolean] that will be used to update the visibility
* state of the associated tooltip.
@@ -539,7 +657,12 @@
* Dismiss the tooltip associated with
* this [TooltipState] if it's currently being shown.
*/
- suspend fun dismiss()
+ fun dismiss()
+
+ /**
+ * Clean up when the this state leaves Composition.
+ */
+ fun onDispose()
}
private class PlainTooltipPositionProvider(
@@ -591,92 +714,6 @@
}
}
-/**
- * Object used to synchronize
- * multiple [TooltipState]s, ensuring that there will
- * only be one tooltip shown on the screen at any given time.
- */
-@Stable
-@ExperimentalMaterial3Api
-private object TooltipSync {
- val mutatorMutex: MutatorMutex = MutatorMutex()
- var mutexOwner: TooltipState? = null
-
- /**
- * Shows the tooltip associated with [TooltipState],
- * it dismisses any tooltip currently being shown.
- */
- suspend fun show(
- state: TooltipState,
- persistent: Boolean
- ) {
- val runBlock: suspend () -> Unit
- val cleanUp: () -> Unit
-
- when (state) {
- is PlainTooltipState -> {
- /**
- * Show associated tooltip for [TooltipDuration] amount of time.
- */
- runBlock = {
- state.isVisible = true
- delay(TooltipDuration)
- }
- /**
- * When the mutex is taken, we just dismiss the associated tooltip.
- */
- cleanUp = { state.isVisible = false }
- }
- is RichTooltipState -> {
- /**
- * Show associated tooltip for [TooltipDuration] amount of time
- * or until tooltip is explicitly dismissed depending on [persistent].
- */
- runBlock = {
- if (persistent) {
- suspendCancellableCoroutine<Unit> {
- state.isVisible = true
- }
- } else {
- state.isVisible = true
- delay(TooltipDuration)
- }
- }
- /**
- * When the mutex is taken, we just dismiss the associated tooltip.
- */
- cleanUp = { state.isVisible = false }
- }
- }
-
- mutatorMutex.mutate(MutatePriority.Default) {
- try {
- mutexOwner = state
- runBlock()
- } finally {
- mutexOwner = null
- // timeout or cancellation has occurred
- // and we close out the current tooltip.
- cleanUp()
- }
- }
- }
-
- /**
- * Dismisses the tooltip currently
- * being shown by freeing up the lock.
- */
- suspend fun dismissCurrentTooltip(
- state: TooltipState
- ) {
- if (state == mutexOwner) {
- mutatorMutex.mutate(MutatePriority.UserInput) {
- /* Do nothing, we're just freeing up the mutex */
- }
- }
- }
-}
-
private fun Modifier.textVerticalPadding(
subheadExists: Boolean,
actionExists: Boolean
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
index 243dc42..8f2683a 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
@@ -28,5 +28,5 @@
* IMPORTANT: Whenever updating this value, please make sure to also update `versionTable` and
* `minimumRuntimeVersionInt` in `VersionChecker.kt` of the compiler.
*/
- const val version: Int = 9901
+ const val version: Int = 10001
}
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt
index 467355f..3744926 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt
@@ -131,6 +131,8 @@
* This configuration was added for migration of the apps in case some code or design was
* relying includeFontPadding=true behavior.
*/
+ @Suppress("GetterSetterNames")
+ @get:Suppress("GetterSetterNames")
val includeFontPadding: Boolean
/**
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 f83c6ec..5b64707 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
@@ -116,14 +116,16 @@
className,
methodName,
currentComposer,
- previewParameters[index.value]
+ previewParameters[index.intValue]
)
}
},
floatingActionButton = {
ExtendedFloatingActionButton(
text = { Text("Next") },
- onClick = { index.value = (index.value + 1) % previewParameters.size }
+ onClick = {
+ index.intValue = (index.intValue + 1) % previewParameters.size
+ }
)
}
)
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 48fd02b..ca9cb43 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -144,6 +144,7 @@
ctor public Modifier.Node();
method public final kotlinx.coroutines.CoroutineScope getCoroutineScope();
method public final androidx.compose.ui.Modifier.Node getNode();
+ method public boolean getShouldAutoInvalidate();
method public final boolean isAttached();
method public void onAttach();
method public void onDetach();
@@ -152,6 +153,7 @@
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
property public final boolean isAttached;
property public final androidx.compose.ui.Modifier.Node node;
+ property public boolean shouldAutoInvalidate;
}
@androidx.compose.runtime.Stable public interface MotionDurationScale extends kotlin.coroutines.CoroutineContext.Element {
@@ -2277,7 +2279,8 @@
public abstract class DelegatingNode extends androidx.compose.ui.Modifier.Node {
ctor public DelegatingNode();
- method public final <T extends androidx.compose.ui.Modifier.Node> T delegated(kotlin.jvm.functions.Function0<? extends T> fn);
+ method protected final <T extends androidx.compose.ui.node.DelegatableNode> T delegate(T delegatableNode);
+ method protected final void undelegate(androidx.compose.ui.node.DelegatableNode instance);
}
public interface DrawModifierNode extends androidx.compose.ui.node.DelegatableNode {
@@ -2309,7 +2312,7 @@
public final class LayoutModifierNodeKt {
method public static void invalidateLayer(androidx.compose.ui.node.LayoutModifierNode);
- method public static void invalidateMeasurements(androidx.compose.ui.node.LayoutModifierNode);
+ method public static void invalidateMeasurement(androidx.compose.ui.node.LayoutModifierNode);
method public static void invalidatePlacement(androidx.compose.ui.node.LayoutModifierNode);
}
@@ -2317,14 +2320,12 @@
ctor public ModifierNodeElement();
method public abstract N create();
method public abstract boolean equals(Object? other);
- method public boolean getAutoInvalidate();
method public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
method public final String? getNameFallback();
method public final Object? getValueOverride();
method public abstract int hashCode();
method public void inspectableProperties(androidx.compose.ui.platform.InspectorInfo);
method public abstract N update(N node);
- property public boolean autoInvalidate;
property public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> inspectableElements;
property public final String? nameFallback;
property public final Object? valueOverride;
@@ -2378,7 +2379,6 @@
}
public final class SemanticsModifierNodeKt {
- method public static androidx.compose.ui.semantics.SemanticsConfiguration collapsedSemanticsConfiguration(androidx.compose.ui.node.SemanticsModifierNode);
method public static void invalidateSemantics(androidx.compose.ui.node.SemanticsModifierNode);
}
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 95fddb6..6d46125 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -154,6 +154,7 @@
ctor public Modifier.Node();
method public final kotlinx.coroutines.CoroutineScope getCoroutineScope();
method public final androidx.compose.ui.Modifier.Node getNode();
+ method public boolean getShouldAutoInvalidate();
method public final boolean isAttached();
method public void onAttach();
method public void onDetach();
@@ -162,6 +163,7 @@
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
property public final boolean isAttached;
property public final androidx.compose.ui.Modifier.Node node;
+ property public boolean shouldAutoInvalidate;
}
@androidx.compose.runtime.Stable public interface MotionDurationScale extends kotlin.coroutines.CoroutineContext.Element {
@@ -2491,7 +2493,8 @@
public abstract class DelegatingNode extends androidx.compose.ui.Modifier.Node {
ctor public DelegatingNode();
- method public final <T extends androidx.compose.ui.Modifier.Node> T delegated(kotlin.jvm.functions.Function0<? extends T> fn);
+ method protected final <T extends androidx.compose.ui.node.DelegatableNode> T delegate(T delegatableNode);
+ method protected final void undelegate(androidx.compose.ui.node.DelegatableNode instance);
}
public interface DrawModifierNode extends androidx.compose.ui.node.DelegatableNode {
@@ -2530,7 +2533,7 @@
public final class LayoutModifierNodeKt {
method public static void invalidateLayer(androidx.compose.ui.node.LayoutModifierNode);
- method public static void invalidateMeasurements(androidx.compose.ui.node.LayoutModifierNode);
+ method public static void invalidateMeasurement(androidx.compose.ui.node.LayoutModifierNode);
method public static void invalidatePlacement(androidx.compose.ui.node.LayoutModifierNode);
}
@@ -2538,14 +2541,12 @@
ctor public ModifierNodeElement();
method public abstract N create();
method public abstract boolean equals(Object? other);
- method public boolean getAutoInvalidate();
method public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
method public final String? getNameFallback();
method public final Object? getValueOverride();
method public abstract int hashCode();
method public void inspectableProperties(androidx.compose.ui.platform.InspectorInfo);
method public abstract N update(N node);
- property public boolean autoInvalidate;
property public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> inspectableElements;
property public final String? nameFallback;
property public final Object? valueOverride;
@@ -2600,7 +2601,6 @@
}
public final class SemanticsModifierNodeKt {
- method public static androidx.compose.ui.semantics.SemanticsConfiguration collapsedSemanticsConfiguration(androidx.compose.ui.node.SemanticsModifierNode);
method public static void invalidateSemantics(androidx.compose.ui.node.SemanticsModifierNode);
}
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index fe0c61d..47520cf 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -144,6 +144,7 @@
ctor public Modifier.Node();
method public final kotlinx.coroutines.CoroutineScope getCoroutineScope();
method public final androidx.compose.ui.Modifier.Node getNode();
+ method public boolean getShouldAutoInvalidate();
method public final boolean isAttached();
method public void onAttach();
method public void onDetach();
@@ -152,6 +153,7 @@
property public final kotlinx.coroutines.CoroutineScope coroutineScope;
property public final boolean isAttached;
property public final androidx.compose.ui.Modifier.Node node;
+ property public boolean shouldAutoInvalidate;
}
@androidx.compose.runtime.Stable public interface MotionDurationScale extends kotlin.coroutines.CoroutineContext.Element {
@@ -2325,7 +2327,8 @@
public abstract class DelegatingNode extends androidx.compose.ui.Modifier.Node {
ctor public DelegatingNode();
- method public final <T extends androidx.compose.ui.Modifier.Node> T delegated(kotlin.jvm.functions.Function0<? extends T> fn);
+ method protected final <T extends androidx.compose.ui.node.DelegatableNode> T delegate(T delegatableNode);
+ method protected final void undelegate(androidx.compose.ui.node.DelegatableNode instance);
}
public interface DrawModifierNode extends androidx.compose.ui.node.DelegatableNode {
@@ -2357,7 +2360,7 @@
public final class LayoutModifierNodeKt {
method public static void invalidateLayer(androidx.compose.ui.node.LayoutModifierNode);
- method public static void invalidateMeasurements(androidx.compose.ui.node.LayoutModifierNode);
+ method public static void invalidateMeasurement(androidx.compose.ui.node.LayoutModifierNode);
method public static void invalidatePlacement(androidx.compose.ui.node.LayoutModifierNode);
}
@@ -2365,14 +2368,12 @@
ctor public ModifierNodeElement();
method public abstract N create();
method public abstract boolean equals(Object? other);
- method public boolean getAutoInvalidate();
method public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> getInspectableElements();
method public final String? getNameFallback();
method public final Object? getValueOverride();
method public abstract int hashCode();
method public void inspectableProperties(androidx.compose.ui.platform.InspectorInfo);
method public abstract N update(N node);
- property public boolean autoInvalidate;
property public final kotlin.sequences.Sequence<androidx.compose.ui.platform.ValueElement> inspectableElements;
property public final String? nameFallback;
property public final Object? valueOverride;
@@ -2426,7 +2427,6 @@
}
public final class SemanticsModifierNodeKt {
- method public static androidx.compose.ui.semantics.SemanticsConfiguration collapsedSemanticsConfiguration(androidx.compose.ui.node.SemanticsModifierNode);
method public static void invalidateSemantics(androidx.compose.ui.node.SemanticsModifierNode);
}
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 6cc20cfc1..2d7bfb2 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
@@ -49,11 +49,13 @@
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.PointerInputModifierNode
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.heading
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
@@ -119,7 +121,7 @@
@ExperimentalComposeUiApi
@Sampled
@Composable
-fun DelegatedNodeSample() {
+fun DelegatedNodeSampleExplicit() {
class TapGestureNode(var onTap: () -> Unit) : PointerInputModifierNode, Modifier.Node() {
override fun onPointerEvent(
pointerEvent: PointerEvent,
@@ -139,7 +141,7 @@
get() = gesture.onTap
set(value) { gesture.onTap = value }
- val gesture = delegated { TapGestureNode(onTap) }
+ val gesture = delegate(TapGestureNode(onTap))
override fun onPointerEvent(
pointerEvent: PointerEvent,
@@ -166,6 +168,151 @@
@ExperimentalComposeUiApi
@Sampled
@Composable
+fun DelegatedNodeSampleImplicit() {
+ class TapGestureNode(var onTap: () -> Unit) : PointerInputModifierNode, Modifier.Node() {
+ override fun onPointerEvent(
+ pointerEvent: PointerEvent,
+ pass: PointerEventPass,
+ bounds: IntSize
+ ) {
+ // ...
+ }
+
+ override fun onCancelPointerInput() {
+ // ...
+ }
+ }
+
+ class TapSemanticsNode(var onTap: () -> Unit) : SemanticsModifierNode, Modifier.Node() {
+ override val semanticsConfiguration: SemanticsConfiguration = SemanticsConfiguration()
+ .apply {
+ onClick {
+ onTap()
+ true
+ }
+ }
+ }
+ class TapGestureWithClickSemantics(onTap: () -> Unit) : DelegatingNode() {
+ var onTap: () -> Unit
+ get() = gesture.onTap
+ set(value) {
+ gesture.onTap = value
+ semantics.onTap = value
+ }
+
+ val gesture = delegate(TapGestureNode(onTap))
+ val semantics = delegate(TapSemanticsNode(onTap))
+ }
+}
+
+@ExperimentalComposeUiApi
+@Sampled
+@Composable
+fun LazyDelegationExample() {
+ class ExpensivePositionHandlingOnPointerEvents() : PointerInputModifierNode, DelegatingNode() {
+
+ val globalAwareNode = object : GlobalPositionAwareModifierNode, Modifier.Node() {
+ override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+ // ...
+ }
+ }
+
+ override fun onPointerEvent(
+ pointerEvent: PointerEvent,
+ pass: PointerEventPass,
+ bounds: IntSize
+ ) {
+ // wait until first pointer event to start listening to global
+ // position
+ if (!globalAwareNode.isAttached) {
+ delegate(globalAwareNode)
+ }
+ // normal input processing
+ }
+
+ override fun onCancelPointerInput() {
+ // ...
+ }
+ }
+
+ class TapGestureNode(var onTap: () -> Unit) : PointerInputModifierNode, Modifier.Node() {
+ override fun onPointerEvent(
+ pointerEvent: PointerEvent,
+ pass: PointerEventPass,
+ bounds: IntSize
+ ) {
+ // ...
+ }
+
+ override fun onCancelPointerInput() {
+ // ...
+ }
+ }
+
+ class TapSemanticsNode(var onTap: () -> Unit) : SemanticsModifierNode, Modifier.Node() {
+ override val semanticsConfiguration: SemanticsConfiguration = SemanticsConfiguration()
+ .apply {
+ onClick {
+ onTap()
+ true
+ }
+ }
+ }
+ class TapGestureWithClickSemantics(onTap: () -> Unit) : DelegatingNode() {
+ var onTap: () -> Unit
+ get() = gesture.onTap
+ set(value) {
+ gesture.onTap = value
+ semantics.onTap = value
+ }
+
+ val gesture = delegate(TapGestureNode(onTap))
+ val semantics = delegate(TapSemanticsNode(onTap))
+ }
+}
+
+@Sampled
+fun ConditionalDelegationExample() {
+ class MyModifierNode(global: Boolean) : DelegatingNode() {
+ val globalAwareNode = object : GlobalPositionAwareModifierNode, Modifier.Node() {
+ override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+ // ...
+ }
+ }.also {
+ if (global) delegate(it)
+ }
+ var global: Boolean = global
+ set(value) {
+ if (global && !value) {
+ undelegate(globalAwareNode)
+ } else if (!global && value) {
+ delegate(globalAwareNode)
+ }
+ field = value
+ }
+ }
+}
+
+@Sampled
+fun DelegateInAttachSample() {
+ class MyModifierNode : DelegatingNode() {
+ val globalAwareNode = object : GlobalPositionAwareModifierNode, Modifier.Node() {
+ override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+ // ...
+ }
+ }
+ override fun onAttach() {
+ // one can conditionally delegate in attach, for instance if certain conditions are met
+ if (requireLayoutDirection() == LayoutDirection.Rtl) {
+ delegate(globalAwareNode)
+ }
+ }
+ }
+}
+
+@ExperimentalComposeUiApi
+@Sampled
+@Composable
fun ModifierNodeElementSample() {
class Circle(var color: Color) : DrawModifierNode, Modifier.Node() {
override fun ContentDrawScope.draw() {
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 27e78de..df76fb54 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
@@ -286,8 +286,6 @@
val switchRoleNode = toggleableNode.replacedChildren.last()
val switchRoleNodeInfo = provider.createAccessibilityNodeInfo(switchRoleNode.id)!!
assertEquals("android.view.View", switchRoleNodeInfo.className)
-// TODO(aelias)
- // assertEquals("Switch", switchRoleNodeInfo.roleDescription)
val stateDescription = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
@@ -3771,8 +3769,8 @@
assertNotNull("Button has no children", lastChild)
assertTrue("Last child should be fake Button role node", lastChild!!.isFake)
assertEquals(
+ Role.Button,
lastChild.unmergedConfig.getOrNull(SemanticsProperties.Role),
- Role.Button
)
}
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 ece6982..b4cc4f9 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
@@ -31,9 +31,7 @@
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.toAndroidRect
-import androidx.compose.ui.node.InnerNodeCoordinator
import androidx.compose.ui.node.LayoutNode
-import androidx.compose.ui.node.SemanticsModifierNode
import androidx.compose.ui.platform.AndroidComposeView
import androidx.compose.ui.platform.AndroidComposeViewAccessibilityDelegateCompat
import androidx.compose.ui.platform.AndroidComposeViewAccessibilityDelegateCompat.SemanticsNodeCopy
@@ -46,7 +44,6 @@
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.ScrollAxisRange
-import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.semantics.SemanticsNode
import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
@@ -1604,19 +1601,10 @@
properties: (SemanticsPropertyReceiver.() -> Unit)
): SemanticsNode {
val layoutNode = LayoutNode(semanticsId = id)
- val nodeCoordinator = InnerNodeCoordinator(layoutNode)
- val modifierNode = object : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration = SemanticsConfiguration().also {
- it.isMergingSemanticsOfDescendants = mergeDescendants
- it.properties()
- }
+ layoutNode.modifier = Modifier.semantics(mergeDescendants) {
+ properties()
}
- modifierNode.updateCoordinator(nodeCoordinator)
- return SemanticsNode(
- modifierNode,
- true,
- layoutNode
- )
+ return SemanticsNode(layoutNode, true)
}
private fun createSemanticsNodeWithChildren(
@@ -1626,20 +1614,10 @@
): SemanticsNode {
val layoutNode = LayoutNode(semanticsId = id)
layoutNode.zSortedChildren.addAll(children.map { it.layoutNode })
- val nodeCoordinator = InnerNodeCoordinator(layoutNode)
- val modifierNode = object : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration = SemanticsConfiguration().also {
- it.properties()
- }
- }
- modifierNode.updateCoordinator(nodeCoordinator)
-
- val semanticsNode = SemanticsNode(modifierNode, true, layoutNode)
layoutNode.modifier = Modifier.semantics {
properties()
}
-
- return semanticsNode
+ return SemanticsNode(layoutNode, true)
}
private fun createSemanticsNodeWithAdjustedBoundsWithProperties(
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
index 47f9c79..8bc1531 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
@@ -20,15 +20,18 @@
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.LayoutIdModifier
import androidx.compose.ui.layout.LayoutIdParentData
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.LayoutModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.ParentDataModifierNode
import androidx.compose.ui.node.Ref
+import androidx.compose.ui.semantics.elementFor
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
@@ -161,6 +164,27 @@
}
@Test
+ fun delegatedParentData() {
+ val node = object : DelegatingNode() {
+ val pd = delegate(LayoutIdModifier("data"))
+ }
+ runOnUiThread {
+ activity.setContent {
+ Layout({
+ Layout(
+ modifier = Modifier.elementFor(node),
+ content = {}
+ ) { _, _ -> layout(0, 0) {} }
+ }) { measurables, constraints ->
+ val placeable = measurables[0].measure(constraints)
+ assertEquals("data", (placeable.parentData as? LayoutIdParentData)?.layoutId)
+ layout(0, 0) { }
+ }
+ }
+ }
+ }
+
+ @Test
fun implementingBothParentDataAndLayoutModifier() {
val parentData = "data"
runOnUiThread {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
index 8bed753..0a41454 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
@@ -44,14 +44,19 @@
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.LayoutModifierImpl
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.platform.InspectableValue
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.elementFor
import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
import androidx.compose.ui.test.captureToImage
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -721,6 +726,100 @@
}
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testDelegatedDrawNodesDraw() {
+ val testTag = "testTag"
+ val size = 200
+
+ val node = object : DelegatingNode() {
+ val draw = delegate(DrawBackgroundModifier {
+ drawRect(Color.Red)
+ })
+ }
+
+ rule.setContent {
+ AtLeastSize(
+ size = size,
+ modifier = Modifier
+ .testTag(testTag)
+ .elementFor(node)
+ ) { }
+ }
+
+ rule.onNodeWithTag(testTag).apply {
+ captureToBitmap().apply {
+ assertEquals(Color.Red.toArgb(), getPixel(1, 1))
+ }
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testMultipleDelegatedDrawNodes() {
+ val testTag = "testTag"
+
+ val node = object : DelegatingNode() {
+ val a = delegate(DrawBackgroundModifier {
+ drawRect(
+ Color.Red,
+ size = Size(10f, 10f)
+ )
+ })
+
+ val b = delegate(DrawBackgroundModifier {
+ drawRect(
+ Color.Blue,
+ topLeft = Offset(10f, 0f),
+ size = Size(10f, 10f))
+ })
+ }
+
+ rule.setContent {
+ AtLeastSize(
+ size = 200,
+ modifier = Modifier
+ .testTag(testTag)
+ .elementFor(node)
+ ) { }
+ }
+
+ rule.onNodeWithTag(testTag).apply {
+ captureToBitmap().apply {
+ assertEquals(Color.Red.toArgb(), getPixel(1, 1))
+ assertEquals(Color.Blue.toArgb(), getPixel(11, 1))
+ }
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testDelegatedLayoutModifierNode() {
+ val testTag = "testTag"
+
+ val node = object : DelegatingNode() {
+ val a = delegate(LayoutModifierImpl { measurable, constraints ->
+ val p = measurable.measure(constraints)
+ layout(10.dp.roundToPx(), 10.dp.roundToPx()) {
+ p.place(0, 0)
+ }
+ })
+ }
+
+ rule.setContent {
+ Box(
+ modifier = Modifier
+ .testTag(testTag)
+ .elementFor(node)
+ )
+ }
+
+ rule
+ .onNodeWithTag(testTag)
+ .assertWidthIsEqualTo(10.dp)
+ .assertHeightIsEqualTo(10.dp)
+ }
+
// captureToImage() requires API level 26
@RequiresApi(Build.VERSION_CODES.O)
private fun SemanticsNodeInteraction.captureToBitmap() = captureToImage().asAndroidBitmap()
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 bb14507..7a42a32 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
@@ -20,7 +20,6 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.ModifierNodeElement
@@ -32,7 +31,6 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-@OptIn(ExperimentalComposeUiApi::class)
@MediumTest
@RunWith(Parameterized::class)
class CombinedFocusModifierNodeTest(private val delegatedFocusTarget: Boolean) {
@@ -213,15 +211,10 @@
fun initParameters() =
listOf(
false,
- // TODO: Delegation does not work right now because a delegated node can
- // reference the node delegating to it, but it can't reference a delegated node in
- // its parent. For some use-cases, a parent needs to invalidate a child. We cannot
- // do this when the child is a delegated node.
- // true
+ true
)
}
- @OptIn(ExperimentalComposeUiApi::class)
private class CombinedFocusNode(delegatedFocusTarget: Boolean) :
FocusRequesterModifierNode,
FocusEventModifierNode,
@@ -229,7 +222,7 @@
DelegatingNode() {
init {
- if (delegatedFocusTarget) delegated { FocusTargetModifierNode() }
+ if (delegatedFocusTarget) delegate(FocusTargetModifierNode())
}
lateinit var focusState: FocusState
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt
index 21d476d..f17a1a2 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/AndroidProcessKeyInputTest.kt
@@ -39,6 +39,10 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import android.view.KeyEvent as AndroidKeyEvent
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.semantics.elementFor
+import org.junit.Ignore
+import org.mockito.kotlin.inOrder
/**
* This test verifies that an Android key event triggers a Compose key event. More detailed test
@@ -98,4 +102,143 @@
assertThat(keyConsumed).isTrue()
}
}
+
+ @Ignore("b/279178695")
+ @Test
+ fun delegated_onKeyEvent_triggered() {
+ // Arrange.
+ lateinit var ownerView: View
+ var receivedKeyEvent: KeyEvent? = null
+ val focusRequester = FocusRequester()
+ val node = object : DelegatingNode() {
+ val ki = delegate(object : KeyInputModifierNode, Modifier.Node() {
+ override fun onKeyEvent(event: KeyEvent): Boolean {
+ receivedKeyEvent = event
+ return true
+ }
+
+ override fun onPreKeyEvent(event: KeyEvent): Boolean {
+ return false
+ }
+ })
+ }
+
+ rule.setFocusableContent {
+ ownerView = LocalView.current
+ Box(
+ modifier = Modifier
+ .focusRequester(focusRequester)
+ .focusTarget()
+ .elementFor(node)
+ )
+ }
+ rule.runOnIdle {
+ focusRequester.requestFocus()
+ }
+
+ // Act.
+ val keyConsumed = rule.runOnIdle {
+ ownerView.dispatchKeyEvent(AndroidKeyEvent(keyEventAction, KeyCodeA))
+ }
+
+ rule.waitUntil { receivedKeyEvent != null }
+
+ // Assert.
+ rule.runOnIdle {
+ val keyEvent = checkNotNull(receivedKeyEvent)
+ assertThat(keyEvent.type).isEqualTo(
+ when (keyEventAction) {
+ ActionUp -> KeyUp
+ ActionDown -> KeyDown
+ else -> error("No tests for this key action.")
+ }
+ )
+ assertThat(keyEvent.key).isEqualTo(A)
+ assertThat(keyConsumed).isTrue()
+ }
+ }
+
+ @Ignore("b/279178695")
+ @Test
+ fun delegated_multiple_onKeyEvent_triggered() {
+ // Arrange.
+ lateinit var ownerView: View
+ var receivedKeyEvent1: KeyEvent? = null
+ var receivedKeyEvent2: KeyEvent? = null
+ val eventLog = mutableListOf<KeyEvent>()
+ val focusRequester = FocusRequester()
+ val node = object : DelegatingNode() {
+ val a = delegate(object : KeyInputModifierNode, Modifier.Node() {
+ override fun onKeyEvent(event: KeyEvent): Boolean {
+ receivedKeyEvent1 = event
+ eventLog.add(event)
+ return false
+ }
+
+ override fun onPreKeyEvent(event: KeyEvent): Boolean {
+ return false
+ }
+ })
+ val b = delegate(object : KeyInputModifierNode, Modifier.Node() {
+ override fun onKeyEvent(event: KeyEvent): Boolean {
+ receivedKeyEvent2 = event
+ eventLog.add(event)
+ return false
+ }
+
+ override fun onPreKeyEvent(event: KeyEvent): Boolean {
+ return false
+ }
+ })
+ }
+
+ rule.setFocusableContent {
+ ownerView = LocalView.current
+ Box(
+ modifier = Modifier
+ .focusRequester(focusRequester)
+ .elementFor(node)
+ .focusTarget()
+ )
+ }
+ rule.runOnIdle {
+ focusRequester.requestFocus()
+ }
+
+ // Act.
+ val keyConsumed = rule.runOnIdle {
+ ownerView.dispatchKeyEvent(AndroidKeyEvent(keyEventAction, KeyCodeA))
+ }
+
+ rule.waitUntil { receivedKeyEvent2 != null }
+
+ // Assert.
+ rule.runOnIdle {
+ val keyEvent1 = checkNotNull(receivedKeyEvent1)
+ assertThat(keyEvent1.type).isEqualTo(
+ when (keyEventAction) {
+ ActionUp -> KeyUp
+ ActionDown -> KeyDown
+ else -> error("No tests for this key action.")
+ }
+ )
+ assertThat(keyEvent1.key).isEqualTo(A)
+ assertThat(keyConsumed).isFalse()
+
+ val keyEvent2 = checkNotNull(receivedKeyEvent2)
+ assertThat(keyEvent2.type).isEqualTo(
+ when (keyEventAction) {
+ ActionUp -> KeyUp
+ ActionDown -> KeyDown
+ else -> error("No tests for this key action.")
+ }
+ )
+ assertThat(keyEvent2.key).isEqualTo(A)
+ assertThat(keyConsumed).isFalse()
+
+ assertThat(eventLog)
+ .containsExactly(receivedKeyEvent1, receivedKeyEvent2)
+ .inOrder()
+ }
+ }
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
index a13aa46..4bc3f57 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
@@ -3529,7 +3529,7 @@
}
private fun areEqual(actualNode: Node, expectedNode: Node): Boolean {
- if (actualNode.pointerInputNode !== expectedNode.pointerInputNode) {
+ if (actualNode.modifierNode !== expectedNode.modifierNode) {
return false
}
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 960fa84..37234ac 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
@@ -17,17 +17,23 @@
package androidx.compose.ui.input.pointer
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
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
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.unit.IntSize
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
@@ -692,6 +698,79 @@
assertThat(resultOfTimeoutOrNull).isNull()
}
}
+
+ @Test
+ @MediumTest
+ fun testDelegatedPointerEvent() {
+ val latch = CountDownLatch(1)
+ val emitter = PointerInputChangeEmitter()
+ val expectedChange = emitter.nextChange(Offset(5f, 5f))
+
+ var returnedChange: PointerEvent? = null
+
+ // Used to manually trigger a PointerEvent created from our PointerInputChange.
+ val suspendingPointerInputModifierNode = SuspendingPointerInputModifierNode {
+ awaitPointerEventScope {
+ returnedChange = awaitPointerEvent()
+ latch.countDown()
+ }
+ }
+ val node = object : DelegatingNode() {
+ val pointer = delegate(suspendingPointerInputModifierNode)
+ }
+
+ rule.setContent {
+ Box(Modifier.elementFor(node))
+ }
+
+ rule.runOnIdle {
+ suspendingPointerInputModifierNode.onPointerEvent(
+ expectedChange.toPointerEvent(),
+ PointerEventPass.Main,
+ IntSize(10, 10)
+ )
+ }
+
+ rule.runOnIdle {
+ assertTrue("Waiting for relaunch timed out", latch.await(200, TimeUnit.MILLISECONDS))
+ assertEquals(expectedChange, returnedChange?.firstChange)
+ }
+ }
+
+ @Test
+ @MediumTest
+ fun testMultipleDelegatedPointerEvents2() {
+ val events = mutableListOf<PointerEvent>()
+ val tag = "input rect"
+
+ val node = object : DelegatingNode() {
+ val piNode1 = delegate(SuspendingPointerInputModifierNode {
+ awaitPointerEventScope {
+ events += awaitPointerEvent()
+ }
+ })
+
+ val piNode2 = delegate(SuspendingPointerInputModifierNode {
+ awaitPointerEventScope {
+ events += awaitPointerEvent()
+ }
+ })
+ }
+
+ rule.setContent {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .testTag(tag)
+ .elementFor(node)
+ )
+ }
+
+ rule.onNodeWithTag(tag).performTouchInput {
+ down(Offset.Zero)
+ }
+ assertThat(events).hasSize(2)
+ }
}
private fun PointerInputChange.toPointerEvent() = PointerEvent(listOf(this))
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEventTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEventTest.kt
index 2b9168f..06d53a3 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEventTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEventTest.kt
@@ -30,7 +30,9 @@
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.focusTarget
import androidx.compose.ui.focus.setFocusableContent
+import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.semantics.elementFor
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
@@ -89,6 +91,94 @@
}
@Test
+ fun delegated_androidWearCrownRotation_triggersRotaryEvent() {
+ val node = object : DelegatingNode() {
+ val rse = delegate(object : RotaryInputModifierNode, Modifier.Node() {
+ override fun onRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+ receivedEvent = event
+ return true
+ }
+ override fun onPreRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+ return false
+ }
+ })
+ }
+ // Arrange.
+ ContentWithInitialFocus {
+ Box(
+ modifier = Modifier
+ .elementFor(node)
+ .focusable(initiallyFocused = true)
+ )
+ }
+
+ // Act.
+ rule.runOnIdle {
+ rootView.dispatchGenericMotionEvent(
+ MotionEventBuilder.newBuilder()
+ .setAction(ACTION_SCROLL)
+ .setSource(SOURCE_ROTARY_ENCODER)
+ .build()
+ )
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(receivedEvent).isNotNull()
+ }
+ }
+
+ @Test
+ fun delegated_multiple_androidWearCrownRotation_triggersRotaryEvent() {
+ var event1: RotaryScrollEvent? = null
+ var event2: RotaryScrollEvent? = null
+ val node = object : DelegatingNode() {
+ val a = delegate(object : RotaryInputModifierNode, Modifier.Node() {
+ override fun onRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+ event1 = event
+ return false
+ }
+ override fun onPreRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+ return false
+ }
+ })
+ val b = delegate(object : RotaryInputModifierNode, Modifier.Node() {
+ override fun onRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+ event2 = event
+ return false
+ }
+ override fun onPreRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+ return false
+ }
+ })
+ }
+ // Arrange.
+ ContentWithInitialFocus {
+ Box(
+ modifier = Modifier
+ .elementFor(node)
+ .focusable(initiallyFocused = true)
+ )
+ }
+
+ // Act.
+ rule.runOnIdle {
+ rootView.dispatchGenericMotionEvent(
+ MotionEventBuilder.newBuilder()
+ .setAction(ACTION_SCROLL)
+ .setSource(SOURCE_ROTARY_ENCODER)
+ .build()
+ )
+ }
+
+ // Assert.
+ rule.runOnIdle {
+ assertThat(event1).isNotNull()
+ assertThat(event2).isNotNull()
+ }
+ }
+
+ @Test
fun focusedItemReceivesHorizontalRotaryEvent() {
// Arrange.
ContentWithInitialFocus {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
index 6d2ce4d..d0c65cd 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
@@ -41,11 +41,14 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.GlobalPositionAwareModifierNode
import androidx.compose.ui.padding
import androidx.compose.ui.platform.AndroidComposeView
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.semantics.elementFor
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
@@ -546,6 +549,76 @@
}
@Test
+ fun delegatedGloballyPositionedNode() {
+ val paddingLeftPx = 100.0f
+ val paddingTopPx = 120.0f
+ var realLeft: Float? = null
+ var realTop: Float? = null
+
+ val positionedLatch = CountDownLatch(1)
+ val node = object : DelegatingNode() {
+ val ogp = delegate(
+ object : GlobalPositionAwareModifierNode, Modifier.Node() {
+ override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+ realLeft = coordinates.positionInParent().x
+ realTop = coordinates.positionInParent().y
+ positionedLatch.countDown()
+ }
+ }
+ )
+ }
+ rule.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .padding(start = paddingLeftPx.toDp(), top = paddingTopPx.toDp())
+ .elementFor(node)
+ )
+ }
+ }
+ assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+
+ assertThat(paddingLeftPx).isEqualTo(realLeft)
+ assertThat(paddingTopPx).isEqualTo(realTop)
+ }
+
+ @Test
+ fun delegatedMultipleGloballyPositionedNodes() {
+ val paddingLeftPx = 100.0f
+ val paddingTopPx = 120.0f
+
+ val positionedLatch = CountDownLatch(2)
+ val node = object : DelegatingNode() {
+ val a = delegate(
+ object : GlobalPositionAwareModifierNode, Modifier.Node() {
+ override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+ positionedLatch.countDown()
+ }
+ }
+ )
+ val b = delegate(
+ object : GlobalPositionAwareModifierNode, Modifier.Node() {
+ override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+ positionedLatch.countDown()
+ }
+ }
+ )
+ }
+ rule.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier
+ .fillMaxSize()
+ .padding(start = paddingLeftPx.toDp(), top = paddingTopPx.toDp())
+ .elementFor(node)
+ )
+ }
+ }
+ assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+ }
+
+ @Test
fun nestedLayoutCoordinates() {
val firstPaddingPx = 10f
val secondPaddingPx = 20f
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
index 250b916c..b825a77 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
@@ -24,10 +24,14 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.LayoutAwareModifierNode
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.elementFor
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.filters.SmallTest
@@ -269,4 +273,147 @@
}
assertNotEquals(Modifier.onSizeChanged(lambda1), Modifier.onSizeChanged(lambda2))
}
+
+ @Test
+ @SmallTest
+ fun delegatedSizeChanged() {
+ var latch = CountDownLatch(1)
+ var changedSize = IntSize.Zero
+ var sizePx by mutableStateOf(10)
+ val node = object : DelegatingNode() {
+ val osc = delegate(
+ object : LayoutAwareModifierNode, Modifier.Node() {
+ override fun onRemeasured(size: IntSize) {
+ changedSize = size
+ latch.countDown()
+ }
+ }
+ )
+ }
+
+ rule.runOnUiThread {
+ activity.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier.padding(10.toDp()).elementFor(node)
+ ) {
+ Box(Modifier.requiredSize(sizePx.toDp()))
+ }
+ }
+ }
+ }
+
+ // Initial setting will call onSizeChanged
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ assertEquals(10, changedSize.height)
+ assertEquals(10, changedSize.width)
+
+ latch = CountDownLatch(1)
+ sizePx = 20
+
+ // We've changed the size of the contents, so we should receive a onSizeChanged call
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ assertEquals(20, changedSize.height)
+ assertEquals(20, changedSize.width)
+ }
+
+ @Test
+ @SmallTest
+ fun multipleDelegatedSizeChanged() {
+ var latch = CountDownLatch(2)
+ var changedSize1 = IntSize.Zero
+ var changedSize2 = IntSize.Zero
+ var sizePx by mutableStateOf(10)
+ val node = object : DelegatingNode() {
+ val a = delegate(
+ object : LayoutAwareModifierNode, Modifier.Node() {
+ override fun onRemeasured(size: IntSize) {
+ changedSize1 = size
+ latch.countDown()
+ }
+ }
+ )
+ val b = delegate(
+ object : LayoutAwareModifierNode, Modifier.Node() {
+ override fun onRemeasured(size: IntSize) {
+ changedSize2 = size
+ latch.countDown()
+ }
+ }
+ )
+ }
+
+ rule.runOnUiThread {
+ activity.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier.padding(10.toDp()).elementFor(node)
+ ) {
+ Box(Modifier.requiredSize(sizePx.toDp()))
+ }
+ }
+ }
+ }
+
+ // Initial setting will call onSizeChanged
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ assertEquals(10, changedSize1.height)
+ assertEquals(10, changedSize1.width)
+ assertEquals(10, changedSize2.height)
+ assertEquals(10, changedSize2.width)
+
+ latch = CountDownLatch(2)
+ sizePx = 20
+
+ // We've changed the size of the contents, so we should receive a onSizeChanged call
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ assertEquals(20, changedSize1.height)
+ assertEquals(20, changedSize1.width)
+ assertEquals(20, changedSize2.height)
+ assertEquals(20, changedSize2.width)
+ }
+
+ @Test
+ @SmallTest
+ fun multipleDelegatedOnPlaced() {
+ var latch = CountDownLatch(2)
+ var paddingDp by mutableStateOf(10)
+ val node = object : DelegatingNode() {
+ val a = delegate(
+ object : LayoutAwareModifierNode, Modifier.Node() {
+ override fun onPlaced(coordinates: LayoutCoordinates) {
+ latch.countDown()
+ }
+ }
+ )
+ val b = delegate(
+ object : LayoutAwareModifierNode, Modifier.Node() {
+ override fun onPlaced(coordinates: LayoutCoordinates) {
+ latch.countDown()
+ }
+ }
+ )
+ }
+
+ rule.runOnUiThread {
+ activity.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier.padding(paddingDp.toDp()).elementFor(node)
+ ) {
+ Box(Modifier.requiredSize(10.dp))
+ }
+ }
+ }
+ }
+
+ // Initial setting will call onSizeChanged
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+
+ latch = CountDownLatch(2)
+ paddingDp = 20
+
+ // We've changed the size of the contents, so we should receive a onSizeChanged call
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ }
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt
index 6409344..9748b17 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt
@@ -958,13 +958,13 @@
}
class Node(var onReset: () -> Unit) : DelegatingNode() {
- private val inner = delegated {
+ private val inner = delegate(
object : Modifier.Node() {
override fun onReset() {
[email protected]()
}
}
- }
+ )
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/DelegatableNodeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/DelegatableNodeTest.kt
index 081c512..e3d113b 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/DelegatableNodeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/DelegatableNodeTest.kt
@@ -513,7 +513,7 @@
// Act.
val child = rule.runOnIdle {
- node1.firstChild(Nodes.Any)
+ node1.child
}
// Assert.
@@ -534,27 +534,7 @@
// Act.
val child = rule.runOnIdle {
- node1.firstChild(Nodes.Any)
- }
-
- // Assert.
- assertThat(child).isEqualTo(node2)
- }
-
- @Test
- fun firstChild_differentLayoutNode() {
- // Arrange.
- val (node1, node2, node3) = List(3) { object : Modifier.Node() {} }
- rule.setContent {
- Box(modifier = modifierElementOf { node1 }) {
- Box(modifier = modifierElementOf { node2 }
- .then(modifierElementOf { node3 }))
- }
- }
-
- // Act.
- val child = rule.runOnIdle {
- node1.firstChild(Nodes.Any)
+ node1.child
}
// Assert.
@@ -576,7 +556,7 @@
// Act.
val child = rule.runOnIdle {
- node1.firstChild(Nodes.Any)
+ node1.child
}
// Assert.
@@ -586,9 +566,9 @@
@Test
fun delegatedNodeGetsCoordinator() {
val node = object : DelegatingNode() {
- val inner = delegated {
+ val inner = delegate(
object : Modifier.Node() { }
- }
+ )
}
rule.setContent {
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 8b7397d..10bf2e4 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
@@ -35,6 +35,8 @@
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.MeasurePolicy
import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectableValue
import androidx.compose.ui.platform.ValueElement
import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
@@ -306,6 +308,23 @@
}
@Test
+ fun higherUpSemanticsOverridePropertiesOfLowerSemanticsOnSameNode() {
+ rule.setContent {
+ Box(Modifier
+ .testTag("tag")
+ .semantics { contentDescription = "high" }
+ .semantics { contentDescription = "low" }
+ )
+ }
+
+ rule
+ .onNodeWithTag("tag")
+ .assert(
+ SemanticsMatcher.expectValue(SemanticsProperties.ContentDescription, listOf("high"))
+ )
+ }
+
+ @Test
fun replacedChildren_includeFakeNodes() {
val tag = "tag1"
rule.setContent {
@@ -868,6 +887,52 @@
root.children[1].config.getOrNull(SemanticsProperties.TestTag)
)
}
+
+ @Test
+ fun delegatedSemanticsPropertiesGetRead() {
+ val node = object : DelegatingNode() {
+ val inner = delegate(SemanticsMod {
+ contentDescription = "hello world"
+ })
+ }
+
+ rule.setContent {
+ Box(
+ Modifier
+ .testTag(TestTag)
+ .elementFor(node)
+ )
+ }
+
+ rule
+ .onNodeWithTag(TestTag)
+ .assertContentDescriptionEquals("hello world")
+ }
+
+ @Test
+ fun multipleDelegatesGetCombined() {
+ val node = object : DelegatingNode() {
+ val a = delegate(SemanticsMod {
+ contentDescription = "hello world"
+ })
+ val b = delegate(SemanticsMod {
+ testProperty = "bar"
+ })
+ }
+
+ rule.setContent {
+ Box(
+ Modifier
+ .testTag(TestTag)
+ .elementFor(node)
+ )
+ }
+
+ rule
+ .onNodeWithTag(TestTag)
+ .assertContentDescriptionEquals("hello world")
+ .assertTestPropertyEquals("bar")
+ }
}
private fun SemanticsNodeInteraction.assertDoesNotHaveProperty(property: SemanticsPropertyKey<*>) {
@@ -877,9 +942,9 @@
private val TestProperty = SemanticsPropertyKey<String>("TestProperty") { parent, child ->
if (parent == null) child else "$parent, $child"
}
-private var SemanticsPropertyReceiver.testProperty by TestProperty
+internal var SemanticsPropertyReceiver.testProperty by TestProperty
-private fun SemanticsNodeInteraction.assertTestPropertyEquals(value: String) = assert(
+internal fun SemanticsNodeInteraction.assertTestPropertyEquals(value: String) = assert(
SemanticsMatcher.expectValue(TestProperty, value)
)
@@ -976,3 +1041,24 @@
}
private enum class TestSlot { First, Second }
+
+internal fun SemanticsMod(
+ mergeDescendants: Boolean = false,
+ properties: SemanticsPropertyReceiver.() -> Unit
+): CoreSemanticsModifierNode {
+ return CoreSemanticsModifierNode(
+ SemanticsConfiguration().apply {
+ isMergingSemanticsOfDescendants = mergeDescendants
+ properties()
+ }
+ )
+}
+
+internal fun Modifier.elementFor(node: Modifier.Node): Modifier {
+ return this then NodeElement(node)
+}
+
+internal data class NodeElement(val node: Modifier.Node) : ModifierNodeElement<Modifier.Node>() {
+ override fun create(): Modifier.Node = node
+ override fun update(node: Modifier.Node): Modifier.Node = node
+}
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 3b68870..5497180 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
@@ -119,6 +119,7 @@
import androidx.compose.ui.node.LayoutNode.UsageByParent
import androidx.compose.ui.node.LayoutNodeDrawScope
import androidx.compose.ui.node.MeasureAndLayoutDelegate
+import androidx.compose.ui.node.Nodes
import androidx.compose.ui.node.OwnedLayer
import androidx.compose.ui.node.Owner
import androidx.compose.ui.node.OwnerSnapshotObserver
@@ -127,7 +128,6 @@
import androidx.compose.ui.semantics.EmptySemanticsModifierNodeElement
import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.semantics.findClosestParentNode
-import androidx.compose.ui.semantics.outerSemantics
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.InternalTextApi
import androidx.compose.ui.text.font.Font
@@ -739,7 +739,7 @@
) {
super.onInitializeAccessibilityNodeInfo(host, info)
var parentId = layoutNode
- .findClosestParentNode { it.outerSemantics != null }
+ .findClosestParentNode { it.nodes.has(Nodes.Semantics) }
?.semanticsId
if (parentId == null ||
parentId == semanticsOwner.unmergedRootSemanticsNode.id
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 8eaf240..5ded9eb 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
@@ -56,9 +56,8 @@
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.node.HitTestResult
import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.node.Nodes
import androidx.compose.ui.node.OwnerScope
-import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.collapsedSemanticsConfiguration
import androidx.compose.ui.node.requireLayoutNode
import androidx.compose.ui.platform.accessibility.hasCollectionInfo
import androidx.compose.ui.platform.accessibility.setCollectionInfo
@@ -76,8 +75,8 @@
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.semantics.outerSemantics
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.InternalTextApi
@@ -2032,24 +2031,24 @@
internal fun hitTestSemanticsAt(x: Float, y: Float): Int {
view.measureAndLayout()
- val hitSemanticsEntities = HitTestResult<SemanticsModifierNode>()
+ val hitSemanticsEntities = HitTestResult()
view.root.hitTestSemantics(
pointerPosition = Offset(x, y),
hitSemanticsEntities = hitSemanticsEntities
)
- val wrapper = hitSemanticsEntities.lastOrNull()?.requireLayoutNode()?.outerSemantics
+ val layoutNode = hitSemanticsEntities.lastOrNull()?.requireLayoutNode()
+
var virtualViewId = InvalidId
- if (wrapper != null) {
+ if (layoutNode?.nodes?.has(Nodes.Semantics) == true) {
// The node below is not added to the tree; it's a wrapper around outer semantics to
// use the methods available to the SemanticsNode
- val semanticsNode = SemanticsNode(wrapper, false)
+ val semanticsNode = SemanticsNode(layoutNode, false)
// Do not 'find' invisible nodes when exploring by touch. This will prevent us from
// sending events for invisible nodes
if (semanticsNode.isVisible) {
- val layoutNode = wrapper.requireLayoutNode()
val androidView = view
.androidViewsHandler
.layoutNodeToHolder[layoutNode]
@@ -2212,17 +2211,18 @@
return
}
// When we finally send the event, make sure it is an accessibility-focusable node.
- var semanticsWrapper = layoutNode.outerSemantics
- ?: layoutNode.findClosestParentNode { it.outerSemantics != null }
- ?.outerSemantics ?: return
- if (!semanticsWrapper.collapsedSemanticsConfiguration().isMergingSemanticsOfDescendants) {
- layoutNode.findClosestParentNode {
- it.outerSemantics
- ?.collapsedSemanticsConfiguration()
- ?.isMergingSemanticsOfDescendants == true
- }?.outerSemantics?.let { semanticsWrapper = it }
+ var semanticsNode = if (layoutNode.nodes.has(Nodes.Semantics))
+ layoutNode
+ else
+ layoutNode.findClosestParentNode { it.nodes.has(Nodes.Semantics) }
+
+ val config = semanticsNode?.collapsedSemantics ?: return
+ if (!config.isMergingSemanticsOfDescendants) {
+ semanticsNode.findClosestParentNode {
+ it.collapsedSemantics?.isMergingSemanticsOfDescendants == true
+ }?.let { semanticsNode = it }
}
- val id = semanticsWrapper.requireLayoutNode().semanticsId
+ val id = semanticsNode?.semanticsId ?: return
if (!subtreeChangedSemanticsNodesIds.add(id)) {
return
}
@@ -2376,8 +2376,7 @@
)
// Here we use the merged node
@OptIn(ExperimentalComposeUiApi::class)
- val mergedNode =
- SemanticsNode(newNode.outerSemanticsNode, true)
+ val mergedNode = newNode.copyWithMergingEnabled()
val contentDescription = mergedNode.config.getOrNull(
SemanticsProperties.ContentDescription
)?.fastJoinToString(",")
@@ -3288,14 +3287,12 @@
// text nodes that are part of the 'merged' text field, for example hint or label.
val ancestor = layoutNode.findClosestParentNode {
// looking for text field merging node
- val ancestorSemanticsConfiguration = it.outerSemantics?.collapsedSemanticsConfiguration()
+ val ancestorSemanticsConfiguration = it.collapsedSemantics
ancestorSemanticsConfiguration?.isMergingSemanticsOfDescendants == true &&
ancestorSemanticsConfiguration.contains(SemanticsActions.SetText)
}
return ancestor != null &&
- ancestor.outerSemantics
- ?.collapsedSemanticsConfiguration()
- ?.getOrNull(SemanticsProperties.Focused) != true
+ ancestor.collapsedSemantics?.getOrNull(SemanticsProperties.Focused) != true
}
private fun AccessibilityAction<*>.accessibilityEquals(other: Any?): Boolean {
@@ -3350,11 +3347,12 @@
) {
return
}
+ val touchBoundsInRoot = currentNode.touchBoundsInRoot
val boundsInRoot = android.graphics.Rect(
- currentNode.touchBoundsInRoot.left.roundToInt(),
- currentNode.touchBoundsInRoot.top.roundToInt(),
- currentNode.touchBoundsInRoot.right.roundToInt(),
- currentNode.touchBoundsInRoot.bottom.roundToInt(),
+ touchBoundsInRoot.left.roundToInt(),
+ touchBoundsInRoot.top.roundToInt(),
+ touchBoundsInRoot.right.roundToInt(),
+ touchBoundsInRoot.bottom.roundToInt(),
)
val region = Region().also { it.set(boundsInRoot) }
val virtualViewId = if (currentNode.id == root.id) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
index 8c3731a..eb96674 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
@@ -19,9 +19,11 @@
import androidx.compose.runtime.Stable
import androidx.compose.ui.internal.JvmDefaultWithCompatibility
import androidx.compose.ui.node.DelegatableNode
+import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.ModifierNodeOwnerScope
import androidx.compose.ui.node.NodeCoordinator
import androidx.compose.ui.node.NodeKind
+import androidx.compose.ui.node.invalidateDraw
import androidx.compose.ui.node.requireOwner
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -214,6 +216,24 @@
var isAttached: Boolean = false
private set
+ /**
+ * If this property returns `true`, then nodes will be automatically invalidated after the
+ * modifier update completes (For example, if the returned Node is a [DrawModifierNode], its
+ * [DrawModifierNode.invalidateDraw] function will be invoked automatically as part of
+ * auto invalidation).
+ *
+ * This is enabled by default, and provides a convenient mechanism to schedule invalidation
+ * and apply changes made to the modifier. You may choose to set this to `false` if your
+ * modifier has auto-invalidatable properties that do not frequently require invalidation to
+ * improve performance by skipping unnecessary invalidation. If `autoInvalidate` is set to
+ * `false`, you must call the appropriate invalidate functions manually when the modifier
+ * is updated or else the updates may not be reflected in the UI appropriately.
+ */
+ @Suppress("GetterSetterNames")
+ @get:Suppress("GetterSetterNames")
+ open val shouldAutoInvalidate: Boolean
+ get() = true
+
internal open fun updateCoordinator(coordinator: NodeCoordinator?) {
this.coordinator = coordinator
}
@@ -239,8 +259,6 @@
it.cancel()
scope = null
}
- // coordinator = null
- // TODO(lmr): cancel jobs / side effects?
}
internal open fun reset() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt
index 1f3306f..ef755f7 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/DrawModifier.kt
@@ -111,7 +111,7 @@
}
@OptIn(ExperimentalComposeUiApi::class)
-private class DrawBackgroundModifier(
+internal class DrawBackgroundModifier(
var onDraw: DrawScope.() -> Unit
) : Modifier.Node(), DrawModifierNode {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
index f2a353b..099388d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
@@ -38,7 +38,7 @@
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.invalidateDraw
import androidx.compose.ui.node.invalidateLayer
-import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateMeasurement
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
@@ -78,11 +78,6 @@
/**
* Customized [ModifierNodeElement] for painting content using [painter].
*
- * IMPORTANT NOTE: This class sets [androidx.compose.ui.node.ModifierNodeElement.autoInvalidate]
- * to false which means it MUST invalidate both draw and the layout. It invalidates both in the
- * [PainterModifierNodeElement.update] method through [LayoutModifierNode.invalidateLayer]
- * (invalidates draw) and [LayoutModifierNode.invalidateMeasurements] (invalidates measure).
- *
* @param painter used to paint content
* @param sizeToIntrinsics `true` to size the element relative to [Painter.intrinsicSize]
* @param alignment specifies alignment of the [painter] relative to content
@@ -100,9 +95,6 @@
val alpha: Float,
val colorFilter: ColorFilter?
) : ModifierNodeElement<PainterModifierNode>() {
- override val autoInvalidate: Boolean
- get() = false
-
override fun create(): PainterModifierNode {
return PainterModifierNode(
painter = painter,
@@ -127,7 +119,7 @@
// Only remeasure if intrinsics have changed.
if (intrinsicsChanged) {
- node.invalidateMeasurements()
+ node.invalidateMeasurement()
}
// redraw because one of the node properties has changed.
node.invalidateDraw()
@@ -149,6 +141,12 @@
/**
* [DrawModifier] used to draw the provided [Painter] followed by the contents
* of the component itself
+ *
+ *
+ * IMPORTANT NOTE: This class sets [androidx.compose.ui.Modifier.Node.shouldAutoInvalidate]
+ * to false which means it MUST invalidate both draw and the layout. It invalidates both in the
+ * [PainterModifierNodeElement.update] method through [LayoutModifierNode.invalidateLayer]
+ * (invalidates draw) and [LayoutModifierNode.invalidateLayout] (invalidates layout).
*/
@OptIn(ExperimentalComposeUiApi::class)
private class PainterModifierNode(
@@ -168,6 +166,9 @@
private val useIntrinsicSize: Boolean
get() = sizeToIntrinsics && painter.intrinsicSize.isSpecified
+ override val shouldAutoInvalidate: Boolean
+ get() = false
+
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifierNode.kt
index 12fd0c7..1fb8667 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifierNode.kt
@@ -22,8 +22,9 @@
import androidx.compose.ui.focus.FocusStateImpl.Inactive
import androidx.compose.ui.node.DelegatableNode
import androidx.compose.ui.node.Nodes
-import androidx.compose.ui.node.visitAncestors
-import androidx.compose.ui.node.visitChildren
+import androidx.compose.ui.node.requireOwner
+import androidx.compose.ui.node.visitSelfAndAncestors
+import androidx.compose.ui.node.visitSelfAndChildren
/**
* Implement this interface create a modifier node that can be used to observe focus state changes
@@ -38,14 +39,18 @@
fun onFocusEvent(focusState: FocusState)
}
+internal fun FocusEventModifierNode.invalidateFocusEvent() {
+ requireOwner().focusOwner.scheduleInvalidation(this)
+}
+
internal fun FocusEventModifierNode.getFocusState(): FocusState {
- visitChildren(Nodes.FocusTarget) {
+ visitSelfAndChildren(Nodes.FocusTarget) {
when (val focusState = it.focusStateImpl) {
// If we find a focused child, we use that child's state as the aggregated state.
Active, ActiveParent, Captured -> return focusState
// We use the Inactive state only if we don't have a focused child.
// ie. we ignore this child if another child provides aggregated state.
- Inactive -> return@visitChildren
+ Inactive -> return@visitSelfAndChildren
}
}
return Inactive
@@ -58,13 +63,8 @@
* Make this public after [FocusTargetModifierNode] is made public.
*/
internal fun FocusTargetModifierNode.refreshFocusEventNodes() {
- visitAncestors(Nodes.FocusEvent or Nodes.FocusTarget) {
- // If we reach the previous focus target node, we have gone too far, as
- // this is applies to the another focus event.
- if (it.isKind(Nodes.FocusTarget)) return
-
+ visitSelfAndAncestors(Nodes.FocusEvent, untilType = Nodes.FocusTarget) {
// TODO(251833873): Consider caching it.getFocusState().
- check(it is FocusEventModifierNode)
it.onFocusEvent(it.getFocusState())
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
index 6217e96..6268fa8 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
@@ -16,17 +16,15 @@
package androidx.compose.ui.focus
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.focus.FocusStateImpl.Inactive
import androidx.compose.ui.node.Nodes
-import androidx.compose.ui.node.visitChildren
+import androidx.compose.ui.node.visitSelfAndChildren
/**
* The [FocusInvalidationManager] allows us to schedule focus related nodes for invalidation.
* These nodes are invalidated after onApplyChanges. It does this by registering an
* onApplyChangesListener when nodes are scheduled for invalidation.
*/
-@OptIn(ExperimentalComposeUiApi::class)
internal class FocusInvalidationManager(
private val onRequestApplyChangesListener: (() -> Unit) -> Unit
) {
@@ -62,7 +60,7 @@
private val invalidateNodes: () -> Unit = {
// Process all the invalidated FocusProperties nodes.
focusPropertiesNodes.forEach {
- it.visitChildren(Nodes.FocusTarget) { focusTarget ->
+ it.visitSelfAndChildren(Nodes.FocusTarget) { focusTarget ->
focusTargetNodes.add(focusTarget)
}
}
@@ -84,7 +82,7 @@
var requiresUpdate = true
var aggregatedNode = false
var focusTarget: FocusTargetModifierNode? = null
- focusEventNode.visitChildren(Nodes.FocusTarget) {
+ focusEventNode.visitSelfAndChildren(Nodes.FocusTarget) {
// If there are multiple focus targets associated with this focus event node,
// we need to calculate the aggregated state.
@@ -101,7 +99,7 @@
if (focusTargetNodes.contains(it)) {
requiresUpdate = false
focusTargetsWithInvalidatedFocusEvents.add(it)
- return@visitChildren
+ return@visitSelfAndChildren
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
index 638594b..35f5332 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
@@ -33,13 +33,13 @@
import androidx.compose.ui.focus.FocusStateImpl.Inactive
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.input.key.KeyInputModifierNode
import androidx.compose.ui.input.rotary.RotaryScrollEvent
import androidx.compose.ui.node.DelegatableNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.NodeKind
import androidx.compose.ui.node.Nodes
import androidx.compose.ui.node.ancestors
+import androidx.compose.ui.node.dispatchForKind
import androidx.compose.ui.node.nearestAncestor
import androidx.compose.ui.node.visitLocalChildren
import androidx.compose.ui.platform.InspectorInfo
@@ -185,7 +185,7 @@
"Event can't be processed because we do not have an active focus target."
}
val focusedKeyInputNode = activeFocusTarget.lastLocalKeyInputNode()
- ?: activeFocusTarget.nearestAncestor(Nodes.KeyInput)
+ ?: activeFocusTarget.nearestAncestor(Nodes.KeyInput)?.node
focusedKeyInputNode?.traverseAncestors(
type = Nodes.KeyInput,
@@ -236,15 +236,15 @@
focusInvalidationManager.scheduleInvalidation(node)
}
- private inline fun <reified T : DelegatableNode> T.traverseAncestors(
+ private inline fun <reified T : DelegatableNode> DelegatableNode.traverseAncestors(
type: NodeKind<T>,
onPreVisit: (T) -> Unit,
onVisit: (T) -> Unit
) {
val ancestors = ancestors(type)
ancestors?.fastForEachReversed(onPreVisit)
- onPreVisit(this)
- onVisit(this)
+ node.dispatchForKind(type, onPreVisit)
+ node.dispatchForKind(type, onVisit)
ancestors?.fastForEach(onVisit)
}
@@ -255,12 +255,11 @@
return rootFocusNode.findActiveFocusNode()?.focusRect()
}
- private fun DelegatableNode.lastLocalKeyInputNode(): KeyInputModifierNode? {
- var focusedKeyInputNode: KeyInputModifierNode? = null
+ private fun DelegatableNode.lastLocalKeyInputNode(): Modifier.Node? {
+ var focusedKeyInputNode: Modifier.Node? = null
visitLocalChildren(Nodes.FocusTarget or Nodes.KeyInput) { modifierNode ->
if (modifierNode.isKind(Nodes.FocusTarget)) return focusedKeyInputNode
- check(modifierNode is KeyInputModifierNode)
focusedKeyInputNode = modifierNode
}
return focusedKeyInputNode
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 bf18f73..fd7f579 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
@@ -17,6 +17,7 @@
package androidx.compose.ui.focus
import androidx.compose.ui.node.DelegatableNode
+import androidx.compose.ui.node.requireOwner
/**
* Implement this interface create a modifier node that can be used to modify the focus properties
@@ -31,3 +32,7 @@
*/
fun modifyFocusProperties(focusProperties: FocusProperties)
}
+
+internal 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/FocusRequesterModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifierNode.kt
index a24d056..5b32dc9 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifierNode.kt
@@ -20,7 +20,7 @@
import androidx.compose.ui.focus.FocusDirection.Companion.Enter
import androidx.compose.ui.node.DelegatableNode
import androidx.compose.ui.node.Nodes
-import androidx.compose.ui.node.visitChildren
+import androidx.compose.ui.node.visitSelfAndChildren
/**
* Implement this interface to create a modifier node that can be used to request changes in
@@ -37,7 +37,7 @@
*/
@OptIn(ExperimentalComposeUiApi::class)
fun FocusRequesterModifierNode.requestFocus(): Boolean {
- visitChildren(Nodes.FocusTarget) { focusTarget ->
+ visitSelfAndChildren(Nodes.FocusTarget) { focusTarget ->
val focusProperties = focusTarget.fetchFocusProperties()
return if (focusProperties.canFocus) {
focusTarget.requestFocus()
@@ -66,7 +66,7 @@
* @sample androidx.compose.ui.samples.CaptureFocusSample
*/
fun FocusRequesterModifierNode.captureFocus(): Boolean {
- visitChildren(Nodes.FocusTarget) {
+ visitSelfAndChildren(Nodes.FocusTarget) {
if (it.captureFocus()) {
return true
}
@@ -89,7 +89,7 @@
* @sample androidx.compose.ui.samples.CaptureFocusSample
*/
fun FocusRequesterModifierNode.freeFocus(): Boolean {
- visitChildren(Nodes.FocusTarget) {
+ visitSelfAndChildren(Nodes.FocusTarget) {
if (it.freeFocus()) return true
}
return false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetModifierNode.kt
index 5b5c194..f09430b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetModifierNode.kt
@@ -29,9 +29,11 @@
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.Nodes
import androidx.compose.ui.node.ObserverNode
+import androidx.compose.ui.node.dispatchForKind
import androidx.compose.ui.node.observeReads
import androidx.compose.ui.node.requireOwner
import androidx.compose.ui.node.visitAncestors
+import androidx.compose.ui.node.visitSelfAndAncestors
import androidx.compose.ui.platform.InspectorInfo
/**
@@ -83,13 +85,7 @@
*/
internal fun fetchFocusProperties(): FocusProperties {
val properties = FocusPropertiesImpl()
- visitAncestors(Nodes.FocusProperties or Nodes.FocusTarget) {
- // If we reach the previous default focus properties node, we have gone too far, as
- // this is applies to the parent focus modifier.
- if (it.isKind(Nodes.FocusTarget)) return properties
-
- // Parent can override any values set by this
- check(it is FocusPropertiesModifierNode)
+ visitSelfAndAncestors(Nodes.FocusProperties, untilType = Nodes.FocusTarget) {
it.modifyFocusProperties(properties)
}
return properties
@@ -169,11 +165,23 @@
}
internal fun scheduleInvalidationForFocusEvents() {
+ // include possibility for ourselves to also be a focus event modifier node in case
+ // we are being delegated to
+ node.dispatchForKind(Nodes.FocusEvent) { eventNode ->
+ eventNode.invalidateFocusEvent()
+ }
+ // Since this is potentially called while _this_ node is getting detached, it is possible
+ // that the nodes above us are already detached, thus, we check for isAttached here.
+ // We should investigate changing the order that children.detach() is called relative to
+ // actually nulling out / detaching ones self.
visitAncestors(Nodes.FocusEvent or Nodes.FocusTarget) {
if (it.isKind(Nodes.FocusTarget)) return@visitAncestors
- check(it is FocusEventModifierNode)
- requireOwner().focusOwner.scheduleInvalidation(it)
+ if (it.isAttached) {
+ it.dispatchForKind(Nodes.FocusEvent) { eventNode ->
+ eventNode.invalidateFocusEvent()
+ }
+ }
}
}
@@ -190,3 +198,7 @@
override fun equals(other: Any?) = other === this
}
}
+
+internal fun FocusTargetModifierNode.invalidateFocusTarget() {
+ requireOwner().focusOwner.scheduleInvalidation(this)
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
index ba495f2..3f5ac4b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
@@ -399,12 +399,6 @@
val spotShadowColor: Color,
val compositingStrategy: CompositingStrategy
) : ModifierNodeElement<SimpleGraphicsLayerModifier>() {
-
- /**
- * [SimpleGraphicsLayerModifier.invalidateLayerBlock] is doing the manual invalidation.
- */
- override val autoInvalidate = false
-
override fun create(): SimpleGraphicsLayerModifier {
return SimpleGraphicsLayerModifier(
scaleX = scaleX,
@@ -549,13 +543,6 @@
private data class BlockGraphicsLayerElement(
val block: GraphicsLayerScope.() -> Unit
) : ModifierNodeElement<BlockGraphicsLayerModifier>() {
-
- /**
- * We can skip remeasuring as we only need to rerun the placement block. we request it
- * manually in the [update] block.
- */
- override val autoInvalidate = false
-
override fun create() = BlockGraphicsLayerModifier(block)
override fun update(node: BlockGraphicsLayerModifier) = node.apply {
@@ -573,6 +560,12 @@
var layerBlock: GraphicsLayerScope.() -> Unit,
) : LayoutModifierNode, Modifier.Node() {
+ /**
+ * We can skip remeasuring as we only need to rerun the placement block. we request it
+ * manually in the update block.
+ */
+ override val shouldAutoInvalidate: Boolean get() = false
+
fun invalidateLayerBlock() {
requireCoordinator(Nodes.Layout).wrapped?.updateLayerBlock(
layerBlock,
@@ -615,6 +608,12 @@
var compositingStrategy: CompositingStrategy = CompositingStrategy.Auto
) : LayoutModifierNode, Modifier.Node() {
+ /**
+ * We can skip remeasuring as we only need to rerun the placement block. we request it
+ * manually in the update block.
+ */
+ override val shouldAutoInvalidate: Boolean get() = false
+
private var layerBlock: GraphicsLayerScope.() -> Unit = {
scaleX = [email protected]
scaleY = [email protected]
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 bf51c11..9a7ddac 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
@@ -19,10 +19,11 @@
import androidx.compose.runtime.collection.MutableVector
import androidx.compose.runtime.collection.mutableVectorOf
import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.node.InternalCoreApi
-import androidx.compose.ui.node.PointerInputModifierNode
-import androidx.compose.ui.node.isAttached
+import androidx.compose.ui.node.Nodes
+import androidx.compose.ui.node.dispatchForKind
import androidx.compose.ui.node.layoutCoordinates
import androidx.compose.ui.util.fastFirstOrNull
import androidx.compose.ui.util.fastForEach
@@ -34,7 +35,6 @@
* @property rootCoordinates the root [LayoutCoordinates] that [PointerInputChange]s will be
* relative to.
*/
-@OptIn(InternalCoreApi::class, ExperimentalComposeUiApi::class)
internal class HitPathTracker(private val rootCoordinates: LayoutCoordinates) {
/*@VisibleForTesting*/
@@ -52,14 +52,14 @@
* @param pointerInputNodes The [PointerInputFilter]s that were hit by [pointerId]. Must be
* ordered from ancestor to descendant.
*/
- fun addHitPath(pointerId: PointerId, pointerInputNodes: List<PointerInputModifierNode>) {
+ fun addHitPath(pointerId: PointerId, pointerInputNodes: List<Modifier.Node>) {
var parent: NodeParent = root
var merging = true
eachPin@ for (i in pointerInputNodes.indices) {
val pointerInputNode = pointerInputNodes[i]
if (merging) {
val node = parent.children.firstOrNull {
- it.pointerInputNode == pointerInputNode
+ it.modifierNode == pointerInputNode
}
if (node != null) {
node.markIsIn()
@@ -226,7 +226,7 @@
var index = 0
while (index < children.size) {
val child = children[index]
- if (!child.pointerInputNode.isAttached) {
+ if (!child.modifierNode.isAttached) {
children.removeAt(index)
child.dispatchCancel()
} else {
@@ -252,7 +252,7 @@
*/
/*@VisibleForTesting*/
@OptIn(InternalCoreApi::class, ExperimentalComposeUiApi::class)
-internal class Node(val pointerInputNode: PointerInputModifierNode) : NodeParent() {
+internal class Node(val modifierNode: Modifier.Node) : NodeParent() {
// Note: this is essentially a set, and writes should be guarded accordingly. We use a
// MutableVector here instead since a set ends up being quite heavy, and calls to
@@ -293,10 +293,12 @@
val event = pointerEvent!!
val size = coordinates!!.size
// Dispatch on the tunneling pass.
- pointerInputNode.onPointerEvent(event, PointerEventPass.Initial, size)
+ modifierNode.dispatchForKind(Nodes.PointerInput) {
+ it.onPointerEvent(event, PointerEventPass.Initial, size)
+ }
// Dispatch to children.
- if (pointerInputNode.isAttached) {
+ if (modifierNode.isAttached) {
children.forEach {
it.dispatchMainEventPass(
// Pass only the already-filtered and position-translated changes down to
@@ -309,9 +311,11 @@
}
}
- if (pointerInputNode.isAttached) {
+ if (modifierNode.isAttached) {
// Dispatch on the bubbling pass.
- pointerInputNode.onPointerEvent(event, PointerEventPass.Main, size)
+ modifierNode.dispatchForKind(Nodes.PointerInput) {
+ it.onPointerEvent(event, PointerEventPass.Main, size)
+ }
}
}
}
@@ -327,10 +331,12 @@
val event = pointerEvent!!
val size = coordinates!!.size
// Dispatch on the tunneling pass.
- pointerInputNode.onPointerEvent(event, PointerEventPass.Final, size)
+ modifierNode.dispatchForKind(Nodes.PointerInput) {
+ it.onPointerEvent(event, PointerEventPass.Final, size)
+ }
// Dispatch to children.
- if (pointerInputNode.isAttached) {
+ if (modifierNode.isAttached) {
children.forEach { it.dispatchFinalEventPass(internalPointerEvent) }
}
}
@@ -362,9 +368,11 @@
)
// Avoid future work if we know this node will no-op
- if (!pointerInputNode.isAttached) return true
+ if (!modifierNode.isAttached) return true
- coordinates = pointerInputNode.layoutCoordinates
+ modifierNode.dispatchForKind(Nodes.PointerInput) {
+ coordinates = it.layoutCoordinates
+ }
@OptIn(ExperimentalComposeUiApi::class)
for ((key, change) in changes) {
@@ -475,7 +483,7 @@
}
/**
- * Calls [block] if there are relevant changes, and if [pointerInputNode] is attached
+ * Calls [block] if there are relevant changes, and if [modifierNode] is attached
*
* @return whether [block] was called
*/
@@ -485,7 +493,7 @@
// If there are no relevant changes, there is nothing to process so return false.
if (relevantChanges.isEmpty()) return false
// If the input filter is not attached, avoid dispatching
- if (!pointerInputNode.isAttached) return false
+ if (!modifierNode.isAttached) return false
block()
@@ -502,7 +510,9 @@
*/
override fun dispatchCancel() {
children.forEach { it.dispatchCancel() }
- pointerInputNode.onCancelPointerInput()
+ modifierNode.dispatchForKind(Nodes.PointerInput) {
+ it.onCancelPointerInput()
+ }
}
fun markIsIn() {
@@ -531,7 +541,7 @@
}
override fun toString(): String {
- return "Node(pointerInputFilter=$pointerInputNode, children=$children, " +
+ return "Node(pointerInputFilter=$modifierNode, children=$children, " +
"pointerIds=$pointerIds)"
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
index d9e1f91..dd43434 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
@@ -21,7 +21,6 @@
import androidx.compose.ui.node.HitTestResult
import androidx.compose.ui.node.InternalCoreApi
import androidx.compose.ui.node.LayoutNode
-import androidx.compose.ui.node.PointerInputModifierNode
import androidx.compose.ui.util.fastForEach
internal interface PositionCalculator {
@@ -32,12 +31,11 @@
/**
* The core element that receives [PointerInputEvent]s and process them in Compose UI.
*/
-@OptIn(InternalCoreApi::class, ExperimentalComposeUiApi::class)
internal class PointerInputEventProcessor(val root: LayoutNode) {
private val hitPathTracker = HitPathTracker(root.coordinates)
private val pointerInputChangeEventProducer = PointerInputChangeEventProducer()
- private val hitResult = HitTestResult<PointerInputModifierNode>()
+ private val hitResult = HitTestResult()
/**
* [process] doesn't currently support reentrancy. This prevents reentrant calls
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutId.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutId.kt
index c51cdcc..7c3e9d3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutId.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutId.kt
@@ -52,7 +52,7 @@
* will act as parent data, and can be used for example by parent layouts to associate
* composable children to [Measurable]s when doing layout, as shown below.
*/
-private class LayoutIdModifier(
+internal class LayoutIdModifier(
layoutId: Any,
) : ParentDataModifierNode, LayoutIdParentData, Modifier.Node() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatableNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatableNode.kt
index bb271ac..e326bec 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatableNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatableNode.kt
@@ -28,7 +28,7 @@
* [Modifier.Node] implements this interface, in practice any [Modifier.Node] can be delegated.
*
* @see DelegatingNode
- * @see DelegatingNode.delegated
+ * @see DelegatingNode.delegate
*/
// TODO(lmr): this interface needs a better name
interface DelegatableNode {
@@ -40,6 +40,8 @@
val node: Modifier.Node
}
+internal val DelegatableNode.isDelegationRoot: Boolean get() = node === this
+
// TREE TRAVERSAL APIS
// For now, traversing the node tree and layout node tree will be kept out of public API.
// Some internal modifiers, such as Focus, PointerInput, etc. will all need to utilize this
@@ -69,12 +71,16 @@
return null
}
-internal inline fun DelegatableNode.visitAncestors(mask: Int, block: (Modifier.Node) -> Unit) {
+internal inline fun DelegatableNode.visitAncestors(
+ mask: Int,
+ includeSelf: Boolean = false,
+ block: (Modifier.Node) -> Unit
+) {
// TODO(lmr): we might want to add some safety wheels to prevent this from being called
// while one of the chains is being diffed / updated. Although that might only be
// necessary for visiting subtree.
check(node.isAttached)
- var node: Modifier.Node? = node.parent
+ var node: Modifier.Node? = if (includeSelf) node else node.parent
var layout: LayoutNode? = requireLayoutNode()
while (layout != null) {
val head = layout.nodes.head
@@ -91,28 +97,6 @@
}
}
-internal fun DelegatableNode.ancestors(mask: Int): List<Modifier.Node>? {
- check(node.isAttached)
- var ancestors: MutableList<Modifier.Node>? = null
- var node: Modifier.Node? = node.parent
- var layout: LayoutNode? = requireLayoutNode()
- while (layout != null) {
- val head = layout.nodes.head
- if (head.aggregateChildKindSet and mask != 0) {
- while (node != null) {
- if (node.kindSet and mask != 0) {
- if (ancestors == null) ancestors = mutableListOf()
- ancestors += node
- }
- node = node.parent
- }
- }
- layout = layout.parent
- node = layout?.nodes?.tail
- }
- return ancestors
-}
-
internal fun DelegatableNode.nearestAncestor(mask: Int): Modifier.Node? {
check(node.isAttached)
var node: Modifier.Node? = node.parent
@@ -277,52 +261,83 @@
type: NodeKind<T>,
block: (T) -> Unit
) = visitLocalChildren(type.mask) {
- if (it is T) block(it)
+ it.dispatchForKind(type, block)
}
internal inline fun <reified T> DelegatableNode.visitLocalParents(
type: NodeKind<T>,
block: (T) -> Unit
) = visitLocalParents(type.mask) {
- if (it is T) block(it)
+ it.dispatchForKind(type, block)
}
-internal inline fun <reified T> DelegatableNode.localParent(type: NodeKind<T>): T? =
- localParent(type.mask) as? T
-
-internal inline fun <reified T> DelegatableNode.localChild(type: NodeKind<T>): T? =
- localChild(type.mask) as? T
-
internal inline fun <reified T> DelegatableNode.visitAncestors(
type: NodeKind<T>,
+ includeSelf: Boolean = false,
block: (T) -> Unit
-) = visitAncestors(type.mask) { if (it is T) block(it) }
+) = visitAncestors(type.mask, includeSelf) { it.dispatchForKind(type, block) }
-@Suppress("UNCHECKED_CAST") // Type info lost due to erasure.
+internal inline fun <reified T> DelegatableNode.visitSelfAndAncestors(
+ type: NodeKind<T>,
+ untilType: NodeKind<*>,
+ block: (T) -> Unit
+) {
+ val self = node
+ visitAncestors(type.mask or untilType.mask, true) {
+ if (it !== self && it.isKind(untilType)) return
+ if (it.isKind(type)) {
+ it.dispatchForKind(type, block)
+ }
+ }
+}
+
internal inline fun <reified T> DelegatableNode.ancestors(
type: NodeKind<T>
-): List<T>? = ancestors(type.mask) as? List<T>
+): List<T>? {
+ var result: MutableList<T>? = null
+ visitAncestors(type) {
+ val list = if (result == null) {
+ mutableListOf<T>().also { result = it }
+ } else result!!
+ list += it
+ }
+ return result
+}
-internal inline fun <reified T : Any> DelegatableNode.nearestAncestor(type: NodeKind<T>): T? =
- nearestAncestor(type.mask) as? T
-
-internal inline fun <reified T : Any> DelegatableNode.firstChild(type: NodeKind<T>): T? =
- firstChild(type.mask) as? T
+internal inline fun <reified T : Any> DelegatableNode.nearestAncestor(type: NodeKind<T>): T? {
+ visitAncestors(type) {
+ return it
+ }
+ return null
+}
internal inline fun <reified T> DelegatableNode.visitSubtree(
type: NodeKind<T>,
block: (T) -> Unit
-) = visitSubtree(type.mask) { if (it is T) block(it) }
+) = visitSubtree(type.mask) { it.dispatchForKind(type, block) }
internal inline fun <reified T> DelegatableNode.visitChildren(
type: NodeKind<T>,
block: (T) -> Unit
-) = visitChildren(type.mask) { if (it is T) block(it) }
+) = visitChildren(type.mask) { it.dispatchForKind(type, block) }
+
+internal inline fun <reified T> DelegatableNode.visitSelfAndChildren(
+ type: NodeKind<T>,
+ block: (T) -> Unit
+) {
+ node.dispatchForKind(type, block)
+ visitChildren(type.mask) { it.dispatchForKind(type, block) }
+}
internal inline fun <reified T> DelegatableNode.visitSubtreeIf(
type: NodeKind<T>,
block: (T) -> Boolean
-) = visitSubtreeIf(type.mask) { if (it is T) block(it) else true }
+) = visitSubtreeIf(type.mask) foo@{
+ it.dispatchForKind(type) {
+ if (!block(it)) return@foo false
+ }
+ true
+}
internal fun DelegatableNode.has(type: NodeKind<*>): Boolean =
node.aggregateChildKindSet and type.mask != 0
@@ -367,4 +382,89 @@
if (node.isAttached) {
requireLayoutNode().invalidateSubtree()
}
+}
+
+// It is safe to do this for LayoutModifierNode because we enforce only a single delegate is
+// a LayoutModifierNode, however for other NodeKinds that is not true. As a result, this function
+// is not generic and instead is made specific to LayoutModifierNode.
+internal fun Modifier.Node.asLayoutModifierNode(): LayoutModifierNode? {
+ if (!isKind(Nodes.Layout)) return null
+ if (this is LayoutModifierNode) return this
+ if (this is DelegatingNode) {
+ var node: Modifier.Node? = delegate
+ while (node != null) {
+ if (node is LayoutModifierNode) return node
+ node = if (node is DelegatingNode && node.isKind(Nodes.Layout)) {
+ // NOTE: we can only do this here because we are enforcing that a delegating node
+ // only behaves as a single LayoutModifierNode, not multiple, so we know that if
+ // the node is of kind "Layout", then one of its delegates has to be a
+ // LayoutModifierNode and *none of the other delegates of its parent can be*. As a
+ // result, we can avoid allocating a collection here and instead just dive down into
+ // this delegate directly.
+ node.delegate
+ } else {
+ node.child
+ }
+ }
+ }
+ return null
+}
+
+/**
+ * Since Modifier.Nodes can have multiple delegates of the same type, generally we should use this
+ * method in lieu of casting a modifier.node to a particular NodeKind's interface type. This will
+ * allow us to properly perform operations on the right delegates for a given node instance.
+ *
+ * If a Node implements T, then this will just be called once. if it does NOT implement T, it will
+ * effectively dispatch recursively (although this is implemented iteratively) to all of its direct
+ * delegates where delegate.isKind(kind) is true.
+ *
+ * In the common case of the node implementing the type directly, this method will not allocate,
+ * however it allocates a vector if it dispatches to delegates.
+ */
+internal inline fun <reified T> Modifier.Node.dispatchForKind(
+ kind: NodeKind<T>,
+ block: (T) -> Unit
+) {
+ var stack: MutableVector<Modifier.Node>? = null
+ var node: Modifier.Node? = this
+ while (node != null) {
+ if (node is T) {
+ block(node)
+ } else if (node.isKind(kind) && node is DelegatingNode) {
+ // We jump through a few extra hoops here to avoid the vector allocation in the
+ // case where there is only one delegate node that implements this particular kind.
+ // It is very likely that a delegating node will have one or zero delegates of a
+ // particular kind, so this seems like a worthwhile optimization to make.
+ var count = 0
+ node.forEachImmediateDelegate { next ->
+ if (next.isKind(kind)) {
+ count++
+ if (count == 1) {
+ node = next
+ } else {
+ // turns out there are multiple delegates that implement this kind, so we
+ // have to allocate in this case.
+ stack = stack ?: mutableVectorOf()
+ val theNode = node
+ if (theNode != null) {
+ stack?.add(theNode)
+ node = null
+ }
+ stack?.add(next)
+ }
+ }
+ }
+ if (count == 1) {
+ // if count == 1 then `node` is pointing to the "next" node we need to look at
+ continue
+ }
+ }
+ node = stack.pop()
+ }
+}
+
+private fun MutableVector<Modifier.Node>?.pop(): Modifier.Node? {
+ return if (this == null || isEmpty()) null
+ else removeAt(size - 1)
}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt
index d8ae34d..b011798e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt
@@ -17,82 +17,244 @@
package androidx.compose.ui.node
import androidx.compose.ui.Modifier
+import org.jetbrains.annotations.TestOnly
/**
* A [Modifier.Node] which is able to delegate work to other [Modifier.Node] instances.
*
* This can be useful to compose multiple node implementations into one.
*
- * @sample androidx.compose.ui.samples.DelegatedNodeSample
+ * @sample androidx.compose.ui.samples.DelegatedNodeSampleExplicit
+ * @sample androidx.compose.ui.samples.DelegatedNodeSampleImplicit
+ * @sample androidx.compose.ui.samples.LazyDelegationExample
+ * @sample androidx.compose.ui.samples.ConditionalDelegationExample
+ * @sample androidx.compose.ui.samples.DelegateInAttachSample
*
* @see DelegatingNode
*/
abstract class DelegatingNode : Modifier.Node() {
+
+ /**
+ * This is the kindSet of the node if it had no delegates. This will never change, but kindSet
+ * might, so we cache this value to be able to more efficiently recalculate the kindSet
+ */
+ @Suppress("LeakingThis")
+ internal val selfKindSet: Int = calculateNodeKindSetFrom(this)
override fun updateCoordinator(coordinator: NodeCoordinator?) {
super.updateCoordinator(coordinator)
- forEachDelegate {
+ forEachImmediateDelegate {
it.updateCoordinator(coordinator)
}
}
- private var delegate: Modifier.Node? = null
+ internal var delegate: Modifier.Node? = null
+
+ @TestOnly
+ internal fun <T : DelegatableNode> delegateUnprotected(delegatableNode: T): T =
+ delegate(delegatableNode)
+ @TestOnly
+ internal fun undelegateUnprotected(instance: DelegatableNode) = undelegate(instance)
/**
* In order to properly delegate work to another [Modifier.Node], the delegated instance must
- * be created and returned inside of a [delegated] call. Doing this will
+ * be created and returned inside of a [delegate] call. Doing this will
* ensure that the created node instance follows all of the right lifecycles and is properly
* discoverable in this position of the node tree.
*
- * By using [delegated], the [fn] parameter is executed synchronously, and the result is
- * returned from this function for immediate use.
+ * By using [delegate], the [delegatableNode] parameter is returned from this function for
+ * convenience.
*
* This method can be called from within an `init` block, however the returned delegated node
- * will not be attached until the delegating node is attached. If [delegated] is called after
+ * will not be attached until the delegating node is attached. If [delegate] is called after
* the delegating node is already attached, the returned delegated node will be attached.
*/
- fun <T : Modifier.Node> delegated(fn: () -> T): T {
- val owner = node
- val delegate = fn()
- delegate.setAsDelegateTo(owner)
+ protected fun <T : DelegatableNode> delegate(delegatableNode: T): T {
+ val delegateNode = delegatableNode.node
+ val isAlreadyDelegated = delegateNode !== delegatableNode
+ if (isAlreadyDelegated) {
+ val delegator = (delegatableNode as? Modifier.Node)?.parent
+ val isDelegatedToThisNode = delegateNode === node && delegator == this
+ if (isDelegatedToThisNode) {
+ // nothing left to do
+ return delegatableNode
+ } else {
+ error("Cannot delegate to an already delegated node")
+ }
+ }
+ check(!delegateNode.isAttached) {
+ "Cannot delegate to an already attached node"
+ }
+ // this could be a delegate itself, so we make sure to setAsDelegateTo(node) instead of
+ // setAsDelegateTo(this).
+ delegateNode.setAsDelegateTo(node)
+ val beforeKindSet = kindSet
+ // need to include the delegate's delegates in the calculation
+ val delegatedKindSet = calculateNodeKindSetFromIncludingDelegates(delegateNode)
+ delegateNode.kindSet = delegatedKindSet
+ validateDelegateKindSet(delegatedKindSet, delegateNode)
+
+ // We store the delegates of a node as a singly-linked-list, with the "head" as `delegate`
+ // and the next pointer as `child`.
+ delegateNode.child = delegate
+ delegate = delegateNode
+
+ // for a delegate, parent always points to the node which delegated to it
+ delegateNode.parent = this
+ updateNodeKindSet(kindSet or delegatedKindSet, recalculateOwner = false)
+
if (isAttached) {
- updateCoordinator(owner.coordinator)
- delegate.attach()
+ if (Nodes.Layout in delegatedKindSet && Nodes.Layout !in beforeKindSet) {
+ // We delegated to a layout modifier. In this case, we need to ensure that a new
+ // NodeCoordinator gets created for this node
+ val chain = requireLayoutNode().nodes
+ node.updateCoordinator(null)
+ chain.syncCoordinators()
+ } else {
+ updateCoordinator(coordinator)
+ }
+ delegateNode.attach()
+ autoInvalidateInsertedNode(delegateNode)
}
- addDelegate(delegate)
- return delegate
+ return delegatableNode
}
- private fun addDelegate(node: Modifier.Node) {
- val tail = delegate
- if (tail != null) {
- node.parent = tail
+ /**
+ * This function expects a node which was passed in to [delegate] for this node, and is
+ * currently being delegated to to be passed in as [instance]. After this function returns, the
+ * node will no longer be attached, and will not be an active delegate of this node.
+ *
+ * If [instance] is not an active delegate of this node, this function will throw an
+ * [IllegalStateException].
+ */
+ protected fun undelegate(instance: DelegatableNode) {
+ var prev: Modifier.Node? = null
+ var it: Modifier.Node? = delegate
+ var found = false
+ while (it != null) {
+ if (it === instance) {
+ // remove from delegate chain
+ if (it.isAttached) {
+ autoInvalidateRemovedNode(it)
+ it.detach()
+ }
+ it.setAsDelegateTo(it) // sets "node" back to itself
+ it.aggregateChildKindSet = 0
+ if (prev == null) {
+ this.delegate = it.child
+ } else {
+ prev.child = it.child
+ }
+ it.child = null
+ it.parent = null
+ found = true
+ break
+ }
+ prev = it
+ it = it.child
}
- delegate = node
+ if (found) {
+ val beforeKindSet = kindSet
+ val afterKindSet = calculateNodeKindSetFromIncludingDelegates(this)
+ updateNodeKindSet(afterKindSet, recalculateOwner = true)
+
+ if (isAttached && Nodes.Layout in beforeKindSet && Nodes.Layout !in afterKindSet) {
+ // the delegate getting removed was a layout delegate. As a result, we need
+ // to sync coordinators
+ val chain = requireLayoutNode().nodes
+ node.updateCoordinator(null)
+ chain.syncCoordinators()
+ }
+ } else {
+ error("Could not find delegate: $instance")
+ }
}
- private inline fun forEachDelegate(block: (Modifier.Node) -> Unit) {
+ private fun validateDelegateKindSet(delegateKindSet: Int, delegateNode: Modifier.Node) {
+ val current = kindSet
+ if (Nodes.Layout in delegateKindSet && Nodes.Layout in current) {
+ // at this point, we know that the node was _already_ a layout modifier, and we are
+ // delegating to another layout modifier. In order to properly handle this, we need
+ // to require that the delegating node is itself a LayoutModifierNode to ensure that
+ // they are explicitly handling the combination. If not, we throw, since
+ check(this is LayoutModifierNode) {
+ "Delegating to multiple LayoutModifierNodes without the delegating node " +
+ "implementing LayoutModifierNode itself is not allowed." +
+ "\nDelegating Node: $this" +
+ "\nDelegate Node: $delegateNode"
+ }
+ }
+ }
+
+ private fun updateNodeKindSet(newKindSet: Int, recalculateOwner: Boolean) {
+ val before = kindSet
+ kindSet = newKindSet
+ if (before != newKindSet) {
+ var agg = newKindSet
+ if (isDelegationRoot) {
+ aggregateChildKindSet = agg
+ }
+ // if we changed, then we must update our aggregateChildKindSet of ourselves and
+ // everything up the spine
+
+ if (isAttached) {
+ val owner = node
+ var it: Modifier.Node? = this
+ // first we traverse up the delegate tree until we hit the "owner" node, which is
+ // the node which is actually part of the tree, ie the "root delegating node".
+ // As we iterate here, we update the aggregateChildKindSet as well as the kindSet,
+ // since a delegating node takes on the kinds of the nodes it delegates to.
+ while (it != null) {
+ agg = it.kindSet or agg
+ it.kindSet = agg
+ if (it === owner) break
+ it = it.parent
+ }
+
+ if (recalculateOwner && it === owner) {
+ agg = calculateNodeKindSetFromIncludingDelegates(owner)
+ owner.kindSet = agg
+ }
+
+ agg = agg or (it?.child?.aggregateChildKindSet ?: 0)
+
+ // Now we are traversing the spine of nodes in the actual tree, so we update the
+ // aggregateChildKindSet here, but not the kindSets.
+ while (it != null) {
+ agg = it.kindSet or agg
+ it.aggregateChildKindSet = agg
+ it = it.parent
+ }
+ }
+ }
+ }
+
+ internal inline fun forEachImmediateDelegate(block: (Modifier.Node) -> Unit) {
var node: Modifier.Node? = delegate
while (node != null) {
block(node)
- node = node.parent
+ node = node.child
}
}
override fun attach() {
super.attach()
- forEachDelegate {
+ forEachImmediateDelegate {
it.updateCoordinator(coordinator)
- it.attach()
+ // NOTE: it might already be attached if the delegate was delegated to inside of
+ // onAttach()
+ if (!it.isAttached) {
+ it.attach()
+ }
}
}
override fun detach() {
- forEachDelegate { it.detach() }
+ forEachImmediateDelegate { it.detach() }
super.detach()
}
override fun reset() {
super.reset()
- forEachDelegate { it.reset() }
+ forEachImmediateDelegate { it.reset() }
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt
index 0c2ede1..c41e989 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt
@@ -16,6 +16,7 @@
package androidx.compose.ui.node
+import androidx.compose.ui.Modifier
import androidx.compose.ui.util.unpackFloat1
import androidx.compose.ui.util.unpackInt2
import kotlin.math.sign
@@ -30,7 +31,7 @@
* @see LayoutNode.hitTest
* @see NodeCoordinator.hitTest
*/
-internal class HitTestResult<T> : List<T> {
+internal class HitTestResult : List<Modifier.Node> {
private var values = arrayOfNulls<Any>(16)
// contains DistanceAndInLayer
private var distanceFromEdgeAndInLayer = LongArray(16)
@@ -92,7 +93,7 @@
* Records [node] as a hit, adding it to the [HitTestResult] or replacing the existing one.
* Runs [childHitTest] to do further hit testing for children.
*/
- fun hit(node: T, isInLayer: Boolean, childHitTest: () -> Unit) {
+ fun hit(node: Modifier.Node, isInLayer: Boolean, childHitTest: () -> Unit) {
hitInMinimumTouchTarget(node, -1f, isInLayer, childHitTest)
}
@@ -101,7 +102,7 @@
* Runs [childHitTest] to do further hit testing for children.
*/
fun hitInMinimumTouchTarget(
- node: T,
+ node: Modifier.Node,
distanceFromEdge: Float,
isInLayer: Boolean,
childHitTest: () -> Unit
@@ -124,7 +125,7 @@
* hit.
*/
fun speculativeHit(
- node: T,
+ node: Modifier.Node,
distanceFromEdge: Float,
isInLayer: Boolean,
childHitTest: () -> Unit
@@ -188,9 +189,9 @@
}
}
- override fun contains(element: T): Boolean = indexOf(element) != -1
+ override fun contains(element: Modifier.Node): Boolean = indexOf(element) != -1
- override fun containsAll(elements: Collection<T>): Boolean {
+ override fun containsAll(elements: Collection<Modifier.Node>): Boolean {
elements.forEach {
if (!contains(it)) {
return false
@@ -199,10 +200,9 @@
return true
}
- @Suppress("UNCHECKED_CAST")
- override fun get(index: Int): T = values[index] as T
+ override fun get(index: Int): Modifier.Node = values[index] as Modifier.Node
- override fun indexOf(element: T): Int {
+ override fun indexOf(element: Modifier.Node): Int {
for (i in 0..lastIndex) {
if (values[i] == element) {
return i
@@ -213,9 +213,9 @@
override fun isEmpty(): Boolean = size == 0
- override fun iterator(): Iterator<T> = HitTestResultIterator()
+ override fun iterator(): Iterator<Modifier.Node> = HitTestResultIterator()
- override fun lastIndexOf(element: T): Int {
+ override fun lastIndexOf(element: Modifier.Node): Int {
for (i in lastIndex downTo 0) {
if (values[i] == element) {
return i
@@ -224,11 +224,12 @@
return -1
}
- override fun listIterator(): ListIterator<T> = HitTestResultIterator()
+ override fun listIterator(): ListIterator<Modifier.Node> = HitTestResultIterator()
- override fun listIterator(index: Int): ListIterator<T> = HitTestResultIterator(index)
+ override fun listIterator(index: Int): ListIterator<Modifier.Node> =
+ HitTestResultIterator(index)
- override fun subList(fromIndex: Int, toIndex: Int): List<T> =
+ override fun subList(fromIndex: Int, toIndex: Int): List<Modifier.Node> =
SubList(fromIndex, toIndex)
/**
@@ -243,18 +244,18 @@
var index: Int = 0,
val minIndex: Int = 0,
val maxIndex: Int = size
- ) : ListIterator<T> {
+ ) : ListIterator<Modifier.Node> {
override fun hasNext(): Boolean = index < maxIndex
override fun hasPrevious(): Boolean = index > minIndex
@Suppress("UNCHECKED_CAST")
- override fun next(): T = values[index++] as T
+ override fun next(): Modifier.Node = values[index++] as Modifier.Node
override fun nextIndex(): Int = index - minIndex
@Suppress("UNCHECKED_CAST")
- override fun previous(): T = values[--index] as T
+ override fun previous(): Modifier.Node = values[--index] as Modifier.Node
override fun previousIndex(): Int = index - minIndex - 1
}
@@ -262,13 +263,13 @@
private inner class SubList(
val minIndex: Int,
val maxIndex: Int
- ) : List<T> {
+ ) : List<Modifier.Node> {
override val size: Int
get() = maxIndex - minIndex
- override fun contains(element: T): Boolean = indexOf(element) != -1
+ override fun contains(element: Modifier.Node): Boolean = indexOf(element) != -1
- override fun containsAll(elements: Collection<T>): Boolean {
+ override fun containsAll(elements: Collection<Modifier.Node>): Boolean {
elements.forEach {
if (!contains(it)) {
return false
@@ -277,10 +278,9 @@
return true
}
- @Suppress("UNCHECKED_CAST")
- override fun get(index: Int): T = values[index + minIndex] as T
+ override fun get(index: Int): Modifier.Node = values[index + minIndex] as Modifier.Node
- override fun indexOf(element: T): Int {
+ override fun indexOf(element: Modifier.Node): Int {
for (i in minIndex..maxIndex) {
if (values[i] == element) {
return i - minIndex
@@ -291,9 +291,10 @@
override fun isEmpty(): Boolean = size == 0
- override fun iterator(): Iterator<T> = HitTestResultIterator(minIndex, minIndex, maxIndex)
+ override fun iterator(): Iterator<Modifier.Node> =
+ HitTestResultIterator(minIndex, minIndex, maxIndex)
- override fun lastIndexOf(element: T): Int {
+ override fun lastIndexOf(element: Modifier.Node): Int {
for (i in maxIndex downTo minIndex) {
if (values[i] == element) {
return i - minIndex
@@ -302,13 +303,13 @@
return -1
}
- override fun listIterator(): ListIterator<T> =
+ override fun listIterator(): ListIterator<Modifier.Node> =
HitTestResultIterator(minIndex, minIndex, maxIndex)
- override fun listIterator(index: Int): ListIterator<T> =
+ override fun listIterator(index: Int): ListIterator<Modifier.Node> =
HitTestResultIterator(minIndex + index, minIndex, maxIndex)
- override fun subList(fromIndex: Int, toIndex: Int): List<T> =
+ override fun subList(fromIndex: Int, toIndex: Int): List<Modifier.Node> =
SubList(minIndex + fromIndex, minIndex + toIndex)
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt
index 07917f3..9407b95 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt
@@ -160,10 +160,10 @@
}
@OptIn(ExperimentalComposeUiApi::class)
- override fun <T : DelegatableNode> hitTestChild(
- hitTestSource: HitTestSource<T>,
+ override fun hitTestChild(
+ hitTestSource: HitTestSource,
pointerPosition: Offset,
- hitTestResult: HitTestResult<T>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean,
isInLayer: Boolean
) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutModifierNode.kt
index c1b1344..b8146be 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutModifierNode.kt
@@ -151,7 +151,7 @@
* This invalidates the current node's measure result, and ensures that a remeasurement
* (the measurement block rerun) of this node will happen for the next frame.
*/
-fun LayoutModifierNode.invalidateMeasurements() = requireLayoutNode().invalidateMeasurements()
+fun LayoutModifierNode.invalidateMeasurement() = requireLayoutNode().invalidateMeasurements()
internal fun LayoutModifierNode.requestRemeasure() = requireLayoutNode().requestRemeasure()
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 aff7b04..e42df1f 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
@@ -22,7 +22,6 @@
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusTargetModifierNode
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.input.pointer.PointerInputFilter
@@ -54,7 +53,6 @@
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.simpleIdentityToString
import androidx.compose.ui.semantics.generateSemanticsId
-import androidx.compose.ui.semantics.outerSemantics
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.DpSize
@@ -420,8 +418,7 @@
this.owner = owner
this.depth = (parent?.depth ?: -1) + 1
- @OptIn(ExperimentalComposeUiApi::class)
- if (outerSemantics != null) {
+ if (nodes.has(Nodes.Semantics)) {
owner.onSemanticsChange()
}
owner.onAttach(this)
@@ -471,8 +468,7 @@
layoutDelegate.resetAlignmentLines()
onDetach?.invoke(owner)
- @OptIn(ExperimentalComposeUiApi::class)
- if (outerSemantics != null) {
+ if (nodes.has(Nodes.Semantics)) {
owner.onSemanticsChange()
}
nodes.detach()
@@ -888,10 +884,9 @@
* @param hitTestResult The collection that the hit [PointerInputFilter]s will be
* added to if hit.
*/
- @OptIn(ExperimentalComposeUiApi::class)
internal fun hitTest(
pointerPosition: Offset,
- hitTestResult: HitTestResult<PointerInputModifierNode>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean = false,
isInLayer: Boolean = true
) {
@@ -906,10 +901,9 @@
}
@Suppress("UNUSED_PARAMETER")
- @OptIn(ExperimentalComposeUiApi::class)
internal fun hitTestSemantics(
pointerPosition: Offset,
- hitSemanticsEntities: HitTestResult<SemanticsModifierNode>,
+ hitSemanticsEntities: HitTestResult,
isTouchEvent: Boolean = true,
isInLayer: Boolean = true
) {
@@ -1011,18 +1005,11 @@
}
}
- @OptIn(ExperimentalComposeUiApi::class)
private fun invalidateFocusOnDetach() {
- if (nodes.has(FocusTarget)) {
- nodes.tailToHead {
- if (
- it.isKind(FocusTarget) &&
- it is FocusTargetModifierNode &&
- it.focusState.isFocused
- ) {
- requireOwner().focusOwner.clearFocus(force = true, refreshFocusEvents = false)
- it.scheduleInvalidationForFocusEvents()
- }
+ nodes.tailToHead(FocusTarget) {
+ if (it.focusState.isFocused) {
+ requireOwner().focusOwner.clearFocus(force = true, refreshFocusEvents = false)
+ it.scheduleInvalidationForFocusEvents()
}
}
}
@@ -1220,7 +1207,7 @@
private fun shouldInvalidateParentLayer(): Boolean {
if (nodes.has(Nodes.Draw) && !nodes.has(Nodes.Layout)) return true
nodes.headToTail {
- if (it.isKind(Nodes.Layout) && it is LayoutModifierNode) {
+ if (it.isKind(Nodes.Layout)) {
if (it.requireCoordinator(Nodes.Layout).layer != null) {
return false
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
index 0fa9d4e..23c5790 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
@@ -51,13 +51,15 @@
// the draw pass as with the new modifier.node / coordinator structure this feels
// somewhat error prone.
if (nextDrawNode != null) {
- nextDrawNode.performDraw(canvas)
+ nextDrawNode.dispatchForKind(Nodes.Draw) {
+ it.performDraw(canvas)
+ }
} else {
// TODO(lmr): this is needed in the case that the drawnode is also a measure node,
// but we should think about the right ways to handle this as this is very error
// prone i think
val coordinator = drawNode.requireCoordinator(Nodes.Draw)
- val nextCoordinator = if (coordinator.tail === drawNode)
+ val nextCoordinator = if (coordinator.tail === drawNode.node)
coordinator.wrapped!!
else
coordinator
@@ -71,13 +73,24 @@
val coordinator = requireCoordinator(Nodes.Draw)
val size = coordinator.size.toSize()
val drawScope = coordinator.layoutNode.mDrawScope
- drawScope.draw(canvas, size, coordinator, this)
+ drawScope.drawDirect(canvas, size, coordinator, this)
}
internal fun draw(
canvas: Canvas,
size: Size,
coordinator: NodeCoordinator,
+ drawNode: Modifier.Node,
+ ) {
+ drawNode.dispatchForKind(Nodes.Draw) {
+ drawDirect(canvas, size, coordinator, it)
+ }
+ }
+
+ internal fun drawDirect(
+ canvas: Canvas,
+ size: Size,
+ coordinator: NodeCoordinator,
drawNode: DrawModifierNode,
) {
val previousDrawNode = this.drawNode
@@ -97,7 +110,7 @@
}
@OptIn(ExperimentalComposeUiApi::class)
-private fun DelegatableNode.nextDrawNode(): DrawModifierNode? {
+private fun DelegatableNode.nextDrawNode(): Modifier.Node? {
val drawMask = Nodes.Draw.mask
val measureMask = Nodes.Layout.mask
val child = node.child ?: return null
@@ -106,7 +119,7 @@
while (next != null) {
if (next.kindSet and measureMask != 0) return null
if (next.kindSet and drawMask != 0) {
- return next as DrawModifierNode
+ return next
}
next = next.child
}
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 311cb3c..0e163c2 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
@@ -38,22 +38,6 @@
*/
abstract class ModifierNodeElement<N : Modifier.Node> : Modifier.Element, InspectableValue {
- /**
- * If this property returns `true`, then nodes will be automatically invalidated after the
- * [update] callback completes (For example, if the returned Node is a [DrawModifierNode], its
- * [DrawModifierNode.invalidateDraw] function will be invoked automatically as part of
- * auto invalidation).
- *
- * This is enabled by default, and provides a convenient mechanism to schedule invalidation
- * and apply changes made to the modifier. You may choose to set this to `false` if your
- * modifier has auto-invalidatable properties that do not frequently require invalidation to
- * improve performance by skipping unnecessary invalidation. If `autoInvalidate` is set to
- * `false`, you must call the appropriate invalidate functions manually in [update] for the
- * new attributes to become visible.
- */
- open val autoInvalidate: Boolean
- get() = true
-
private var _inspectorValues: InspectorInfo? = null
private val inspectorValues: InspectorInfo
get() = _inspectorValues ?: InspectorInfo()
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 d99ba77..aa342c6 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
@@ -236,19 +236,20 @@
}
}
- private fun syncCoordinators() {
+ fun syncCoordinators() {
var coordinator: NodeCoordinator = innerCoordinator
var node: Modifier.Node? = tail.parent
while (node != null) {
- if (node.isKind(Nodes.Layout) && node is LayoutModifierNode) {
+ val layoutmod = node.asLayoutModifierNode()
+ if (layoutmod != null) {
val next = if (node.coordinator != null) {
val c = node.coordinator as LayoutModifierNodeCoordinator
val prevNode = c.layoutModifierNode
- c.layoutModifierNode = node
+ c.layoutModifierNode = layoutmod
if (prevNode !== node) c.onLayoutModifierNodeChanged()
c
} else {
- val c = LayoutModifierNodeCoordinator(layoutNode, node)
+ val c = LayoutModifierNodeCoordinator(layoutNode, layoutmod)
node.updateCoordinator(c)
c
}
@@ -540,7 +541,7 @@
): Modifier.Node {
val node = when (element) {
is ModifierNodeElement<*> -> element.create().also {
- it.kindSet = calculateNodeKindSetFrom(it)
+ it.kindSet = calculateNodeKindSetFromIncludingDelegates(it)
}
else -> BackwardsCompatNode(element)
}
@@ -591,15 +592,13 @@
return replaceNode(node, updated)
} else {
// the node was updated. we are done.
- if (next.autoInvalidate) {
- if (updated.isAttached) {
- // the modifier element is labeled as "auto invalidate", which means
- // that since the node was updated, we need to invalidate everything
- // relevant to it.
- autoInvalidateUpdatedNode(updated)
- } else {
- updated.updatedNodeAwaitingAttachForInvalidation = true
- }
+ if (updated.isAttached) {
+ // the modifier element is labeled as "auto invalidate", which means
+ // that since the node was updated, we need to invalidate everything
+ // relevant to it.
+ autoInvalidateUpdatedNode(updated)
+ } else {
+ updated.updatedNodeAwaitingAttachForInvalidation = true
}
return updated
}
@@ -632,7 +631,7 @@
internal inline fun <reified T> headToTail(type: NodeKind<T>, block: (T) -> Unit) {
headToTail(type.mask) {
- if (it is T) block(it)
+ it.dispatchForKind(type, block)
}
}
@@ -671,7 +670,7 @@
internal inline fun <reified T> tailToHead(type: NodeKind<T>, block: (T) -> Unit) {
tailToHead(type.mask) {
- if (it is T) block(it)
+ it.dispatchForKind(type, block)
}
}
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 b0430e4..ab86a7a 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
@@ -41,7 +41,7 @@
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.findRootCoordinates
import androidx.compose.ui.layout.positionInRoot
-import androidx.compose.ui.semantics.outerSemantics
+import androidx.compose.ui.semantics.collapsedSemantics
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
@@ -108,7 +108,7 @@
inline fun <reified T> visitNodes(type: NodeKind<T>, block: (T) -> Unit) {
visitNodes(type.mask, type.includeSelfInTraversal) {
- if (it is T) block(it)
+ it.dispatchForKind(type, block)
}
}
@@ -116,16 +116,8 @@
return headNode(type.includeSelfInTraversal)?.has(type) == true
}
- inline fun <reified T> head(type: NodeKind<T>): T? {
- visitNodes(type.mask, type.includeSelfInTraversal) { return it as? T }
- return null
- }
-
- fun <T> headUnchecked(type: NodeKind<T>): T? {
- visitNodes(type.mask, type.includeSelfInTraversal) {
- @Suppress("UNCHECKED_CAST")
- return it as T
- }
+ fun head(type: NodeKind<*>): Modifier.Node? {
+ visitNodes(type.mask, type.includeSelfInTraversal) { return it }
return null
}
@@ -242,8 +234,10 @@
if (layoutNode.nodes.has(Nodes.ParentData)) {
with(layoutNode.density) {
layoutNode.nodes.tailToHead {
- if (it.isKind(Nodes.ParentData) && it is ParentDataModifierNode) {
- data = with(it) { modifyParentData(data) }
+ if (it.isKind(Nodes.ParentData)) {
+ it.dispatchForKind(Nodes.ParentData) {
+ data = with(it) { modifyParentData(data) }
+ }
}
if (it === thisNode) return@tailToHead
}
@@ -494,14 +488,14 @@
* This can only be `false` when [isTouchEvent] is `true` or else a layer miss means the event
* will be clipped out.
*/
- fun <T : DelegatableNode> hitTest(
- hitTestSource: HitTestSource<T>,
+ fun hitTest(
+ hitTestSource: HitTestSource,
pointerPosition: Offset,
- hitTestResult: HitTestResult<T>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean,
isInLayer: Boolean
) {
- val head = headUnchecked(hitTestSource.entityType())
+ val head = head(hitTestSource.entityType())
if (!withinLayerBounds(pointerPosition)) {
// This missed the clip, but if this layout is too small and this is within the
// minimum touch target, we still consider it a hit.
@@ -566,10 +560,10 @@
* The [NodeCoordinator] had a hit in bounds and can record any children in the
* [hitTestResult].
*/
- private fun <T : DelegatableNode> T?.hit(
- hitTestSource: HitTestSource<T>,
+ private fun Modifier.Node?.hit(
+ hitTestSource: HitTestSource,
pointerPosition: Offset,
- hitTestResult: HitTestResult<T>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean,
isInLayer: Boolean
) {
@@ -577,7 +571,7 @@
hitTestChild(hitTestSource, pointerPosition, hitTestResult, isTouchEvent, isInLayer)
} else {
hitTestResult.hit(this, isInLayer) {
- nextUncheckedUntil(hitTestSource.entityType(), Nodes.Layout)
+ nextUntil(hitTestSource.entityType(), Nodes.Layout)
.hit(hitTestSource, pointerPosition, hitTestResult, isTouchEvent, isInLayer)
}
}
@@ -587,10 +581,10 @@
* The [NodeCoordinator] had a hit [distanceFromEdge] from the bounds and it is within
* the minimum touch target distance, so it should be recorded as such in the [hitTestResult].
*/
- private fun <T : DelegatableNode> T?.hitNear(
- hitTestSource: HitTestSource<T>,
+ private fun Modifier.Node?.hitNear(
+ hitTestSource: HitTestSource,
pointerPosition: Offset,
- hitTestResult: HitTestResult<T>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean,
isInLayer: Boolean,
distanceFromEdge: Float
@@ -604,7 +598,7 @@
distanceFromEdge,
isInLayer
) {
- nextUncheckedUntil(hitTestSource.entityType(), Nodes.Layout).hitNear(
+ nextUntil(hitTestSource.entityType(), Nodes.Layout).hitNear(
hitTestSource,
pointerPosition,
hitTestResult,
@@ -620,10 +614,10 @@
* The [NodeCoordinator] had a miss, but it hasn't been clipped out. The child must be
* checked to see if it hit.
*/
- private fun <T : DelegatableNode> T?.speculativeHit(
- hitTestSource: HitTestSource<T>,
+ private fun Modifier.Node?.speculativeHit(
+ hitTestSource: HitTestSource,
pointerPosition: Offset,
- hitTestResult: HitTestResult<T>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean,
isInLayer: Boolean,
distanceFromEdge: Float
@@ -638,7 +632,7 @@
distanceFromEdge,
isInLayer
) {
- nextUncheckedUntil(hitTestSource.entityType(), Nodes.Layout).speculativeHit(
+ nextUntil(hitTestSource.entityType(), Nodes.Layout).speculativeHit(
hitTestSource,
pointerPosition,
hitTestResult,
@@ -648,7 +642,7 @@
)
}
} else {
- nextUncheckedUntil(hitTestSource.entityType(), Nodes.Layout).speculativeHit(
+ nextUntil(hitTestSource.entityType(), Nodes.Layout).speculativeHit(
hitTestSource,
pointerPosition,
hitTestResult,
@@ -662,10 +656,10 @@
/**
* Do a [hitTest] on the children of this [NodeCoordinator].
*/
- open fun <T : DelegatableNode> hitTestChild(
- hitTestSource: HitTestSource<T>,
+ open fun hitTestChild(
+ hitTestSource: HitTestSource,
pointerPosition: Offset,
- hitTestResult: HitTestResult<T>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean,
isInLayer: Boolean
) {
@@ -1136,17 +1130,17 @@
* used in their implementations are different. This extracts the differences between the
* two methods into a single interface.
*/
- internal interface HitTestSource<N : DelegatableNode> {
+ internal interface HitTestSource {
/**
* Returns the [NodeKind] for the hit test target.
*/
- fun entityType(): NodeKind<N>
+ fun entityType(): NodeKind<*>
/**
* Pointer input hit tests can intercept child hits when enabled. This returns `true`
* if the modifier has requested intercepting.
*/
- fun interceptOutOfBoundsChildEvents(node: N): Boolean
+ fun interceptOutOfBoundsChildEvents(node: Modifier.Node): Boolean
/**
* Returns false if the parent layout node has a state that suppresses
@@ -1160,7 +1154,7 @@
fun childHitTest(
layoutNode: LayoutNode,
pointerPosition: Offset,
- hitTestResult: HitTestResult<N>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean,
isInLayer: Boolean
)
@@ -1210,18 +1204,22 @@
*/
@OptIn(ExperimentalComposeUiApi::class)
val PointerInputSource =
- object : HitTestSource<PointerInputModifierNode> {
+ object : HitTestSource {
override fun entityType() = Nodes.PointerInput
- override fun interceptOutOfBoundsChildEvents(node: PointerInputModifierNode) =
- node.interceptOutOfBoundsChildEvents()
+ override fun interceptOutOfBoundsChildEvents(node: Modifier.Node): Boolean {
+ node.dispatchForKind(Nodes.PointerInput) {
+ if (it.interceptOutOfBoundsChildEvents()) return true
+ }
+ return false
+ }
override fun shouldHitTestChildren(parentLayoutNode: LayoutNode) = true
override fun childHitTest(
layoutNode: LayoutNode,
pointerPosition: Offset,
- hitTestResult: HitTestResult<PointerInputModifierNode>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean,
isInLayer: Boolean
) = layoutNode.hitTest(pointerPosition, hitTestResult, isTouchEvent, isInLayer)
@@ -1231,19 +1229,18 @@
* Hit testing specifics for semantics.
*/
val SemanticsSource =
- object : HitTestSource<SemanticsModifierNode> {
+ object : HitTestSource {
override fun entityType() = Nodes.Semantics
- override fun interceptOutOfBoundsChildEvents(node: SemanticsModifierNode) = false
+ override fun interceptOutOfBoundsChildEvents(node: Modifier.Node) = false
override fun shouldHitTestChildren(parentLayoutNode: LayoutNode) =
- parentLayoutNode.outerSemantics?.collapsedSemanticsConfiguration()
- ?.isClearingSemantics != true
+ parentLayoutNode.collapsedSemantics?.isClearingSemantics != true
override fun childHitTest(
layoutNode: LayoutNode,
pointerPosition: Offset,
- hitTestResult: HitTestResult<SemanticsModifierNode>,
+ hitTestResult: HitTestResult,
isTouchEvent: Boolean,
isInLayer: Boolean
) = layoutNode.hitTestSemantics(
@@ -1308,7 +1305,10 @@
}
}
-private fun <T> DelegatableNode.nextUncheckedUntil(type: NodeKind<T>, stopType: NodeKind<*>): T? {
+private fun DelegatableNode.nextUntil(
+ type: NodeKind<*>,
+ stopType: NodeKind<*>
+): Modifier.Node? {
val child = node.child ?: return null
if (child.aggregateChildKindSet and type.mask == 0) return null
var next: Modifier.Node? = child
@@ -1316,8 +1316,7 @@
val kindSet = next.kindSet
if (kindSet and stopType.mask != 0) return null
if (kindSet and type.mask != 0) {
- @Suppress("UNCHECKED_CAST")
- return next as? T
+ return next
}
next = next.child
}
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 6e9b108..b998c618 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
@@ -27,6 +27,9 @@
import androidx.compose.ui.focus.FocusProperties
import androidx.compose.ui.focus.FocusPropertiesModifierNode
import androidx.compose.ui.focus.FocusTargetModifierNode
+import androidx.compose.ui.focus.invalidateFocusEvent
+import androidx.compose.ui.focus.invalidateFocusProperties
+import androidx.compose.ui.focus.invalidateFocusTarget
import androidx.compose.ui.input.key.KeyInputModifierNode
import androidx.compose.ui.input.key.SoftKeyboardInterceptionModifierNode
import androidx.compose.ui.input.pointer.PointerInputModifier
@@ -50,6 +53,8 @@
internal inline infix fun Int.or(other: NodeKind<*>): Int = this or other.mask
+internal inline operator fun Int.contains(value: NodeKind<*>): Boolean = this and value.mask != 0
+
// For a given NodeCoordinator, the "LayoutAware" nodes that it is concerned with should include
// its own measureNode if the measureNode happens to implement LayoutAware. If the measureNode
// implements any other node interfaces, such as draw, those should be visited by the coordinator
@@ -144,6 +149,10 @@
@OptIn(ExperimentalComposeUiApi::class)
internal fun calculateNodeKindSetFrom(node: Modifier.Node): Int {
+ // This function does not take delegates into account, as a result, the kindSet will never
+ // change, so if it is non-zero, it means we've already calculated it and we can just bail
+ // early here.
+ if (node.kindSet != 0) return node.kindSet
var mask = Nodes.Any.mask
if (node is LayoutModifierNode) {
mask = mask or Nodes.Layout
@@ -200,34 +209,62 @@
private const val Inserted = 1
private const val Removed = 2
-internal fun autoInvalidateRemovedNode(node: Modifier.Node) = autoInvalidateNode(node, Removed)
-
-internal fun autoInvalidateInsertedNode(node: Modifier.Node) = autoInvalidateNode(node, Inserted)
-
-internal fun autoInvalidateUpdatedNode(node: Modifier.Node) = autoInvalidateNode(node, Updated)
-
-private fun autoInvalidateNode(node: Modifier.Node, phase: Int) {
+internal fun autoInvalidateRemovedNode(node: Modifier.Node) {
check(node.isAttached)
- if (node.isKind(Nodes.Layout) && node is LayoutModifierNode) {
- node.invalidateMeasurements()
+ autoInvalidateNodeIncludingDelegates(node, 0.inv(), Removed)
+}
+
+internal fun autoInvalidateInsertedNode(node: Modifier.Node) {
+ check(node.isAttached)
+ autoInvalidateNodeIncludingDelegates(node, 0.inv(), Inserted)
+}
+
+internal fun autoInvalidateUpdatedNode(node: Modifier.Node) {
+ check(node.isAttached)
+ autoInvalidateNodeIncludingDelegates(node, 0.inv(), Updated)
+}
+
+internal fun autoInvalidateNodeIncludingDelegates(
+ node: Modifier.Node,
+ remainingSet: Int,
+ phase: Int,
+) {
+ if (node is DelegatingNode) {
+ autoInvalidateNodeSelf(node, node.selfKindSet and remainingSet, phase)
+ val newRemaining = remainingSet and node.selfKindSet.inv()
+ node.forEachImmediateDelegate {
+ autoInvalidateNodeIncludingDelegates(it, newRemaining, phase)
+ }
+ } else {
+ autoInvalidateNodeSelf(node, node.kindSet and remainingSet, phase)
+ }
+}
+
+private fun autoInvalidateNodeSelf(node: Modifier.Node, selfKindSet: Int, phase: Int) {
+ // TODO(lmr): Implementing it this way means that delegates of an autoInvalidate=false node will
+ // still get invalidated. Not sure if that's what we want or not.
+ // Don't invalidate the node if it marks itself as autoInvalidate = false.
+ if (phase == Updated && !node.shouldAutoInvalidate) return
+ if (Nodes.Layout in selfKindSet && node is LayoutModifierNode) {
+ node.invalidateMeasurement()
if (phase == Removed) {
val coordinator = node.requireCoordinator(Nodes.Layout)
coordinator.onRelease()
}
}
- if (node.isKind(Nodes.GlobalPositionAware) && node is GlobalPositionAwareModifierNode) {
+ if (Nodes.GlobalPositionAware in selfKindSet && node is GlobalPositionAwareModifierNode) {
node.requireLayoutNode().invalidateMeasurements()
}
- if (node.isKind(Nodes.Draw) && node is DrawModifierNode) {
+ if (Nodes.Draw in selfKindSet && node is DrawModifierNode) {
node.invalidateDraw()
}
- if (node.isKind(Nodes.Semantics) && node is SemanticsModifierNode) {
+ if (Nodes.Semantics in selfKindSet && node is SemanticsModifierNode) {
node.invalidateSemantics()
}
- if (node.isKind(Nodes.ParentData) && node is ParentDataModifierNode) {
+ if (Nodes.ParentData in selfKindSet && node is ParentDataModifierNode) {
node.invalidateParentData()
}
- if (node.isKind(Nodes.FocusTarget) && node is FocusTargetModifierNode) {
+ if (Nodes.FocusTarget in selfKindSet && node is FocusTargetModifierNode) {
when (phase) {
// when we previously had focus target modifier on a node and then this modifier
// is removed we need to notify the focus tree about so the focus state is reset.
@@ -236,17 +273,17 @@
}
}
if (
- node.isKind(Nodes.FocusProperties) &&
+ Nodes.FocusProperties in selfKindSet &&
node is FocusPropertiesModifierNode &&
node.specifiesCanFocusProperty()
) {
when (phase) {
Removed -> node.scheduleInvalidationOfAssociatedFocusTargets()
- else -> node.requireOwner().focusOwner.scheduleInvalidation(node)
+ else -> node.invalidateFocusProperties()
}
}
- if (node.isKind(Nodes.FocusEvent) && node is FocusEventModifierNode && phase != Removed) {
- node.requireOwner().focusOwner.scheduleInvalidation(node)
+ if (Nodes.FocusEvent in selfKindSet && node is FocusEventModifierNode && phase != Removed) {
+ node.invalidateFocusEvent()
}
}
@@ -254,7 +291,7 @@
visitChildren(Nodes.FocusTarget) {
// Schedule invalidation for the focus target,
// which will cause it to recalculate focus properties.
- requireOwner().focusOwner.scheduleInvalidation(it)
+ it.invalidateFocusTarget()
}
}
@@ -280,4 +317,16 @@
set(value) { canFocusValue = value }
fun isCanFocusSet(): Boolean = canFocusValue != null
fun reset() { canFocusValue = null }
+}
+
+internal fun calculateNodeKindSetFromIncludingDelegates(node: Modifier.Node): Int {
+ return if (node is DelegatingNode) {
+ var mask = node.selfKindSet
+ node.forEachImmediateDelegate {
+ mask = mask or calculateNodeKindSetFromIncludingDelegates(it)
+ }
+ mask
+ } else {
+ calculateNodeKindSetFrom(node)
+ }
}
\ No newline at end of file
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 cd01e8b..8c80e15 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
@@ -40,20 +40,12 @@
fun SemanticsModifierNode.invalidateSemantics() = requireOwner().onSemanticsChange()
-fun SemanticsModifierNode.collapsedSemanticsConfiguration(): SemanticsConfiguration {
- val next = localChild(Nodes.Semantics)
- if (next == null || semanticsConfiguration.isClearingSemantics) {
- return semanticsConfiguration
- }
-
- val config = semanticsConfiguration.copy()
- config.collapsePeer(next.collapsedSemanticsConfiguration())
- return config
-}
-
internal val SemanticsModifierNode.useMinimumTouchTarget: Boolean
get() = semanticsConfiguration.getOrNull(SemanticsActions.OnClick) != null
+internal val SemanticsConfiguration.useMinimumTouchTarget: Boolean
+ get() = getOrNull(SemanticsActions.OnClick) != null
+
internal fun SemanticsModifierNode.touchBoundsInRoot(): Rect {
if (!node.isAttached) {
return Rect.Zero
@@ -64,3 +56,14 @@
return requireCoordinator(Nodes.Semantics).touchBoundsInRoot()
}
+
+internal fun Modifier.Node.touchBoundsInRoot(useMinimumTouchTarget: Boolean): Rect {
+ if (!node.isAttached) {
+ return Rect.Zero
+ }
+ if (!useMinimumTouchTarget) {
+ return requireCoordinator(Nodes.Semantics).boundsInRoot()
+ }
+
+ return requireCoordinator(Nodes.Semantics).touchBoundsInRoot()
+}
\ No newline at end of file
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 2ac9f46..41cdc4f 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
@@ -16,7 +16,6 @@
package androidx.compose.ui.semantics
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
@@ -31,28 +30,29 @@
import androidx.compose.ui.node.Nodes
import androidx.compose.ui.node.RootForTest
import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.collapsedSemanticsConfiguration
import androidx.compose.ui.node.requireCoordinator
import androidx.compose.ui.node.requireLayoutNode
import androidx.compose.ui.node.touchBoundsInRoot
+import androidx.compose.ui.node.useMinimumTouchTarget
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEach
-/**
- * A list of key/value pairs associated with a layout node or its subtree.
- *
- * Each SemanticsNode takes its id and initial key/value list from the
- * outermost modifier on one layout node. It also contains the "collapsed" configuration
- * of any other semantics modifiers on the same layout node, and if "mergeDescendants" is
- * specified and enabled, also the "merged" configuration of its subtree.
- */
-@OptIn(ExperimentalComposeUiApi::class)
-class SemanticsNode internal constructor(
+internal fun SemanticsNode(
+ layoutNode: LayoutNode,
+ mergingEnabled: Boolean
+) = SemanticsNode(
+ layoutNode.nodes.head(Nodes.Semantics)!!.node,
+ mergingEnabled,
+ layoutNode,
+ layoutNode.collapsedSemantics!!
+)
+
+internal fun SemanticsNode(
/*
* This is expected to be the outermost semantics modifier on a layout node.
*/
- internal val outerSemanticsNode: SemanticsModifierNode,
+ outerSemanticsNode: SemanticsModifierNode,
/**
* mergingEnabled specifies whether mergeDescendants config has any effect.
*
@@ -63,12 +63,31 @@
*
* mergingEnabled is typically true or false consistently on every node of a SemanticsNode tree.
*/
- val mergingEnabled: Boolean,
-
+ mergingEnabled: Boolean,
/**
* The [LayoutNode] that this is associated with.
*/
- internal val layoutNode: LayoutNode = outerSemanticsNode.requireLayoutNode()
+ layoutNode: LayoutNode = outerSemanticsNode.requireLayoutNode()
+) = SemanticsNode(
+ outerSemanticsNode.node,
+ mergingEnabled,
+ layoutNode,
+ layoutNode.collapsedSemantics ?: SemanticsConfiguration()
+)
+
+/**
+ * A list of key/value pairs associated with a layout node or its subtree.
+ *
+ * Each SemanticsNode takes its id and initial key/value list from the
+ * outermost modifier on one layout node. It also contains the "collapsed" configuration
+ * of any other semantics modifiers on the same layout node, and if "mergeDescendants" is
+ * specified and enabled, also the "merged" configuration of its subtree.
+ */
+class SemanticsNode internal constructor(
+ internal val outerSemanticsNode: Modifier.Node,
+ val mergingEnabled: Boolean,
+ internal val layoutNode: LayoutNode,
+ internal val unmergedConfig: SemanticsConfiguration,
) {
// We emit fake nodes for several cases. One is to prevent the content description clobbering
// issue. Another case is temporary workaround to retrieve default role ordering for Button
@@ -76,12 +95,9 @@
internal var isFake = false
private var fakeNodeParent: SemanticsNode? = null
- internal val unmergedConfig = outerSemanticsNode.collapsedSemanticsConfiguration()
-
internal val isUnmergedLeafNode get() =
!isFake && replacedChildren.isEmpty() && layoutNode.findClosestParentNode {
- it.outerSemantics
- ?.collapsedSemanticsConfiguration()
+ it.collapsedSemantics
?.isMergingSemanticsOfDescendants == true
} == null
@@ -117,7 +133,7 @@
} else {
outerSemanticsNode
}
- return entity.touchBoundsInRoot()
+ return entity.node.touchBoundsInRoot(unmergedConfig.useMinimumTouchTarget)
}
/**
@@ -217,11 +233,7 @@
if (this.isFake) return listOf()
val unmergedChildren: MutableList<SemanticsNode> = mutableListOf()
- val semanticsChildren = this.layoutNode.findOneLayerOfSemanticsWrappers()
-
- semanticsChildren.fastForEach { semanticsChild ->
- unmergedChildren.add(SemanticsNode(semanticsChild, mergingEnabled))
- }
+ this.layoutNode.fillOneLayerOfSemanticsWrappers(unmergedChildren)
if (includeFakeNodes) {
emitFakeNodes(unmergedChildren)
@@ -230,6 +242,20 @@
return unmergedChildren
}
+ private fun LayoutNode.fillOneLayerOfSemanticsWrappers(
+ list: MutableList<SemanticsNode>
+ ) {
+ // TODO(lmr): visitChildren would be great for this but we would lose the zSorted bit...
+ // i wonder if we can optimize this for the common case of no z-sortedness going on.
+ zSortedChildren.forEach { child ->
+ if (child.nodes.has(Nodes.Semantics)) {
+ list.add(SemanticsNode(child, mergingEnabled))
+ } else {
+ child.fillOneLayerOfSemanticsWrappers(list)
+ }
+ }
+ }
+
/**
* Contains the children in inverse hit test order (i.e. paint order).
*
@@ -297,21 +323,18 @@
var node: LayoutNode? = null
if (mergingEnabled) {
node = this.layoutNode.findClosestParentNode {
- it.outerSemantics
- ?.collapsedSemanticsConfiguration()
- ?.isMergingSemanticsOfDescendants == true
+ it.collapsedSemantics?.isMergingSemanticsOfDescendants == true
}
}
if (node == null) {
- node = this.layoutNode.findClosestParentNode { it.outerSemantics != null }
+ node = this.layoutNode.findClosestParentNode { it.nodes.has(Nodes.Semantics) }
}
- val outerSemantics = node?.outerSemantics
- if (outerSemantics == null)
+ if (node == null)
return null
- return SemanticsNode(outerSemantics, mergingEnabled)
+ return SemanticsNode(node, mergingEnabled)
}
private fun findOneLayerOfMergingSemanticsNodes(
@@ -337,8 +360,7 @@
*/
internal fun findCoordinatorToGetBounds(): NodeCoordinator? {
if (isFake) return parent?.findCoordinatorToGetBounds()
- val semanticsModifierNode = layoutNode.outerMergingSemantics
- .takeIf { unmergedConfig.isMergingSemanticsOfDescendants } ?: outerSemanticsNode
+ val semanticsModifierNode = layoutNode.outerMergingSemantics ?: outerSemanticsNode
return semanticsModifierNode.requireCoordinator(Nodes.Semantics)
}
@@ -373,13 +395,14 @@
role: Role?,
properties: SemanticsPropertyReceiver.() -> Unit
): SemanticsNode {
+ val configuration = SemanticsConfiguration().also {
+ it.isMergingSemanticsOfDescendants = false
+ it.isClearingSemantics = false
+ it.properties()
+ }
val fakeNode = SemanticsNode(
outerSemanticsNode = object : SemanticsModifierNode, Modifier.Node() {
- override val semanticsConfiguration = SemanticsConfiguration().also {
- it.isMergingSemanticsOfDescendants = false
- it.isClearingSemantics = false
- it.properties()
- }
+ override val semanticsConfiguration = configuration
},
mergingEnabled = false,
layoutNode = LayoutNode(
@@ -387,43 +410,44 @@
semanticsId =
if (role != null) roleFakeNodeId() else contentDescriptionFakeNodeId()
),
+ unmergedConfig = configuration
)
fakeNode.isFake = true
fakeNode.fakeNodeParent = this
return fakeNode
}
+
+ internal fun copyWithMergingEnabled(): SemanticsNode {
+ return SemanticsNode(
+ outerSemanticsNode,
+ true,
+ layoutNode,
+ unmergedConfig
+ )
+ }
}
-/**
- * Returns the outermost semantics node on a LayoutNode.
- */
-@OptIn(ExperimentalComposeUiApi::class)
-internal val LayoutNode.outerSemantics: SemanticsModifierNode?
- get() = nodes.head(Nodes.Semantics)
+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
+ }
-@OptIn(ExperimentalComposeUiApi::class)
internal val LayoutNode.outerMergingSemantics: SemanticsModifierNode?
get() = nodes.firstFromHead(Nodes.Semantics) {
it.semanticsConfiguration.isMergingSemanticsOfDescendants
}
-@OptIn(ExperimentalComposeUiApi::class)
-private fun LayoutNode.findOneLayerOfSemanticsWrappers(
- list: MutableList<SemanticsModifierNode> = mutableListOf()
-): List<SemanticsModifierNode> {
- // TODO(lmr): visitChildren would be great for this but we would lose the zSorted bit...
- // i wonder if we can optimize this for the common case of no z-sortedness going on.
- zSortedChildren.forEach { child ->
- val outerSemantics = child.outerSemantics
- if (outerSemantics != null) {
- list.add(outerSemantics)
- } else {
- child.findOneLayerOfSemanticsWrappers(list)
- }
- }
- return list
-}
-
/**
* Executes [selector] on every parent of this [LayoutNode] and returns the closest
* [LayoutNode] to return `true` from [selector] or null if [selector] returns false
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 7e32c0b..a5d43a5 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
@@ -32,12 +32,12 @@
*/
val rootSemanticsNode: SemanticsNode
get() {
- return SemanticsNode(rootNode.outerSemantics!!, mergingEnabled = true)
+ return SemanticsNode(rootNode, mergingEnabled = true)
}
val unmergedRootSemanticsNode: SemanticsNode
get() {
- return SemanticsNode(rootNode.outerSemantics!!, mergingEnabled = false)
+ return SemanticsNode(rootNode, mergingEnabled = false)
}
}
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
new file mode 100644
index 0000000..15c3ed5
--- /dev/null
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/DelegatingNodeTest.kt
@@ -0,0 +1,888 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.node
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+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.unit.Constraints
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+import com.google.common.truth.Truth.assertThat
+
+@RunWith(JUnit4::class)
+class DelegatingNodeTest {
+
+ @Test
+ fun testKindSetIncludesDelegates() {
+ assertThat(DelegatedWrapper { DrawMod() }.kindSet)
+ .isEqualTo(Nodes.Any or Nodes.Draw)
+ }
+
+ @Test
+ fun testKindSetUpdatesAfter() {
+ val a = DrawMod("a")
+ val b = DelegatedWrapper { LayoutMod("b") }
+ val c = object : DelegatingNode() {}
+ val chain = layout(a, b, c)
+ assert(chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Layout))
+ assert(!chain.has(Nodes.Semantics))
+
+ assert(a.kindSet == Nodes.Any or Nodes.Draw)
+ assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Layout)
+
+ assert(b.kindSet == Nodes.Any or Nodes.Layout)
+ assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Layout)
+
+ assert(c.kindSet == Nodes.Any.mask)
+ assert(c.aggregateChildKindSet == Nodes.Any.mask)
+
+ c.delegateUnprotected(SemanticsMod("c"))
+ assert(chain.has(Nodes.Semantics))
+
+ assert(a.kindSet == Nodes.Any or Nodes.Draw)
+ assert(a.aggregateChildKindSet ==
+ Nodes.Any or Nodes.Draw or Nodes.Layout or Nodes.Semantics)
+
+ assert(b.kindSet == Nodes.Any or Nodes.Layout)
+ assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Layout or Nodes.Semantics)
+
+ assert(c.kindSet == Nodes.Any or Nodes.Semantics)
+ assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Semantics)
+ }
+
+ @Test
+ fun testAsKindReturnsDelegate() {
+ val node = DelegatedWrapper { DrawMod() }
+ assert(node.isKind(Nodes.Draw))
+ assert(node.asKind(Nodes.Draw) is DrawMod)
+ assert(node.asKind(Nodes.Draw) === node.wrapped)
+ }
+
+ @Test
+ fun testAsKindReturnsNestedDelegate() {
+ val node = DelegatedWrapper { DelegatedWrapper { DrawMod() } }
+ assert(node.isKind(Nodes.Draw))
+ assert(node.asKind(Nodes.Draw) is DrawMod)
+ assert(node.asKind(Nodes.Draw) === node.wrapped.wrapped)
+ }
+
+ @Test
+ fun testAsKindReturnsSelf() {
+ val node = object : DrawModifierNode, DelegatingNode() {
+ val wrapped = delegate(DrawMod())
+ override fun ContentDrawScope.draw() {
+ with(wrapped) { draw() }
+ }
+ }
+ assert(node.isKind(Nodes.Draw))
+ assert(node.asKind(Nodes.Draw) is DrawModifierNode)
+ assert(node.asKind(Nodes.Draw) === node)
+ }
+
+ @Test
+ fun testAsKindMultipleDelegatesReturnsLast() {
+ val node = object : DelegatingNode() {
+ val first = delegate(DrawMod())
+ val second = delegate(DrawMod())
+ }
+ assert(node.isKind(Nodes.Draw))
+ assert(node.asKind(Nodes.Draw) is DrawModifierNode)
+ assert(node.asKind(Nodes.Draw) === node.second)
+ }
+
+ @Test
+ fun testDispatchForMultipleDelegatesSameKind() {
+ val node = object : DelegatingNode() {
+ val first = delegate(DelegatedWrapper { DrawMod("first") })
+ val second = delegate(DrawMod("second"))
+ }
+ assertDispatchOrder(node, Nodes.Draw, node.first.wrapped, node.second)
+ }
+
+ @Test
+ fun testDispatchForSelfOnlyDispatchesToSelf() {
+ val node = object : DrawModifierNode, DelegatingNode() {
+ val wrapped = delegate(DrawMod())
+ override fun ContentDrawScope.draw() {
+ with(wrapped) { draw() }
+ }
+ }
+ assertDispatchOrder(node, Nodes.Draw, node)
+ }
+
+ @Test
+ fun testDispatchNestedSelfStops() {
+ val node = object : DelegatingNode() {
+ val first = delegate(DrawMod())
+ val second = delegate(DrawMod())
+ val third = delegate(object : DrawModifierNode, DelegatingNode() {
+ val first = delegate(DrawMod())
+ val second = delegate(DrawMod())
+ override fun ContentDrawScope.draw() {
+ with(first) { draw() }
+ }
+ })
+ }
+ assertDispatchOrder(node, Nodes.Draw,
+ node.first,
+ node.second,
+ node.third
+ )
+ }
+
+ @Test
+ fun testHeadToTailNoDelegates() {
+ val a = DrawMod("a")
+ val b = DrawMod("b")
+ val chain = layout(a, b)
+ val recorder = Recorder()
+ chain.headToTail(Nodes.Draw, recorder)
+ assertThat(recorder.recorded).isEqualTo(listOf(a, b))
+ }
+
+ @Test
+ fun testHeadToTailWithDelegate() {
+ val a = DelegatedWrapper { DrawMod() }
+ val b = DrawMod()
+ val chain = layout(a, b)
+ val recorder = Recorder()
+ chain.headToTail(Nodes.Draw, recorder)
+ assertThat(recorder.recorded).isEqualTo(listOf(a.wrapped, b))
+ }
+
+ @Test
+ fun testVisitSubtreeWithDelegates() {
+ val x = DrawMod("x")
+ val a = DelegatedWrapper { DrawMod("a") }
+ val b = DrawMod("b")
+ val c = DelegatedWrapper { DrawMod("c") }
+ val d = DrawMod("d")
+ layout(x, a, b) {
+ layout(c)
+ layout(d)
+ }
+ val recorder = Recorder()
+ x.visitSubtree(Nodes.Draw, recorder)
+ assertThat(recorder.recorded)
+ .isEqualTo(listOf(
+ a.wrapped,
+ b,
+ d,
+ c.wrapped,
+ ))
+ }
+
+ @Test
+ fun testVisitAncestorsWithDelegates() {
+ val x = DrawMod("x")
+ val a = DelegatedWrapper { DrawMod("a") }
+ val b = DrawMod("b")
+ val c = DelegatedWrapper { DrawMod("c") }
+ val d = DrawMod("d")
+ layout(a) {
+ layout(b) {
+ layout(c) {
+ layout(d, x)
+ }
+ }
+ }
+ val recorder = Recorder()
+ x.visitAncestors(Nodes.Draw, block = recorder)
+ assertThat(recorder.recorded)
+ .isEqualTo(listOf(
+ d,
+ c.wrapped,
+ b,
+ a.wrapped,
+ ))
+ }
+
+ @Test
+ fun testUndelegate() {
+ val node = object : DelegatingNode() {}
+ val chain = layout(node)
+
+ assert(!node.isKind(Nodes.Draw))
+ assert(!chain.has(Nodes.Draw))
+
+ val draw = node.delegateUnprotected(DrawMod())
+
+ assert(node.isKind(Nodes.Draw))
+ assert(chain.has(Nodes.Draw))
+ assert(node.asKind(Nodes.Draw) === draw)
+
+ node.undelegateUnprotected(draw)
+
+ assert(!draw.isAttached)
+ assert(node.isAttached)
+
+ assert(!node.isKind(Nodes.Draw))
+ assert(!chain.has(Nodes.Draw))
+ }
+
+ @Test
+ fun testUndelegateWithMultipleDelegates() {
+ val node = object : DelegatingNode() {}
+ val chain = layout(node)
+
+ assert(!node.isKind(Nodes.Draw))
+ assert(!chain.has(Nodes.Draw))
+
+ val draw = node.delegateUnprotected(DrawMod())
+ val layout = node.delegateUnprotected(LayoutMod())
+ val semantics = node.delegateUnprotected(SemanticsMod())
+ val draw2 = node.delegateUnprotected(DrawMod())
+
+ assert(node.isKind(Nodes.Semantics))
+ assert(chain.has(Nodes.Semantics))
+ assert(node.asKind(Nodes.Semantics) === semantics)
+
+ assert(node.isKind(Nodes.Draw))
+ assert(chain.has(Nodes.Draw))
+ assert(node.asKind(Nodes.Draw) === draw2)
+
+ assert(node.isKind(Nodes.Layout))
+ assert(chain.has(Nodes.Layout))
+ assert(node.asKind(Nodes.Layout) === layout)
+
+ node.undelegateUnprotected(semantics)
+
+ assert(!node.isKind(Nodes.Semantics))
+ assert(!chain.has(Nodes.Semantics))
+
+ assert(node.isKind(Nodes.Draw))
+ assert(chain.has(Nodes.Draw))
+ assert(node.asKind(Nodes.Draw) === draw2)
+
+ assert(node.isKind(Nodes.Layout))
+ assert(chain.has(Nodes.Layout))
+ assert(node.asKind(Nodes.Layout) === layout)
+
+ node.undelegateUnprotected(draw2)
+
+ assert(node.isKind(Nodes.Draw))
+ assert(chain.has(Nodes.Draw))
+ assert(node.asKind(Nodes.Draw) === draw)
+
+ assert(node.isKind(Nodes.Layout))
+ assert(chain.has(Nodes.Layout))
+ assert(node.asKind(Nodes.Layout) === layout)
+ }
+
+ @Test
+ fun testDelegateUndelegateInChain() {
+ val a = object : DelegatingNode() {}
+ val b = object : DelegatingNode() {}
+ val c = object : DelegatingNode() {}
+ val chain = layout(a, b, c)
+ assert(!chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+
+ val draw = c.delegateUnprotected(DrawMod())
+ assert(chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(a.kindSet == Nodes.Any.mask)
+ assert(b.kindSet == Nodes.Any.mask)
+ assert(c.kindSet == Nodes.Any or Nodes.Draw)
+ assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+ assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+ assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+ val sem = b.delegateUnprotected(SemanticsMod())
+ assert(chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(a.kindSet == Nodes.Any.mask)
+ assert(b.kindSet == Nodes.Any or Nodes.Semantics)
+ assert(c.kindSet == Nodes.Any or Nodes.Draw)
+ assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+ assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+ assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+ val lm = a.delegateUnprotected(LayoutMod())
+ assert(chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Semantics))
+ assert(chain.has(Nodes.Layout))
+ assert(a.kindSet == Nodes.Any or Nodes.Layout)
+ assert(b.kindSet == Nodes.Any or Nodes.Semantics)
+ assert(c.kindSet == Nodes.Any or Nodes.Draw)
+ assert(a.aggregateChildKindSet ==
+ Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+ assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+ assert(c.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+ c.undelegateUnprotected(draw)
+ assert(!chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Semantics))
+ assert(chain.has(Nodes.Layout))
+ assert(a.kindSet == Nodes.Any or Nodes.Layout)
+ assert(b.kindSet == Nodes.Any or Nodes.Semantics)
+ assert(c.kindSet == Nodes.Any.mask)
+ assert(a.aggregateChildKindSet == Nodes.Any or Nodes.Semantics or Nodes.Layout)
+ assert(b.aggregateChildKindSet == Nodes.Any or Nodes.Semantics)
+ assert(c.aggregateChildKindSet == Nodes.Any.mask)
+
+ b.undelegateUnprotected(sem)
+ assert(!chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(chain.has(Nodes.Layout))
+ assert(a.kindSet == Nodes.Any or Nodes.Layout)
+ assert(b.kindSet == Nodes.Any.mask)
+ assert(c.kindSet == Nodes.Any.mask)
+ assert(a.aggregateChildKindSet == Nodes.Any.mask or Nodes.Layout)
+ assert(b.aggregateChildKindSet == Nodes.Any.mask)
+ assert(c.aggregateChildKindSet == Nodes.Any.mask)
+
+ a.undelegateUnprotected(lm)
+ assert(!chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(a.kindSet == Nodes.Any.mask)
+ assert(b.kindSet == Nodes.Any.mask)
+ assert(c.kindSet == Nodes.Any.mask)
+ assert(a.aggregateChildKindSet == Nodes.Any.mask)
+ assert(b.aggregateChildKindSet == Nodes.Any.mask)
+ assert(c.aggregateChildKindSet == Nodes.Any.mask)
+ }
+
+ @Test
+ fun testDelegateUndelegateInNode() {
+ val node = object : DelegatingNode() {}
+ val chain = layout(node)
+ assert(!chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+
+ val draw = node.delegateUnprotected(DrawMod())
+ assert(chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Draw)
+ assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+ val sem = node.delegateUnprotected(SemanticsMod())
+ assert(chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+ assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+
+ val lm = node.delegateUnprotected(LayoutMod())
+ assert(chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Semantics))
+ assert(chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+ assert(node.aggregateChildKindSet ==
+ Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+
+ node.undelegateUnprotected(draw)
+ assert(!chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Semantics))
+ assert(chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Semantics or Nodes.Layout)
+ assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Semantics or Nodes.Layout)
+
+ node.undelegateUnprotected(sem)
+ assert(!chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Layout)
+ assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Layout)
+
+ node.undelegateUnprotected(lm)
+ assert(!chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any.mask)
+ assert(node.aggregateChildKindSet == Nodes.Any.mask)
+ }
+
+ @Test
+ fun testDelegateUndelegateNested() {
+ val node = object : DelegatingNode() {}
+ val chain = layout(node)
+ assert(!chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+
+ val draw = node.delegateUnprotected(DelegatedWrapper { DrawMod() })
+ assert(chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Draw)
+ assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+ val sem = draw.delegateUnprotected(DelegatedWrapper { SemanticsMod() })
+ assert(chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+ assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+
+ val lm = sem.delegateUnprotected(LayoutMod())
+ assert(chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Semantics))
+ assert(chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+ assert(node.aggregateChildKindSet ==
+ Nodes.Any or Nodes.Draw or Nodes.Semantics or Nodes.Layout)
+
+ sem.undelegateUnprotected(lm)
+ assert(chain.has(Nodes.Draw))
+ assert(chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+ assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw or Nodes.Semantics)
+
+ draw.undelegateUnprotected(sem)
+ assert(chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any or Nodes.Draw)
+ assert(node.aggregateChildKindSet == Nodes.Any or Nodes.Draw)
+
+ node.undelegateUnprotected(draw)
+ assert(!chain.has(Nodes.Draw))
+ assert(!chain.has(Nodes.Semantics))
+ assert(!chain.has(Nodes.Layout))
+ assert(node.kindSet == Nodes.Any.mask)
+ assert(node.aggregateChildKindSet == Nodes.Any.mask)
+ }
+
+ @Test
+ fun testUndelegateForNestedDelegate() {
+ val a = object : DelegatingNode() {}
+ val chain = layout(a)
+
+ val b = a.delegateUnprotected(object : DelegatingNode() {})
+ val c = a.delegateUnprotected(object : DelegatingNode() {})
+
+ assert(!chain.has(Nodes.Draw))
+ assert(!a.isKind(Nodes.Draw))
+
+ val draw = c.delegateUnprotected(DrawMod())
+
+ assert(chain.has(Nodes.Draw))
+ assert(a.isKind(Nodes.Draw))
+ assert(!b.isKind(Nodes.Draw))
+ assert(c.isKind(Nodes.Draw))
+
+ c.undelegateUnprotected(draw)
+
+ assert(!chain.has(Nodes.Draw))
+ assert(!a.isKind(Nodes.Draw))
+ assert(!b.isKind(Nodes.Draw))
+ assert(!c.isKind(Nodes.Draw))
+ }
+
+ @Test
+ fun testInvalidateInsertedNode() {
+ val node = object : DelegatingNode() {
+ val draw = delegate(DrawMod())
+ val layout = delegate(LayoutMod())
+ val semantics = delegate(SemanticsMod())
+ }
+ val chain = layout(node)
+ chain.clearInvalidations()
+
+ autoInvalidateNodeIncludingDelegates(node, 0.inv(), 1)
+
+ assert(chain.drawInvalidated())
+ assert(chain.layoutInvalidated())
+ assert(chain.semanticsInvalidated())
+ }
+
+ @Test
+ fun testNestedNodeInvalidation() {
+ val node = object : DelegatingNode() {
+ val wrapped = delegate(
+ DelegatedWrapper {
+ DelegatedWrapper { DrawMod() }
+ }
+ )
+ }
+ val chain = layout(node)
+ chain.clearInvalidations()
+
+ autoInvalidateNodeIncludingDelegates(node, 0.inv(), 1)
+
+ assert(chain.drawInvalidated())
+ assert(!chain.layoutInvalidated())
+ assert(!chain.semanticsInvalidated())
+ }
+
+ @Test
+ fun testDelegateUndelegateCausesInvalidationsForDelegateKindsOnly() {
+ val node = object : DelegatingNode() {
+ val semantics = delegate(SemanticsMod())
+ }
+ val chain = layout(node)
+ chain.clearInvalidations()
+
+ val draw = node.delegateUnprotected(DrawMod())
+ assert(chain.drawInvalidated())
+ assert(!chain.semanticsInvalidated())
+
+ chain.clearInvalidations()
+ node.undelegateUnprotected(draw)
+
+ assert(chain.drawInvalidated())
+ assert(!chain.semanticsInvalidated())
+ }
+
+ @Test
+ fun testDelegatingToLayoutNodeUpdatesCoordinators() {
+ val a = DrawMod()
+ val b = object : DelegatingNode() {}
+ val c = LayoutMod()
+ layout(a, b, c)
+
+ val aCoord = a.requireCoordinator(Nodes.Any)
+ val bCoord = b.requireCoordinator(Nodes.Any)
+ val cCoord = c.requireCoordinator(Nodes.Any)
+
+ assert(cCoord === bCoord)
+ assert(cCoord === aCoord)
+
+ val lm = b.delegateUnprotected(LayoutMod())
+
+ assert(cCoord === c.requireCoordinator(Nodes.Any))
+ assert(cCoord !== b.requireCoordinator(Nodes.Any))
+ assert(cCoord !== a.requireCoordinator(Nodes.Any))
+
+ assert(a.requireCoordinator(Nodes.Any) === b.requireCoordinator(Nodes.Any))
+
+ b.undelegateUnprotected(lm)
+
+ assert(c.requireCoordinator(Nodes.Any) === b.requireCoordinator(Nodes.Any))
+ assert(c.requireCoordinator(Nodes.Any) === a.requireCoordinator(Nodes.Any))
+ }
+
+ @Test
+ fun testDelegateAttachDetach() {
+ val a = object : DelegatingNode() {}
+ val b = object : DelegatingNode() {}
+ val c = DrawMod()
+ a.delegateUnprotected(b)
+ b.delegateUnprotected(c)
+
+ // not attached yet, but the nodes should all point to a
+ assert(!a.isAttached)
+ assert(!b.isAttached)
+ assert(!c.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === a)
+ assert(c.node === a)
+
+ val chain = layout(a)
+
+ // attached now, nodes should still point to a
+ assert(a.isAttached)
+ assert(b.isAttached)
+ assert(c.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === a)
+ assert(c.node === a)
+
+ // detached now, nodes should still point to a
+ chain.detach()
+
+ assert(!a.isAttached)
+ assert(!b.isAttached)
+ assert(!c.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === a)
+ assert(c.node === a)
+
+ chain.attach()
+
+ // attached now, nodes should still point to a
+ assert(a.isAttached)
+ assert(b.isAttached)
+ assert(c.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === a)
+ assert(c.node === a)
+
+ b.undelegateUnprotected(c)
+ a.undelegateUnprotected(b)
+
+ // delegates are detached. nodes should point to themselves
+ assert(a.isAttached)
+ assert(!b.isAttached)
+ assert(!c.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === b)
+ assert(c.node === c)
+ }
+
+ @Test
+ fun testDelegateInAttachUndelegateInDetach() {
+ val b = DrawMod()
+ val a = object : DelegatingNode() {
+ override fun onAttach() {
+ delegate(b)
+ }
+
+ override fun onDetach() {
+ undelegate(b)
+ }
+ }
+
+ // not attached yet or delegated yet
+ assert(!a.isAttached)
+ assert(!b.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === b)
+
+ val chain = layout(a)
+
+ // attached now, nodes should now point to a
+ assert(a.isAttached)
+ assert(b.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === a)
+
+ chain.detach()
+
+ // detached AND undelegated now
+ assert(!a.isAttached)
+ assert(!b.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === b)
+
+ chain.attach()
+
+ // attached and delegated now
+ assert(a.isAttached)
+ assert(b.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === a)
+ }
+
+ @Test
+ fun testDelegateInAttach() {
+ val b = DrawMod()
+ val a = object : DelegatingNode() {
+ override fun onAttach() {
+ delegate(b)
+ }
+ }
+
+ // not attached yet or delegated yet
+ assert(!a.isAttached)
+ assert(!b.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === b)
+
+ val chain = layout(a)
+
+ // attached now, nodes should now point to a
+ assert(a.isAttached)
+ assert(b.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === a)
+
+ chain.detach()
+
+ // detached now, still delegated
+ assert(!a.isAttached)
+ assert(!b.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === a)
+
+ chain.attach()
+
+ // attached, still delegated
+ assert(a.isAttached)
+ assert(b.isAttached)
+
+ assert(a.node === a)
+ assert(b.node === a)
+ }
+}
+
+private fun NodeChain.clearInvalidations() {
+ val owner = layoutNode.owner
+ check(owner is MockOwner)
+ owner.onRequestMeasureParams.clear()
+ owner.invalidatedLayers.clear()
+ owner.semanticsChanged = false
+}
+
+private fun NodeChain.layoutInvalidated(): Boolean {
+ val owner = layoutNode.owner
+ check(owner is MockOwner)
+ return owner.onRequestMeasureParams.isNotEmpty()
+}
+
+private fun NodeChain.drawInvalidated(): Boolean {
+ val owner = layoutNode.owner
+ check(owner is MockOwner)
+ return owner.invalidatedLayers.isNotEmpty()
+}
+
+private fun NodeChain.semanticsInvalidated(): Boolean {
+ val owner = layoutNode.owner
+ check(owner is MockOwner)
+ return owner.semanticsChanged
+}
+
+internal fun layout(
+ vararg modifiers: Modifier.Node,
+ block: LayoutScope.() -> Unit = {}
+): NodeChain {
+ val owner = MockOwner()
+ val root = LayoutNode()
+ val ln = unattachedLayout(*modifiers)
+ LayoutScopeImpl(ln).block()
+ root.insertAt(0, ln)
+ root.attach(owner)
+ root.innerCoordinator.updateLayerBlock({})
+ return ln.nodes
+}
+
+private fun unattachedLayout(vararg modifiers: Modifier.Node): LayoutNode {
+ val ln = LayoutNode()
+ var m: Modifier = Modifier
+ for (node in modifiers) {
+ m = m.then(NodeElement(node))
+ }
+ ln.nodes.updateFrom(m)
+ return ln
+}
+
+internal data class NodeElement(val node: Modifier.Node) : ModifierNodeElement<Modifier.Node>() {
+ override fun create(): Modifier.Node = node
+ override fun update(node: Modifier.Node): Modifier.Node = node
+}
+
+class Recorder : (Any) -> Unit {
+ val recorded = mutableListOf<Any>()
+ override fun invoke(p1: Any) {
+ recorded.add(p1)
+ }
+}
+internal class LayoutScopeImpl(val layout: LayoutNode) : LayoutScope {
+ override fun layout(vararg modifiers: Modifier.Node, block: LayoutScope.() -> Unit) {
+ val ln = unattachedLayout(*modifiers)
+ LayoutScopeImpl(ln).block()
+ layout.insertAt(layout.children.size, ln)
+ }
+}
+interface LayoutScope {
+ fun layout(vararg modifiers: Modifier.Node) = layout(*modifiers) {}
+ fun layout(vararg modifiers: Modifier.Node, block: LayoutScope.() -> Unit)
+}
+
+internal inline fun <reified T> assertDispatchOrder(
+ node: Modifier.Node,
+ kind: NodeKind<T>,
+ vararg expected: T
+) {
+ val dispatches = mutableListOf<T>()
+ node.dispatchForKind(kind) {
+ dispatches.add(it)
+ }
+ assertThat(dispatches.toTypedArray()).isEqualTo(expected)
+}
+
+class DrawMod(val id: String = "") : DrawModifierNode, Modifier.Node() {
+ override fun ContentDrawScope.draw() {}
+ override fun toString(): String {
+ return "DrawMod($id)"
+ }
+}
+
+class SemanticsMod(val id: String = "") : SemanticsModifierNode, Modifier.Node() {
+ override val semanticsConfiguration: SemanticsConfiguration
+ get() = SemanticsConfiguration()
+ override fun toString(): String {
+ return "SemanticsMod($id)"
+ }
+}
+
+class LayoutMod(val id: String = "") : LayoutModifierNode, Modifier.Node() {
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ val placeable = measurable.measure(constraints)
+ return layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
+ }
+ override fun toString(): String {
+ return "LayoutMod($id)"
+ }
+}
+
+class DelegatedWrapper<T : Modifier.Node>(fn: () -> T) : DelegatingNode() {
+ val wrapped = delegate(fn())
+ override fun toString(): String = "Wrapped<$wrapped>"
+}
+
+internal inline fun <reified T> Modifier.Node.asKind(kind: NodeKind<T>): T? {
+ if (!isKind(kind)) return null
+ if (this is T) return this
+ if (this is DelegatingNode) {
+ forEachDelegateBreadthFirst {
+ if (it is T) return it
+ }
+ }
+ return null
+}
+
+internal inline fun DelegatingNode.forEachDelegateBreadthFirst(block: (Modifier.Node) -> Unit) {
+ var node: Modifier.Node? = delegate
+ var queue: ArrayDeque<Modifier.Node>? = null
+ while (node != null) {
+ block(node)
+ if (node is DelegatingNode) {
+ queue = queue.enqueue(node.delegate)
+ }
+ node = node.child ?: queue?.removeFirst()
+ }
+}
+
+private fun ArrayDeque<Modifier.Node>?.enqueue(node: Modifier.Node?): ArrayDeque<Modifier.Node>? {
+ if (node == null) return this
+ val queue = this ?: ArrayDeque(8)
+ queue.addLast(node)
+ return queue
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/HitTestResultTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/HitTestResultTest.kt
index 711c503..665bb01 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/HitTestResultTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/HitTestResultTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.ui.node
+import androidx.compose.ui.Modifier
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -26,7 +27,7 @@
class HitTestResultTest {
@Test
fun testHit() {
- val hitTestResult = HitTestResult<String>()
+ val hitTestResult = HitTestResult()
hitTestResult.hit("Hello", true) {
hitTestResult.hit("World", true) {
assertThat(hitTestResult.hasHit()).isFalse()
@@ -38,18 +39,18 @@
assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(0f, false)).isFalse()
assertThat(hitTestResult).hasSize(2)
- assertThat(hitTestResult[0]).isEqualTo("Hello")
- assertThat(hitTestResult[1]).isEqualTo("World")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+ assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
hitTestResult.hit("Baz", true) {}
assertThat(hitTestResult.hasHit()).isTrue()
assertThat(hitTestResult).hasSize(1)
- assertThat(hitTestResult[0]).isEqualTo("Baz")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Baz"))
}
@Test
fun testHitClipped() {
- val hitTestResult = HitTestResult<String>()
+ val hitTestResult = HitTestResult()
hitTestResult.hit("Hello", false) {
hitTestResult.hit("World", false) {
assertThat(hitTestResult.hasHit()).isFalse()
@@ -61,18 +62,18 @@
assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(0f, false)).isFalse()
assertThat(hitTestResult).hasSize(2)
- assertThat(hitTestResult[0]).isEqualTo("Hello")
- assertThat(hitTestResult[1]).isEqualTo("World")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+ assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
hitTestResult.hit("Baz", false) {}
assertThat(hitTestResult.hasHit()).isFalse()
assertThat(hitTestResult).hasSize(1)
- assertThat(hitTestResult[0]).isEqualTo("Baz")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Baz"))
}
@Test
fun testHitInMinimumTouchTarget() {
- val hitTestResult = HitTestResult<String>()
+ val hitTestResult = HitTestResult()
hitTestResult.hitInMinimumTouchTarget("Hello", 1f, true) {
hitTestResult.hitInMinimumTouchTarget("World", 2f, false) { }
assertThat(hitTestResult.hasHit()).isFalse()
@@ -85,18 +86,18 @@
assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(1.5f, true)).isFalse()
assertThat(hitTestResult).hasSize(2)
- assertThat(hitTestResult[0]).isEqualTo("Hello")
- assertThat(hitTestResult[1]).isEqualTo("World")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+ assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
hitTestResult.hitInMinimumTouchTarget("Baz", 0.5f, false) { }
assertThat(hitTestResult.hasHit()).isFalse()
assertThat(hitTestResult).hasSize(1)
- assertThat(hitTestResult[0]).isEqualTo("Baz")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Baz"))
}
@Test
fun testHasHit() {
- val hitTestResult = HitTestResult<String>()
+ val hitTestResult = HitTestResult()
hitTestResult.hitInMinimumTouchTarget("Hello", 1f, true) {
hitTestResult.hit("World", true) {
assertThat(hitTestResult.hasHit()).isFalse()
@@ -108,7 +109,7 @@
@Test
fun testEasySpeculativeHit() {
- val hitTestResult = HitTestResult<String>()
+ val hitTestResult = HitTestResult()
hitTestResult.speculativeHit("Hello", 1f, true) {
}
assertThat(hitTestResult).hasSize(0)
@@ -122,20 +123,20 @@
assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(1.5f, true)).isFalse()
assertThat(hitTestResult).hasSize(2)
- assertThat(hitTestResult[0]).isEqualTo("Hello")
- assertThat(hitTestResult[1]).isEqualTo("World")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+ assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
}
@Test
fun testSpeculativeHitWithMove() {
- val hitTestResult = HitTestResult<String>()
+ val hitTestResult = HitTestResult()
hitTestResult.hitInMinimumTouchTarget("Foo", 1.5f, true) { }
hitTestResult.speculativeHit("Hello", 1f, true) {
}
assertThat(hitTestResult).hasSize(1)
- assertThat(hitTestResult[0]).isEqualTo("Foo")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Foo"))
hitTestResult.speculativeHit("Hello", 1f, true) {
hitTestResult.hitInMinimumTouchTarget("World", 2f, true) {}
@@ -146,13 +147,13 @@
assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(1.25f, true)).isFalse()
assertThat(hitTestResult).hasSize(2)
- assertThat(hitTestResult[0]).isEqualTo("Hello")
- assertThat(hitTestResult[1]).isEqualTo("World")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+ assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
}
@Test
fun testSpeculateHitWithDeepHit() {
- val hitTestResult = HitTestResult<String>()
+ val hitTestResult = HitTestResult()
hitTestResult.hitInMinimumTouchTarget("Foo", 1.5f, true) { }
hitTestResult.speculativeHit("Hello", 2f, true) {
@@ -164,8 +165,8 @@
assertThat(hitTestResult.isHitInMinimumTouchTargetBetter(1.25f, true)).isFalse()
assertThat(hitTestResult).hasSize(2)
- assertThat(hitTestResult[0]).isEqualTo("Hello")
- assertThat(hitTestResult[1]).isEqualTo("World")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+ assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
hitTestResult.speculativeHit("Goodbye", 2f, true) {
hitTestResult.hitInMinimumTouchTarget("Cruel", 1f, true) {
@@ -173,7 +174,7 @@
}
}
- assertThat(hitTestResult.toList()).isEqualTo(listOf("Goodbye", "Cruel", "World!"))
+ assertThat(hitTestResult.toList()).isEqualTo(nodeListOf("Goodbye", "Cruel", "World!"))
}
@Test
@@ -198,18 +199,18 @@
@Test
fun testContainsAll() {
val hitTestResult = fillHitTestResult()
- assertThat(hitTestResult.containsAll(listOf("Hello", "great", "this"))).isTrue()
- assertThat(hitTestResult.containsAll(listOf("Hello", "great", "foo", "this"))).isFalse()
+ assertThat(hitTestResult.containsAll(nodeListOf("Hello", "great", "this"))).isTrue()
+ assertThat(hitTestResult.containsAll(nodeListOf("Hello", "great", "foo", "this"))).isFalse()
}
@Test
fun testGet() {
val hitTestResult = fillHitTestResult()
- assertThat(hitTestResult[0]).isEqualTo("Hello")
- assertThat(hitTestResult[1]).isEqualTo("World")
- assertThat(hitTestResult[2]).isEqualTo("this")
- assertThat(hitTestResult[3]).isEqualTo("is")
- assertThat(hitTestResult[4]).isEqualTo("great")
+ assertThat(hitTestResult[0]).isEqualTo(SNode("Hello"))
+ assertThat(hitTestResult[1]).isEqualTo(SNode("World"))
+ assertThat(hitTestResult[2]).isEqualTo(SNode("this"))
+ assertThat(hitTestResult[3]).isEqualTo(SNode("is"))
+ assertThat(hitTestResult[4]).isEqualTo(SNode("great"))
}
@Test
@@ -229,14 +230,14 @@
assertThat(hitTestResult.isEmpty()).isFalse()
hitTestResult.clear()
assertThat(hitTestResult.isEmpty()).isTrue()
- assertThat(HitTestResult<String>().isEmpty()).isTrue()
+ assertThat(HitTestResult().isEmpty()).isTrue()
}
@Test
fun testIterator() {
val hitTestResult = fillHitTestResult()
assertThat(hitTestResult.toList()).isEqualTo(
- listOf("Hello", "World", "this", "is", "great")
+ nodeListOf("Hello", "World", "this", "is", "great")
)
}
@@ -266,12 +267,12 @@
assertThat(iterator.hasNext()).isTrue()
val hasPrevious = (index != 0)
assertThat(iterator.hasPrevious()).isEqualTo(hasPrevious)
- assertThat(iterator.next()).isEqualTo(value)
+ assertThat(iterator.next()).isEqualTo(SNode(value))
}
for (index in values.lastIndex downTo 0) {
val value = values[index]
- assertThat(iterator.previous()).isEqualTo(value)
+ assertThat(iterator.previous()).isEqualTo(SNode(value))
}
}
@@ -290,12 +291,12 @@
assertThat(iterator.hasNext()).isTrue()
val hasPrevious = (index != 0)
assertThat(iterator.hasPrevious()).isEqualTo(hasPrevious)
- assertThat(iterator.next()).isEqualTo(values[index])
+ assertThat(iterator.next()).isEqualTo(SNode(values[index]))
}
for (index in values.lastIndex downTo 0) {
val value = values[index]
- assertThat(iterator.previous()).isEqualTo(value)
+ assertThat(iterator.previous()).isEqualTo(SNode(value))
}
}
@@ -305,45 +306,45 @@
val subList = hitTestResult.subList(2, 4)
assertThat(subList).hasSize(2)
- assertThat(subList.toList()).isEqualTo(listOf("this", "is"))
- assertThat(subList.contains("this")).isTrue()
- assertThat(subList.contains("foo")).isFalse()
- assertThat(subList.containsAll(listOf("this", "is"))).isTrue()
- assertThat(subList.containsAll(listOf("is", "this"))).isTrue()
- assertThat(subList.containsAll(listOf("foo", "this"))).isFalse()
- assertThat(subList[0]).isEqualTo("this")
- assertThat(subList[1]).isEqualTo("is")
- assertThat(subList.indexOf("is")).isEqualTo(1)
+ assertThat(subList.toList()).isEqualTo(nodeListOf("this", "is"))
+ assertThat(subList.contains(SNode("this"))).isTrue()
+ assertThat(subList.contains(SNode("foo"))).isFalse()
+ assertThat(subList.containsAll(nodeListOf("this", "is"))).isTrue()
+ assertThat(subList.containsAll(nodeListOf("is", "this"))).isTrue()
+ assertThat(subList.containsAll(nodeListOf("foo", "this"))).isFalse()
+ assertThat(subList[0]).isEqualTo(SNode("this"))
+ assertThat(subList[1]).isEqualTo(SNode("is"))
+ assertThat(subList.indexOf(SNode("is"))).isEqualTo(1)
assertThat(subList.isEmpty()).isFalse()
assertThat(hitTestResult.subList(4, 4).isEmpty()).isTrue()
assertThat(subList.subList(0, 2).toList()).isEqualTo(subList.toList())
- assertThat(subList.subList(0, 1)[0]).isEqualTo("this")
+ assertThat(subList.subList(0, 1)[0]).isEqualTo(SNode("this"))
val listIterator1 = subList.listIterator()
assertThat(listIterator1.hasNext()).isTrue()
assertThat(listIterator1.hasPrevious()).isFalse()
assertThat(listIterator1.nextIndex()).isEqualTo(0)
- assertThat(listIterator1.next()).isEqualTo("this")
+ assertThat(listIterator1.next()).isEqualTo(SNode("this"))
assertThat(listIterator1.hasNext()).isTrue()
assertThat(listIterator1.hasPrevious()).isTrue()
assertThat(listIterator1.nextIndex()).isEqualTo(1)
- assertThat(listIterator1.next()).isEqualTo("is")
+ assertThat(listIterator1.next()).isEqualTo(SNode("is"))
assertThat(listIterator1.hasNext()).isFalse()
assertThat(listIterator1.hasPrevious()).isTrue()
assertThat(listIterator1.previousIndex()).isEqualTo(1)
- assertThat(listIterator1.previous()).isEqualTo("is")
+ assertThat(listIterator1.previous()).isEqualTo(SNode("is"))
val listIterator2 = subList.listIterator(1)
assertThat(listIterator2.hasPrevious()).isTrue()
assertThat(listIterator2.hasNext()).isTrue()
assertThat(listIterator2.previousIndex()).isEqualTo(0)
assertThat(listIterator2.nextIndex()).isEqualTo(1)
- assertThat(listIterator2.previous()).isEqualTo("this")
+ assertThat(listIterator2.previous()).isEqualTo(SNode("this"))
}
@Test
fun siblingHits() {
- val hitTestResult = HitTestResult<String>()
+ val hitTestResult = HitTestResult()
hitTestResult.siblingHits {
hitTestResult.hit("Hello", true) {
@@ -361,7 +362,7 @@
hitTestResult.hit("great", true) {}
}
assertThat(hitTestResult.toList()).isEqualTo(
- listOf(
+ nodeListOf(
"Hello",
"World",
"this",
@@ -371,8 +372,8 @@
)
}
- private fun fillHitTestResult(last: String? = null): HitTestResult<String> {
- val hitTestResult = HitTestResult<String>()
+ private fun fillHitTestResult(last: String? = null): HitTestResult {
+ val hitTestResult = HitTestResult()
hitTestResult.hit("Hello", true) {
hitTestResult.hit("World", true) {
hitTestResult.hit("this", true) {
@@ -389,3 +390,27 @@
return hitTestResult
}
}
+
+internal fun nodeListOf(vararg strings: String) = strings.map { SNode(it) }
+internal fun HitTestResult.hit(string: String, isInLayer: Boolean, childHitTest: () -> Unit) {
+ hit(SNode(string), isInLayer, childHitTest)
+}
+
+internal fun HitTestResult.hitInMinimumTouchTarget(
+ string: String,
+ distanceFromEdge: Float,
+ isInLayer: Boolean,
+ childHitTest: () -> Unit
+) = hitInMinimumTouchTarget(SNode(string), distanceFromEdge, isInLayer, childHitTest)
+
+internal fun HitTestResult.speculativeHit(
+ string: String,
+ distanceFromEdge: Float,
+ isInLayer: Boolean,
+ childHitTest: () -> Unit
+) = speculativeHit(SNode(string), distanceFromEdge, isInLayer, childHitTest)
+
+internal fun HitTestResult.contains(string: String) = contains(SNode(string))
+internal fun HitTestResult.indexOf(string: String) = indexOf(SNode(string))
+internal fun HitTestResult.lastIndexOf(string: String) = lastIndexOf(SNode(string))
+internal data class SNode(val string: String) : Modifier.Node()
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 5c31d51..90f7edc 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
@@ -1043,7 +1043,7 @@
).apply {
attach(MockOwner())
}
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
layoutNode.hitTest(Offset(0f, 0f), hit)
@@ -1061,7 +1061,7 @@
).apply {
attach(MockOwner())
}
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
layoutNode.hitTest(Offset(-3f, 3f), hit, true)
@@ -1079,7 +1079,7 @@
).apply {
attach(MockOwner())
}
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
layoutNode.hitTest(Offset(0f, 3f), hit, true)
@@ -1097,7 +1097,7 @@
).apply {
attach(MockOwner())
}
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
layoutNode.hitTest(Offset(3f, 0f), hit, true)
@@ -1115,7 +1115,7 @@
)
outerNode.add(layoutNode)
layoutNode.onNodePlaced()
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
outerNode.hitTest(Offset(-3f, 3f), hit, true)
@@ -1136,7 +1136,7 @@
)
outerNode.add(layoutNode)
layoutNode.onNodePlaced()
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
outerNode.hitTest(Offset(25f, 25f), hit)
@@ -1159,7 +1159,7 @@
)
outerNode.add(layoutNode)
layoutNode.onNodePlaced()
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
outerNode.hitTest(Offset(25f, 25f), hit)
@@ -1187,7 +1187,7 @@
layoutNode1.onNodePlaced()
layoutNode2.onNodePlaced()
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Hit closer to layoutNode1
outerNode.hitTest(Offset(5.1f, 5.5f), hit, true)
@@ -1290,7 +1290,7 @@
layoutNode2.onNodePlaced()
layoutNode3.onNodePlaced()
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Hit outside of layoutNode2, but near layoutNode1
outerNode.hitTest(Offset(10.1f, 10.1f), hit, true)
@@ -1325,7 +1325,7 @@
layoutNode1.onNodePlaced()
layoutNode2.onNodePlaced()
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Hit layoutNode1
outerNode.hitTest(Offset(3.95f, 3.95f), hit, true)
@@ -1354,7 +1354,7 @@
).apply {
attach(MockOwner())
}
- val hit = HitTestResult<SemanticsModifierNode>()
+ val hit = HitTestResult()
layoutNode.hitTestSemantics(Offset(-3f, 3f), hit)
@@ -1372,7 +1372,7 @@
val layoutNode = LayoutNode(0, 0, 1, 1, semanticsModifier, DpSize(48.dp, 48.dp))
outerNode.add(layoutNode)
layoutNode.onNodePlaced()
- val hit = HitTestResult<SemanticsModifierNode>()
+ val hit = HitTestResult()
layoutNode.hitTestSemantics(Offset(-3f, 3f), hit)
@@ -1406,42 +1406,42 @@
layoutNode2.onNodePlaced()
// Hit closer to layoutNode1
- val hit1 = HitTestResult<SemanticsModifierNode>()
+ val hit1 = HitTestResult()
outerNode.hitTestSemantics(Offset(5.1f, 5.5f), hit1, true)
assertThat(hit1).hasSize(1)
assertThat(hit1[0]).isEqualTo(semanticsModifier1)
// Hit closer to layoutNode2
- val hit2 = HitTestResult<SemanticsModifierNode>()
+ val hit2 = HitTestResult()
outerNode.hitTestSemantics(Offset(5.9f, 5.5f), hit2, true)
assertThat(hit2).hasSize(1)
assertThat(hit2[0]).isEqualTo(semanticsModifier2)
// Hit closer to layoutNode1
- val hit3 = HitTestResult<SemanticsModifierNode>()
+ val hit3 = HitTestResult()
outerNode.hitTestSemantics(Offset(5.5f, 5.1f), hit3, true)
assertThat(hit3).hasSize(1)
assertThat(hit3[0]).isEqualTo(semanticsModifier1)
// Hit closer to layoutNode2
- val hit4 = HitTestResult<SemanticsModifierNode>()
+ val hit4 = HitTestResult()
outerNode.hitTestSemantics(Offset(5.5f, 5.9f), hit4, true)
assertThat(hit4).hasSize(1)
assertThat(hit4[0]).isEqualTo(semanticsModifier2)
// Hit inside layoutNode1
- val hit5 = HitTestResult<SemanticsModifierNode>()
+ val hit5 = HitTestResult()
outerNode.hitTestSemantics(Offset(4.9f, 4.9f), hit5, true)
assertThat(hit5).hasSize(1)
assertThat(hit5[0]).isEqualTo(semanticsModifier1)
// Hit inside layoutNode2
- val hit6 = HitTestResult<SemanticsModifierNode>()
+ val hit6 = HitTestResult()
outerNode.hitTestSemantics(Offset(6.1f, 6.1f), hit6, true)
assertThat(hit6).hasSize(1)
@@ -1466,14 +1466,14 @@
layoutNode2.onNodePlaced()
// Hit layoutNode1
- val hit1 = HitTestResult<SemanticsModifierNode>()
+ val hit1 = HitTestResult()
outerNode.hitTestSemantics(Offset(3.95f, 3.95f), hit1, true)
assertThat(hit1).hasSize(1)
assertThat(hit1[0].toModifier()).isEqualTo(semanticsModifier1)
// Hit layoutNode2
- val hit2 = HitTestResult<SemanticsModifierNode>()
+ val hit2 = HitTestResult()
outerNode.hitTestSemantics(Offset(4.05f, 4.05f), hit2, true)
assertThat(hit2).hasSize(1)
@@ -1490,7 +1490,7 @@
).apply {
attach(MockOwner())
}
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
layoutNode.hitTest(Offset(-1f, -1f), hit)
layoutNode.hitTest(Offset(0f, -1f), hit)
@@ -1518,7 +1518,7 @@
).apply {
attach(MockOwner())
}
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
layoutNode.hitTest(Offset(-3f, -5f), hit)
layoutNode.hitTest(Offset(0f, -5f), hit)
@@ -1593,7 +1593,7 @@
else -> throw IllegalStateException()
}
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Act.
@@ -1686,8 +1686,8 @@
val offset1 = Offset(25f, 25f)
val offset2 = Offset(75f, 75f)
- val hit1 = mutableListOf<PointerInputModifierNode>()
- val hit2 = mutableListOf<PointerInputModifierNode>()
+ val hit1 = mutableListOf<Modifier.Node>()
+ val hit2 = mutableListOf<Modifier.Node>()
// Act
@@ -1767,9 +1767,9 @@
val offset2 = Offset(75f, 75f)
val offset3 = Offset(125f, 125f)
- val hit1 = mutableListOf<PointerInputModifierNode>()
- val hit2 = mutableListOf<PointerInputModifierNode>()
- val hit3 = mutableListOf<PointerInputModifierNode>()
+ val hit1 = mutableListOf<Modifier.Node>()
+ val hit2 = mutableListOf<Modifier.Node>()
+ val hit3 = mutableListOf<Modifier.Node>()
parentLayoutNode.hitTest(offset1, hit1)
parentLayoutNode.hitTest(offset2, hit2)
@@ -1831,9 +1831,9 @@
val offset2 = Offset(50f, 75f)
val offset3 = Offset(50f, 125f)
- val hit1 = mutableListOf<PointerInputModifierNode>()
- val hit2 = mutableListOf<PointerInputModifierNode>()
- val hit3 = mutableListOf<PointerInputModifierNode>()
+ val hit1 = mutableListOf<Modifier.Node>()
+ val hit2 = mutableListOf<Modifier.Node>()
+ val hit3 = mutableListOf<Modifier.Node>()
// Act
@@ -1895,9 +1895,9 @@
val offset2 = Offset(75f, 50f)
val offset3 = Offset(125f, 50f)
- val hit1 = mutableListOf<PointerInputModifierNode>()
- val hit2 = mutableListOf<PointerInputModifierNode>()
- val hit3 = mutableListOf<PointerInputModifierNode>()
+ val hit1 = mutableListOf<Modifier.Node>()
+ val hit2 = mutableListOf<Modifier.Node>()
+ val hit3 = mutableListOf<Modifier.Node>()
// Act
@@ -2009,7 +2009,7 @@
Offset(4f, 3f)
)
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Act and Assert
@@ -2065,7 +2065,7 @@
val offset1 = Offset(50f, 75f)
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Act.
@@ -2112,7 +2112,7 @@
layoutNode1.onNodePlaced()
val offset1 = Offset(499f, 499f)
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Act.
@@ -2171,7 +2171,7 @@
val offset1 = Offset(499f, 499f)
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Act.
@@ -2218,7 +2218,7 @@
val offset = Offset(50f, 50f)
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Act.
@@ -2245,7 +2245,7 @@
val offset = Offset.Zero
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Act.
@@ -2292,7 +2292,7 @@
parent.remeasure()
parent.replace()
- val hit = mutableListOf<PointerInputModifierNode>()
+ val hit = mutableListOf<Modifier.Node>()
// Act.
@@ -2618,6 +2618,8 @@
TODO("Not yet implemented")
}
+ val invalidatedLayers = mutableListOf<OwnedLayer>()
+
override fun createLayer(
drawBlock: (Canvas) -> Unit,
invalidateParentLayer: () -> Unit
@@ -2670,6 +2672,7 @@
}
override fun invalidate() {
+ invalidatedLayers.add(this)
}
override fun destroy() {
@@ -2696,7 +2699,9 @@
}
}
+ var semanticsChanged: Boolean = false
override fun onSemanticsChange() {
+ semanticsChanged = true
}
override fun onLayoutChange(layoutNode: LayoutNode) {
@@ -2717,10 +2722,10 @@
@OptIn(ExperimentalComposeUiApi::class)
private fun LayoutNode.hitTest(
pointerPosition: Offset,
- hitPointerInputFilters: MutableList<PointerInputModifierNode>,
+ hitPointerInputFilters: MutableList<Modifier.Node>,
isTouchEvent: Boolean = false
) {
- val hitTestResult = HitTestResult<PointerInputModifierNode>()
+ val hitTestResult = HitTestResult()
hitTest(pointerPosition, hitTestResult, isTouchEvent)
hitPointerInputFilters.addAll(hitTestResult)
}
@@ -2786,7 +2791,9 @@
}
@OptIn(ExperimentalComposeUiApi::class)
-fun List<PointerInputModifierNode>.toFilters(): List<PointerInputFilter> = map { it.toFilter() }
+fun List<Modifier.Node>.toFilters(): List<PointerInputFilter> = map {
+ (it as PointerInputModifierNode).toFilter()
+}
// This returns the corresponding modifier that produced the Node. This is only possible for
// Nodes that are BackwardsCompatNodes and once we refactor semantics / pointer input to use
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 e7c9df02..f730450 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
@@ -268,15 +268,15 @@
for (constraints in channel) {
val newConstraints = channel.tryReceive().getOrNull() ?: constraints
val currentConstraints =
- if (direction.value == 1) startConstraint else endConstraint
+ if (direction.intValue == 1) startConstraint else endConstraint
if (newConstraints != currentConstraints) {
- if (direction.value == 1) {
+ if (direction.intValue == 1) {
endConstraint = newConstraints
} else {
startConstraint = newConstraints
}
- progress.animateTo(direction.value.toFloat(), animationSpec)
- direction.value = if (direction.value == 1) 0 else 1
+ progress.animateTo(direction.intValue.toFloat(), animationSpec)
+ direction.intValue = if (direction.intValue == 1) 0 else 1
finishedAnimationListener?.invoke()
}
}
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 f447e35..42515c3 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
@@ -25,10 +25,10 @@
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.FloatState
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -97,12 +97,12 @@
* You should use this state to offset your content accordingly. The recommended way is to
* use `Modifier.offsetPx`. This includes the resistance by default, if resistance is enabled.
*/
- val offset: State<Float> get() = offsetState
+ val offset: FloatState get() = offsetState
/**
* The amount by which the [carouselSwipeable] has been swiped past its bounds.
*/
- val overflow: State<Float> get() = overflowState
+ val overflow: FloatState get() = overflowState
// Use `Float.NaN` as a placeholder while the state is uninitialised.
private val offsetState = mutableStateOf(0f)
@@ -132,8 +132,8 @@
requireNotNull(initialOffset) {
"The initial value must have an associated anchor."
}
- offsetState.value = initialOffset
- absoluteOffset.value = initialOffset
+ offsetState.floatValue = initialOffset
+ absoluteOffset.floatValue = initialOffset
}
}
@@ -168,10 +168,10 @@
newState ?: newAnchors.keys.minByOrNull { abs(it - animationTargetValue) }!!
} else {
// we're not animating, proceed by finding the new anchors for an old value
- val actualOldValue = oldAnchors[offset.value]
+ val actualOldValue = oldAnchors[offset.floatValue]
val value = if (actualOldValue == currentValue) currentValue else actualOldValue
newAnchors.getOffset(value) ?: newAnchors
- .keys.minByOrNull { abs(it - offset.value) }!!
+ .keys.minByOrNull { abs(it - offset.floatValue) }!!
}
try {
animateInternalToOffset(targetOffset, animationSpec)
@@ -193,24 +193,24 @@
internal var resistance: ResistanceConfig? by mutableStateOf(null)
internal val draggableState = DraggableState {
- val newAbsolute = absoluteOffset.value + it
+ val newAbsolute = absoluteOffset.floatValue + it
val clamped = newAbsolute.coerceIn(minBound, maxBound)
val overflow = newAbsolute - clamped
val resistanceDelta = resistance?.computeResistance(overflow) ?: 0f
- offsetState.value = clamped + resistanceDelta
- overflowState.value = overflow
- absoluteOffset.value = newAbsolute
+ offsetState.floatValue = clamped + resistanceDelta
+ overflowState.floatValue = overflow
+ absoluteOffset.floatValue = newAbsolute
}
private suspend fun snapInternalToOffset(target: Float) {
draggableState.drag {
- dragBy(target - absoluteOffset.value)
+ dragBy(target - absoluteOffset.floatValue)
}
}
private suspend fun animateInternalToOffset(target: Float, spec: AnimationSpec<Float>) {
draggableState.drag {
- var prevValue = absoluteOffset.value
+ var prevValue = absoluteOffset.floatValue
animationTarget.value = target
isAnimationRunning = true
try {
@@ -236,8 +236,8 @@
get() {
// TODO(calintat): Track current velocity (b/149549482) and use that here.
val target = animationTarget.value ?: computeTarget(
- offset = offset.value,
- lastValue = anchors.getOffset(currentValue) ?: offset.value,
+ offset = offset.floatValue,
+ lastValue = anchors.getOffset(currentValue) ?: offset.floatValue,
anchors = anchors.keys,
thresholds = thresholds,
velocity = 0f,
@@ -253,7 +253,7 @@
*/
val progress: SwipeProgress<T>
get() {
- val bounds = findBounds(offset.value, anchors.keys)
+ val bounds = findBounds(offset.floatValue, anchors.keys)
val from: T
val to: T
val fraction: Float
@@ -277,7 +277,7 @@
}
from = anchors.getValue(a)
to = anchors.getValue(b)
- fraction = (offset.value - a) / (b - a)
+ fraction = (offset.floatValue - a) / (b - a)
}
}
return SwipeProgress(from, to, fraction)
@@ -290,7 +290,7 @@
* moving from right to left or bottom to top, or 0f if no swipe or animation is in progress.
*/
val direction: Float
- get() = anchors.getOffset(currentValue)?.let { sign(offset.value - it) } ?: 0f
+ get() = anchors.getOffset(currentValue)?.let { sign(offset.floatValue - it) } ?: 0f
/**
* Set the state without any animation and suspend until it's set
@@ -323,7 +323,7 @@
}
animateInternalToOffset(targetOffset, anim)
} finally {
- val endOffset = absoluteOffset.value
+ val endOffset = absoluteOffset.floatValue
val endValue = anchors
// fighting rounding error once again, anchor should be as close as 0.5 pixels
.filterKeys { anchorOffset -> abs(anchorOffset - endOffset) < 0.5f }
@@ -350,7 +350,7 @@
latestNonEmptyAnchorsFlow.collect { anchors ->
val lastAnchor = anchors.getOffset(currentValue)!!
val targetValue = computeTarget(
- offset = offset.value,
+ offset = offset.floatValue,
lastValue = lastAnchor,
anchors = anchors.keys,
thresholds = thresholds,
@@ -381,9 +381,9 @@
* @return the amount of [delta] consumed
*/
fun performDrag(delta: Float): Float {
- val potentiallyConsumed = absoluteOffset.value + delta
+ val potentiallyConsumed = absoluteOffset.floatValue + delta
val clamped = potentiallyConsumed.coerceIn(minBound, maxBound)
- val deltaToConsume = clamped - absoluteOffset.value
+ val deltaToConsume = clamped - absoluteOffset.floatValue
if (abs(deltaToConsume) > 0) {
draggableState.dispatchRawDelta(deltaToConsume)
}
@@ -849,7 +849,7 @@
override suspend fun onPreFling(available: Velocity): Velocity {
val toFling = Offset(available.x, available.y).toFloat()
- return if (toFling < 0 && offset.value > minBound) {
+ return if (toFling < 0 && offset.floatValue > minBound) {
performFling(velocity = toFling)
// since we go to the anchor with tween settling, consume all for the best UX
available
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java b/core/core-i18n/src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java
index 23fa07b..f64ccb0 100644
--- a/core/core-i18n/src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java
@@ -34,7 +34,6 @@
import androidx.core.i18n.DateTimeFormatterSkeletonOptions.Year;
import androidx.core.os.BuildCompat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -86,12 +85,7 @@
}
@Test @SmallTest
- @SdkSuppress(maxSdkVersion = 33) // b/262909049: Do not run this test on pre-release Android U.
public void testSkeletonOptions() {
- if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
- return; // b/262909049: Do not run this test on pre-release Android U.
- }
-
final DateTimeFormatterSkeletonOptions.Builder builder =
new DateTimeFormatterSkeletonOptions.Builder()
.setYear(Year.NUMERIC)
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatJdkStylesTest.kt b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatJdkStylesTest.kt
index cead280..4f0a3ea 100644
--- a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatJdkStylesTest.kt
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatJdkStylesTest.kt
@@ -89,7 +89,9 @@
val jdkFormatter = DateFormat.getTimeInstance(javaStyle, locale)
val options = DateTimeFormatterJdkStyleOptions.createTimeInstance(javaStyle)
val compatFormatter = DateTimeFormatter(options, locale)
- assertEquals(jdkFormatter.format(testCalendar.time), compatFormatter.format(testCalendar))
+ assertEquals(
+ Helper.normalizeNnbsp(jdkFormatter.format(testCalendar.time)),
+ Helper.normalizeNnbsp(compatFormatter.format(testCalendar)))
}
private fun checkDateTime(
@@ -101,7 +103,9 @@
val options =
DateTimeFormatterJdkStyleOptions.createDateTimeInstance(javaDateStyle, javaTimeStyle)
val compatFormatter = DateTimeFormatter(options, locale)
- assertEquals(jdkFormatter.format(testCalendar.time), compatFormatter.format(testCalendar))
+ assertEquals(
+ Helper.normalizeNnbsp(jdkFormatter.format(testCalendar.time)),
+ Helper.normalizeNnbsp(compatFormatter.format(testCalendar)))
}
@Test @SmallTest
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterCommonOptionsTest.kt b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterCommonOptionsTest.kt
index 265f46b..6860ebe 100644
--- a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterCommonOptionsTest.kt
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterCommonOptionsTest.kt
@@ -40,12 +40,7 @@
)
@Test @SmallTest
- @SdkSuppress(maxSdkVersion = 33) // b/262909049: Do not run this test on pre-release Android U.
fun test() {
- if (Build.VERSION.SDK_INT == 33 && Build.VERSION.CODENAME != "REL") {
- return // b/262909049: Do not run this test on pre-release Android U.
- }
-
val commonFormats = mapOf(
DateTimeFormatterCommonOptions.ABBR_MONTH_WEEKDAY_DAY to "Sun, Sep 19",
DateTimeFormatterCommonOptions.ABBR_MONTH_DAY to "Sep 19",
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt
index 3671c09..9534b9f 100644
--- a/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt
@@ -205,35 +205,29 @@
val enUsForceH23 = Locale.forLanguageTag("en-US-u-hc-h23")
val enUsForceH24 = Locale.forLanguageTag("en-US-u-hc-h24")
- val expectedUs: String
- val expectedUs11: String
- val expectedUs12: String
- val expectedUs23: String
- val expectedUs24: String
- // TODO: check this. Is `-u-hc-` not honored at all? File bug, maybe implement workaround.
- if (BuildCompat.isAtLeastU()) {
- expectedUs = "9:42:12\u202FPM"
- } else {
- expectedUs = "9:42:12 PM"
- }
- expectedUs11 = expectedUs
- expectedUs12 = expectedUs
- expectedUs23 = expectedUs
- expectedUs24 = expectedUs
+ val expectedUs: String = "9:42:12 PM"
+ val expectedUs11: String = expectedUs
+ val expectedUs12: String = expectedUs
+ // TODO: check this. Is `-u-hc-` not honored at all?
+ // Official bug: https://unicode-org.atlassian.net/browse/ICU-11870
+ // It only manifests for the predefined formats (`DateFormat.MEDIUM` and so on),
+ // not for patterns generated from skeletons.
+ val expectedUs23: String = expectedUs
+ val expectedUs24: String = expectedUs
var formatter: java.text.DateFormat
// Formatting with style does not honor the uc overrides
formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, Locale.US)
- assertEquals(expectedUs, formatter.format(testMillis))
+ assertEquals(expectedUs, Helper.normalizeNnbsp(formatter.format(testMillis)))
formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, enUsForceH11)
- assertEquals(expectedUs11, formatter.format(testMillis))
+ assertEquals(expectedUs11, Helper.normalizeNnbsp(formatter.format(testMillis)))
formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, enUsForceH12)
- assertEquals(expectedUs12, formatter.format(testMillis))
+ assertEquals(expectedUs12, Helper.normalizeNnbsp(formatter.format(testMillis)))
formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, enUsForceH23)
- assertEquals(expectedUs23, formatter.format(testMillis))
+ assertEquals(expectedUs23, Helper.normalizeNnbsp(formatter.format(testMillis)))
formatter = java.text.DateFormat.getTimeInstance(java.text.DateFormat.MEDIUM, enUsForceH24)
- assertEquals(expectedUs24, formatter.format(testMillis))
+ assertEquals(expectedUs24, Helper.normalizeNnbsp(formatter.format(testMillis)))
}
@Test @SmallTest
diff --git a/core/core-i18n/src/androidTest/java/androidx/core/i18n/Helper.kt b/core/core-i18n/src/androidTest/java/androidx/core/i18n/Helper.kt
new file mode 100644
index 0000000..cea0afd5
--- /dev/null
+++ b/core/core-i18n/src/androidTest/java/androidx/core/i18n/Helper.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 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.core.i18n
+
+object Helper {
+ /*
+ * This method changes all instances of U+202F to U+0020.
+ *
+ * Android U takes ICU 71.1, which uses NNBSP (NARROW NO-BREAK SPACE, U+202F)
+ * betwee time and day cycle (for example 9:42\u202FPM)
+ *
+ * The Android `java.text.DateFormat` was patched to not use nnbsp (U+202F)
+ * in Android U, but ICU still returns times with U+202F.
+ * So this would give different results, but it is expected.
+ * In time this will probably go away (as the newer Android images propagate everywhere).
+ *
+ * And, since the patch happened without changing the Android version (pre-release),
+ * there are some Android U images that use space and some that use NNBSP.
+ * So testing the version is not enough to reliably tell if we will get.
+ */
+ fun normalizeNnbsp(text: String): String {
+ return text.replace("\u202F", " ")
+ }
+}
\ No newline at end of file
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 5b50848..9d411f1 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -885,7 +885,6 @@
}
public final class NotificationManagerCompat {
- ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS) @VisibleForTesting protected NotificationManagerCompat(android.app.NotificationManager, android.content.Context);
method public boolean areNotificationsEnabled();
method public void cancel(int);
method public void cancel(String?, int);
diff --git a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java
index f9226fe..3628701c 100644
--- a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutInfoCompatTest.java
@@ -524,4 +524,15 @@
final ShortcutInfo shortcut = compat.toShortcutInfo();
assertTrue(shortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER));
}
+
+ @Test
+ public void testSetCategoriesAndAddCapabilityBinding() {
+ HashSet<String> categories = new HashSet<>();
+ categories.add("a");
+ mBuilder.setActivity(new ComponentName(mContext, TestActivity.class))
+ .setCategories(categories)
+ .addCapabilityBinding("b")
+ .build();
+ assertEquals(1, categories.size());
+ }
}
diff --git a/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java b/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
index 5ecfc5f..ae70ed1 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
@@ -17,7 +17,6 @@
package androidx.core.app;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
import android.Manifest;
import android.app.AppOpsManager;
@@ -222,9 +221,8 @@
Context.NOTIFICATION_SERVICE);
}
- @RestrictTo(TESTS)
@VisibleForTesting
- protected NotificationManagerCompat(@NonNull NotificationManager notificationManager,
+ NotificationManagerCompat(@NonNull NotificationManager notificationManager,
@NonNull Context context) {
mContext = context;
mNotificationManager = notificationManager;
diff --git a/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java b/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
index b5b9106..0915eb8 100644
--- a/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
@@ -38,6 +38,7 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
+import androidx.collection.ArraySet;
import androidx.core.app.Person;
import androidx.core.content.LocusIdCompat;
import androidx.core.graphics.drawable.IconCompat;
@@ -800,7 +801,9 @@
*/
@NonNull
public Builder setCategories(@NonNull Set<String> categories) {
- mInfo.mCategories = categories;
+ ArraySet<String> set = new ArraySet<>();
+ set.addAll(categories);
+ mInfo.mCategories = set;
return this;
}
diff --git a/core/core/src/main/java/androidx/core/os/BuildCompat.java b/core/core/src/main/java/androidx/core/os/BuildCompat.java
index 7bed9ce..4dfca8e 100644
--- a/core/core/src/main/java/androidx/core/os/BuildCompat.java
+++ b/core/core/src/main/java/androidx/core/os/BuildCompat.java
@@ -27,7 +27,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresOptIn;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import java.util.Locale;
@@ -50,7 +50,7 @@
*
* @hide
*/
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
protected static boolean isAtLeastPreReleaseCodename(@NonNull String codename,
@NonNull String buildCodename) {
diff --git a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
index a1c9161..4f9487b 100644
--- a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
+++ b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
@@ -18,7 +18,6 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -172,7 +171,6 @@
/** @hide */
@VisibleForTesting
- @RestrictTo(TESTS)
public static void resetTypefaceCache() {
FontRequestWorker.resetTypefaceCache();
}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
index 594efb9..3524fe2 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
@@ -21,7 +21,6 @@
import android.os.CancellationSignal
import android.util.Log
import androidx.credentials.ClearCredentialStateRequest
-import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.credentials.CreateCredentialRequest
import androidx.credentials.CreateCredentialResponse
@@ -52,8 +51,7 @@
@Suppress("deprecation")
class CredentialProviderPlayServicesImpl(private val context: Context) : CredentialProvider {
- @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
- @set:RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
var googleApiAvailability = GoogleApiAvailability.getInstance()
override fun onGetCredential(
request: GetCredentialRequest,
diff --git a/datastore/datastore-preferences-core/build.gradle b/datastore/datastore-preferences-core/build.gradle
index 34b4020..a9112b0 100644
--- a/datastore/datastore-preferences-core/build.gradle
+++ b/datastore/datastore-preferences-core/build.gradle
@@ -115,10 +115,7 @@
)
dependencies {
- bundleInside(project(
- path: ":datastore:datastore-preferences-proto",
- configuration: "export"
- ))
+ bundleInside(project(path: ":datastore:datastore-preferences-proto", configuration: "export"))
}
androidx {
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index df1063c..ce7f919 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -1017,6 +1017,8 @@
\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] Button in androidx\.wear\.tiles\.material has been deprecated
# > Configure project :androidx-demos
WARNING: The option setting 'android\.experimental\.disableCompileSdkChecks=true' is experimental\.
+# Remove once aosp/2548452 is reverted
+WARNING: The option setting 'android\.overrideVersionCheck=true' is experimental\.
The current default is 'false'\.
# > Task :compose:foundation:foundation:processDebugAndroidTestManifest
\$SUPPORT/compose/foundation/foundation/src/androidAndroidTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
diff --git a/development/update_kotlin.sh b/development/update_kotlin.sh
new file mode 100755
index 0000000..a5c3673
--- /dev/null
+++ b/development/update_kotlin.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+set -e
+
+export KOTLIN_VERSION="1.9.0-dev-6188"
+
+# Download and place konan
+export KONAN_DIR=../../prebuilts/androidx/konan/nativeCompilerPrebuilts/dev/$KOTLIN_VERSION/linux-x86_64
+mkdir -p $KONAN_DIR
+curl -o $KONAN_DIR/kotlin-native-prebuilt-linux-x86_64-$KOTLIN_VERSION.tar.gz https://download-cf.jetbrains.com/kotlin/native/builds/dev/$KOTLIN_VERSION/linux-x86_64/kotlin-native-prebuilt-linux-x86_64-$KOTLIN_VERSION.tar.gz
+
+# Download maven artifacts
+ARTIFACTS_TO_DOWNLOAD="org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlinx-serialization-compiler-plugin-embeddable:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-test:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-test-junit:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION,"
+ARTIFACTS_TO_DOWNLOAD+="org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:$KOTLIN_VERSION"
+
+./development/importMaven/importMaven.sh --allow-jetbrains-dev "$ARTIFACTS_TO_DOWNLOAD"
diff --git a/docs/onboarding.md b/docs/onboarding.md
index dc8f072..feb4b7d 100644
--- a/docs/onboarding.md
+++ b/docs/onboarding.md
@@ -785,20 +785,26 @@
implementing bug fixes are expected to include new regression tests specific to
the issue being fixed.
-### Running Tests
+### Running tests {#run-tests}
-#### Single Test Class or Method
+Generally, tests in the AndroidX repository should be run through the Android
+Studio UI. You can also run tests from the command line or via remote devices on
+FTL, see
+[Running unit and integration tests](/company/teams/androidx/testing.md#running)
+for details.
-1. Open the desired test file in Android Studio.
-2. Right-click on a test class or @Test method name and select `Run FooBarTest`
+#### Single test class or method
-#### Full Test Package
+1. Open the desired test file in Android Studio
+2. Right-click on a test class or `@Test` method name and select `Run <name>`
-1. In the project side panel open the desired module.
-2. Find the directory with the tests
-3. Right-click on the directory and select `Run androidx.foobar`
+#### Full test package
-### Running Sample Apps
+1. In the `Project` side panel, open the desired module
+2. Find the package directory with the tests
+3. Right-click on the directory and select `Run <package>`
+
+### Running sample apps {#run-samples}
The AndroidX repository has a set of Android applications that exercise AndroidX
code. These applications can be useful when you want to debug a real running
diff --git a/emoji2/emoji2-bundled/api/1.4.0-beta03.txt b/emoji2/emoji2-bundled/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/1.4.0-beta03.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+ public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+ ctor public BundledEmojiCompatConfig(android.content.Context);
+ }
+
+}
+
diff --git a/emoji2/emoji2-bundled/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2-bundled/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+ public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+ ctor public BundledEmojiCompatConfig(android.content.Context);
+ }
+
+}
+
diff --git a/emoji2/emoji2-bundled/api/res-1.4.0-beta03.txt b/emoji2/emoji2-bundled/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/res-1.4.0-beta03.txt
diff --git a/emoji2/emoji2-bundled/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2-bundled/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+ public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+ ctor public BundledEmojiCompatConfig(android.content.Context);
+ }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/api/1.4.0-beta03.txt b/emoji2/emoji2-emojipicker/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..e2360fa
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/1.4.0-beta03.txt
@@ -0,0 +1,43 @@
+// Signature format: 4.0
+package androidx.emoji2.emojipicker {
+
+ public final class EmojiPickerView extends android.widget.FrameLayout {
+ ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+ ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs);
+ ctor public EmojiPickerView(android.content.Context context);
+ method public int getEmojiGridColumns();
+ method public float getEmojiGridRows();
+ method public void setEmojiGridColumns(int);
+ method public void setEmojiGridRows(float);
+ method public void setOnEmojiPickedListener(androidx.core.util.Consumer<androidx.emoji2.emojipicker.EmojiViewItem>? onEmojiPickedListener);
+ method public void setRecentEmojiProvider(androidx.emoji2.emojipicker.RecentEmojiProvider recentEmojiProvider);
+ property public final int emojiGridColumns;
+ property public final float emojiGridRows;
+ }
+
+ public final class EmojiViewItem {
+ ctor public EmojiViewItem(String emoji, java.util.List<java.lang.String> variants);
+ method public String getEmoji();
+ method public java.util.List<java.lang.String> getVariants();
+ property public final String emoji;
+ property public final java.util.List<java.lang.String> variants;
+ }
+
+ public interface RecentEmojiAsyncProvider {
+ method public com.google.common.util.concurrent.ListenableFuture<java.util.List<java.lang.String>> getRecentEmojiListAsync();
+ method public void recordSelection(String emoji);
+ }
+
+ public interface RecentEmojiProvider {
+ method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+ method public void recordSelection(String emoji);
+ }
+
+ public final class RecentEmojiProviderAdapter implements androidx.emoji2.emojipicker.RecentEmojiProvider {
+ ctor public RecentEmojiProviderAdapter(androidx.emoji2.emojipicker.RecentEmojiAsyncProvider recentEmojiAsyncProvider);
+ method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+ method public void recordSelection(String emoji);
+ }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2-emojipicker/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..e2360fa
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,43 @@
+// Signature format: 4.0
+package androidx.emoji2.emojipicker {
+
+ public final class EmojiPickerView extends android.widget.FrameLayout {
+ ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+ ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs);
+ ctor public EmojiPickerView(android.content.Context context);
+ method public int getEmojiGridColumns();
+ method public float getEmojiGridRows();
+ method public void setEmojiGridColumns(int);
+ method public void setEmojiGridRows(float);
+ method public void setOnEmojiPickedListener(androidx.core.util.Consumer<androidx.emoji2.emojipicker.EmojiViewItem>? onEmojiPickedListener);
+ method public void setRecentEmojiProvider(androidx.emoji2.emojipicker.RecentEmojiProvider recentEmojiProvider);
+ property public final int emojiGridColumns;
+ property public final float emojiGridRows;
+ }
+
+ public final class EmojiViewItem {
+ ctor public EmojiViewItem(String emoji, java.util.List<java.lang.String> variants);
+ method public String getEmoji();
+ method public java.util.List<java.lang.String> getVariants();
+ property public final String emoji;
+ property public final java.util.List<java.lang.String> variants;
+ }
+
+ public interface RecentEmojiAsyncProvider {
+ method public com.google.common.util.concurrent.ListenableFuture<java.util.List<java.lang.String>> getRecentEmojiListAsync();
+ method public void recordSelection(String emoji);
+ }
+
+ public interface RecentEmojiProvider {
+ method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+ method public void recordSelection(String emoji);
+ }
+
+ public final class RecentEmojiProviderAdapter implements androidx.emoji2.emojipicker.RecentEmojiProvider {
+ ctor public RecentEmojiProviderAdapter(androidx.emoji2.emojipicker.RecentEmojiAsyncProvider recentEmojiAsyncProvider);
+ method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+ method public void recordSelection(String emoji);
+ }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/api/res-1.4.0-beta03.txt b/emoji2/emoji2-emojipicker/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/res-1.4.0-beta03.txt
diff --git a/emoji2/emoji2-emojipicker/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2-emojipicker/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..e2360fa
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,43 @@
+// Signature format: 4.0
+package androidx.emoji2.emojipicker {
+
+ public final class EmojiPickerView extends android.widget.FrameLayout {
+ ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+ ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs);
+ ctor public EmojiPickerView(android.content.Context context);
+ method public int getEmojiGridColumns();
+ method public float getEmojiGridRows();
+ method public void setEmojiGridColumns(int);
+ method public void setEmojiGridRows(float);
+ method public void setOnEmojiPickedListener(androidx.core.util.Consumer<androidx.emoji2.emojipicker.EmojiViewItem>? onEmojiPickedListener);
+ method public void setRecentEmojiProvider(androidx.emoji2.emojipicker.RecentEmojiProvider recentEmojiProvider);
+ property public final int emojiGridColumns;
+ property public final float emojiGridRows;
+ }
+
+ public final class EmojiViewItem {
+ ctor public EmojiViewItem(String emoji, java.util.List<java.lang.String> variants);
+ method public String getEmoji();
+ method public java.util.List<java.lang.String> getVariants();
+ property public final String emoji;
+ property public final java.util.List<java.lang.String> variants;
+ }
+
+ public interface RecentEmojiAsyncProvider {
+ method public com.google.common.util.concurrent.ListenableFuture<java.util.List<java.lang.String>> getRecentEmojiListAsync();
+ method public void recordSelection(String emoji);
+ }
+
+ public interface RecentEmojiProvider {
+ method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+ method public void recordSelection(String emoji);
+ }
+
+ public final class RecentEmojiProviderAdapter implements androidx.emoji2.emojipicker.RecentEmojiProvider {
+ ctor public RecentEmojiProviderAdapter(androidx.emoji2.emojipicker.RecentEmojiAsyncProvider recentEmojiAsyncProvider);
+ method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+ method public void recordSelection(String emoji);
+ }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
index c6962da..94e4abc 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
@@ -112,7 +112,7 @@
.getDimensionPixelSize(R.dimen.emoji_picker_popup_view_elevation)
.toFloat()
showAtLocation(
- emojiView,
+ popupView,
Gravity.NO_GRAVITY,
x.roundToInt(),
y
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
index d9694c0..d2befe8 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
@@ -28,5 +28,5 @@
<string name="emoji_category_symbols" msgid="5626171724310261787">"符號"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"旗幟"</string>
<string name="emoji_empty_non_recent_category" msgid="288822832574892625">"沒有可用的 Emoji"</string>
- <string name="emoji_empty_recent_category" msgid="7863877827879290200">"您尚未使用任何 Emoji"</string>
+ <string name="emoji_empty_recent_category" msgid="7863877827879290200">"你尚未使用任何 Emoji"</string>
</resources>
diff --git a/emoji2/emoji2-views-helper/api/1.4.0-beta03.txt b/emoji2/emoji2-views-helper/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..30a6feb
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/1.4.0-beta03.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.emoji2.viewsintegration {
+
+ public final class EmojiEditTextHelper {
+ ctor public EmojiEditTextHelper(android.widget.EditText);
+ ctor public EmojiEditTextHelper(android.widget.EditText, boolean);
+ method public android.text.method.KeyListener? getKeyListener(android.text.method.KeyListener?);
+ method public int getMaxEmojiCount();
+ method public boolean isEnabled();
+ method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+ method public void setEnabled(boolean);
+ method public void setMaxEmojiCount(@IntRange(from=0) int);
+ }
+
+ public final class EmojiTextViewHelper {
+ ctor public EmojiTextViewHelper(android.widget.TextView);
+ ctor public EmojiTextViewHelper(android.widget.TextView, boolean);
+ method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+ method public boolean isEnabled();
+ method public void setAllCaps(boolean);
+ method public void setEnabled(boolean);
+ method public void updateTransformationMethod();
+ method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+ }
+
+}
+
diff --git a/emoji2/emoji2-views-helper/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2-views-helper/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..30a6feb
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.emoji2.viewsintegration {
+
+ public final class EmojiEditTextHelper {
+ ctor public EmojiEditTextHelper(android.widget.EditText);
+ ctor public EmojiEditTextHelper(android.widget.EditText, boolean);
+ method public android.text.method.KeyListener? getKeyListener(android.text.method.KeyListener?);
+ method public int getMaxEmojiCount();
+ method public boolean isEnabled();
+ method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+ method public void setEnabled(boolean);
+ method public void setMaxEmojiCount(@IntRange(from=0) int);
+ }
+
+ public final class EmojiTextViewHelper {
+ ctor public EmojiTextViewHelper(android.widget.TextView);
+ ctor public EmojiTextViewHelper(android.widget.TextView, boolean);
+ method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+ method public boolean isEnabled();
+ method public void setAllCaps(boolean);
+ method public void setEnabled(boolean);
+ method public void updateTransformationMethod();
+ method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+ }
+
+}
+
diff --git a/emoji2/emoji2-views-helper/api/res-1.4.0-beta03.txt b/emoji2/emoji2-views-helper/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/res-1.4.0-beta03.txt
diff --git a/emoji2/emoji2-views-helper/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2-views-helper/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..30a6feb
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.emoji2.viewsintegration {
+
+ public final class EmojiEditTextHelper {
+ ctor public EmojiEditTextHelper(android.widget.EditText);
+ ctor public EmojiEditTextHelper(android.widget.EditText, boolean);
+ method public android.text.method.KeyListener? getKeyListener(android.text.method.KeyListener?);
+ method public int getMaxEmojiCount();
+ method public boolean isEnabled();
+ method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+ method public void setEnabled(boolean);
+ method public void setMaxEmojiCount(@IntRange(from=0) int);
+ }
+
+ public final class EmojiTextViewHelper {
+ ctor public EmojiTextViewHelper(android.widget.TextView);
+ ctor public EmojiTextViewHelper(android.widget.TextView, boolean);
+ method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+ method public boolean isEnabled();
+ method public void setAllCaps(boolean);
+ method public void setEnabled(boolean);
+ method public void updateTransformationMethod();
+ method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+ }
+
+}
+
diff --git a/emoji2/emoji2-views/api/1.4.0-beta03.txt b/emoji2/emoji2-views/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/1.4.0-beta03.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+ public class EmojiButton extends android.widget.Button {
+ ctor public EmojiButton(android.content.Context);
+ ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+ public class EmojiEditText extends android.widget.EditText {
+ ctor public EmojiEditText(android.content.Context);
+ ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+ method public int getMaxEmojiCount();
+ method public void setMaxEmojiCount(@IntRange(from=0) int);
+ }
+
+ public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+ ctor public EmojiExtractTextLayout(android.content.Context);
+ ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+ method public int getEmojiReplaceStrategy();
+ method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+ method public void setEmojiReplaceStrategy(int);
+ }
+
+ public class EmojiTextView extends android.widget.TextView {
+ ctor public EmojiTextView(android.content.Context);
+ ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+}
+
diff --git a/emoji2/emoji2-views/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2-views/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+ public class EmojiButton extends android.widget.Button {
+ ctor public EmojiButton(android.content.Context);
+ ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+ public class EmojiEditText extends android.widget.EditText {
+ ctor public EmojiEditText(android.content.Context);
+ ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+ method public int getMaxEmojiCount();
+ method public void setMaxEmojiCount(@IntRange(from=0) int);
+ }
+
+ public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+ ctor public EmojiExtractTextLayout(android.content.Context);
+ ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+ method public int getEmojiReplaceStrategy();
+ method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+ method public void setEmojiReplaceStrategy(int);
+ }
+
+ public class EmojiTextView extends android.widget.TextView {
+ ctor public EmojiTextView(android.content.Context);
+ ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+}
+
diff --git a/emoji2/emoji2-views/api/res-1.4.0-beta03.txt b/emoji2/emoji2-views/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..8bc8423
--- /dev/null
+++ b/emoji2/emoji2-views/api/res-1.4.0-beta03.txt
@@ -0,0 +1,2 @@
+attr emojiReplaceStrategy
+attr maxEmojiCount
diff --git a/emoji2/emoji2-views/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2-views/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+ public class EmojiButton extends android.widget.Button {
+ ctor public EmojiButton(android.content.Context);
+ ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+ public class EmojiEditText extends android.widget.EditText {
+ ctor public EmojiEditText(android.content.Context);
+ ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+ method public int getMaxEmojiCount();
+ method public void setMaxEmojiCount(@IntRange(from=0) int);
+ }
+
+ public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+ ctor public EmojiExtractTextLayout(android.content.Context);
+ ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+ method public int getEmojiReplaceStrategy();
+ method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+ method public void setEmojiReplaceStrategy(int);
+ }
+
+ public class EmojiTextView extends android.widget.TextView {
+ ctor public EmojiTextView(android.content.Context);
+ ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+}
+
diff --git a/emoji2/emoji2/api/1.4.0-beta03.txt b/emoji2/emoji2/api/1.4.0-beta03.txt
new file mode 100644
index 0000000..11d9335
--- /dev/null
+++ b/emoji2/emoji2/api/1.4.0-beta03.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+ public final class DefaultEmojiCompatConfig {
+ method public static androidx.emoji2.text.FontRequestEmojiCompatConfig? create(android.content.Context);
+ }
+
+ @AnyThread public class EmojiCompat {
+ method public static androidx.emoji2.text.EmojiCompat get();
+ method public String getAssetSignature();
+ method public int getEmojiEnd(CharSequence, @IntRange(from=0) int);
+ method public int getEmojiMatch(CharSequence, @IntRange(from=0) int);
+ method public int getEmojiStart(CharSequence, @IntRange(from=0) int);
+ method public int getLoadState();
+ method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+ method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+ method @Deprecated public boolean hasEmojiGlyph(CharSequence);
+ method @Deprecated public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+ method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+ method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+ method public static boolean isConfigured();
+ method public void load();
+ method @CheckResult public CharSequence? process(CharSequence?);
+ method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+ method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ method public void updateEditorInfo(android.view.inputmethod.EditorInfo);
+ field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+ field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+ field public static final int EMOJI_FALLBACK = 2; // 0x2
+ field public static final int EMOJI_SUPPORTED = 1; // 0x1
+ field public static final int EMOJI_UNSUPPORTED = 0; // 0x0
+ field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+ field public static final int LOAD_STATE_FAILED = 2; // 0x2
+ field public static final int LOAD_STATE_LOADING = 0; // 0x0
+ field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+ field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+ field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+ field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+ field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+ field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+ }
+
+ public abstract static class EmojiCompat.Config {
+ ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+ method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+ method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+ method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+ method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+ method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+ method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+ method public androidx.emoji2.text.EmojiCompat.Config setSpanFactory(androidx.emoji2.text.EmojiCompat.SpanFactory);
+ method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+ method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+ method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ }
+
+ public static interface EmojiCompat.GlyphChecker {
+ method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+ }
+
+ public abstract static class EmojiCompat.InitCallback {
+ ctor public EmojiCompat.InitCallback();
+ method public void onFailed(Throwable?);
+ method public void onInitialized();
+ }
+
+ public static interface EmojiCompat.MetadataRepoLoader {
+ method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+ }
+
+ public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+ ctor public EmojiCompat.MetadataRepoLoaderCallback();
+ method public abstract void onFailed(Throwable?);
+ method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+ }
+
+ public static interface EmojiCompat.SpanFactory {
+ method @RequiresApi(19) public androidx.emoji2.text.EmojiSpan createSpan(androidx.emoji2.text.TypefaceEmojiRasterizer);
+ }
+
+ public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+ ctor public EmojiCompatInitializer();
+ method public Boolean create(android.content.Context);
+ method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+ }
+
+ @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+ method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+ method public final androidx.emoji2.text.TypefaceEmojiRasterizer getTypefaceRasterizer();
+ }
+
+ public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+ ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+ method @Deprecated public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+ method public androidx.emoji2.text.FontRequestEmojiCompatConfig setLoadingExecutor(java.util.concurrent.Executor);
+ method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+ }
+
+ public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+ ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+ method public long getRetryDelay();
+ }
+
+ public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+ ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+ method public abstract long getRetryDelay();
+ }
+
+ @AnyThread @RequiresApi(19) public final class MetadataRepo {
+ method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+ method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+ method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+ }
+
+ @AnyThread @RequiresApi(19) public class TypefaceEmojiRasterizer {
+ method public void draw(android.graphics.Canvas, float, float, android.graphics.Paint);
+ method public int getCodepointAt(int);
+ method public int getCodepointsLength();
+ method public int getHeight();
+ method public android.graphics.Typeface getTypeface();
+ method public int getWidth();
+ method public boolean isDefaultEmoji();
+ method public boolean isPreferredSystemRender();
+ }
+
+}
+
diff --git a/emoji2/emoji2/api/public_plus_experimental_1.4.0-beta03.txt b/emoji2/emoji2/api/public_plus_experimental_1.4.0-beta03.txt
new file mode 100644
index 0000000..11d9335
--- /dev/null
+++ b/emoji2/emoji2/api/public_plus_experimental_1.4.0-beta03.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+ public final class DefaultEmojiCompatConfig {
+ method public static androidx.emoji2.text.FontRequestEmojiCompatConfig? create(android.content.Context);
+ }
+
+ @AnyThread public class EmojiCompat {
+ method public static androidx.emoji2.text.EmojiCompat get();
+ method public String getAssetSignature();
+ method public int getEmojiEnd(CharSequence, @IntRange(from=0) int);
+ method public int getEmojiMatch(CharSequence, @IntRange(from=0) int);
+ method public int getEmojiStart(CharSequence, @IntRange(from=0) int);
+ method public int getLoadState();
+ method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+ method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+ method @Deprecated public boolean hasEmojiGlyph(CharSequence);
+ method @Deprecated public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+ method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+ method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+ method public static boolean isConfigured();
+ method public void load();
+ method @CheckResult public CharSequence? process(CharSequence?);
+ method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+ method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ method public void updateEditorInfo(android.view.inputmethod.EditorInfo);
+ field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+ field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+ field public static final int EMOJI_FALLBACK = 2; // 0x2
+ field public static final int EMOJI_SUPPORTED = 1; // 0x1
+ field public static final int EMOJI_UNSUPPORTED = 0; // 0x0
+ field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+ field public static final int LOAD_STATE_FAILED = 2; // 0x2
+ field public static final int LOAD_STATE_LOADING = 0; // 0x0
+ field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+ field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+ field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+ field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+ field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+ field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+ }
+
+ public abstract static class EmojiCompat.Config {
+ ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+ method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+ method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+ method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+ method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+ method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+ method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+ method public androidx.emoji2.text.EmojiCompat.Config setSpanFactory(androidx.emoji2.text.EmojiCompat.SpanFactory);
+ method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+ method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+ method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ }
+
+ public static interface EmojiCompat.GlyphChecker {
+ method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+ }
+
+ public abstract static class EmojiCompat.InitCallback {
+ ctor public EmojiCompat.InitCallback();
+ method public void onFailed(Throwable?);
+ method public void onInitialized();
+ }
+
+ public static interface EmojiCompat.MetadataRepoLoader {
+ method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+ }
+
+ public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+ ctor public EmojiCompat.MetadataRepoLoaderCallback();
+ method public abstract void onFailed(Throwable?);
+ method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+ }
+
+ public static interface EmojiCompat.SpanFactory {
+ method @RequiresApi(19) public androidx.emoji2.text.EmojiSpan createSpan(androidx.emoji2.text.TypefaceEmojiRasterizer);
+ }
+
+ public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+ ctor public EmojiCompatInitializer();
+ method public Boolean create(android.content.Context);
+ method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+ }
+
+ @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+ method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+ method public final androidx.emoji2.text.TypefaceEmojiRasterizer getTypefaceRasterizer();
+ }
+
+ public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+ ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+ method @Deprecated public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+ method public androidx.emoji2.text.FontRequestEmojiCompatConfig setLoadingExecutor(java.util.concurrent.Executor);
+ method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+ }
+
+ public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+ ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+ method public long getRetryDelay();
+ }
+
+ public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+ ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+ method public abstract long getRetryDelay();
+ }
+
+ @AnyThread @RequiresApi(19) public final class MetadataRepo {
+ method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+ method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+ method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+ }
+
+ @AnyThread @RequiresApi(19) public class TypefaceEmojiRasterizer {
+ method public void draw(android.graphics.Canvas, float, float, android.graphics.Paint);
+ method public int getCodepointAt(int);
+ method public int getCodepointsLength();
+ method public int getHeight();
+ method public android.graphics.Typeface getTypeface();
+ method public int getWidth();
+ method public boolean isDefaultEmoji();
+ method public boolean isPreferredSystemRender();
+ }
+
+}
+
diff --git a/emoji2/emoji2/api/res-1.4.0-beta03.txt b/emoji2/emoji2/api/res-1.4.0-beta03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emoji2/emoji2/api/res-1.4.0-beta03.txt
diff --git a/emoji2/emoji2/api/restricted_1.4.0-beta03.txt b/emoji2/emoji2/api/restricted_1.4.0-beta03.txt
new file mode 100644
index 0000000..11d9335
--- /dev/null
+++ b/emoji2/emoji2/api/restricted_1.4.0-beta03.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+ public final class DefaultEmojiCompatConfig {
+ method public static androidx.emoji2.text.FontRequestEmojiCompatConfig? create(android.content.Context);
+ }
+
+ @AnyThread public class EmojiCompat {
+ method public static androidx.emoji2.text.EmojiCompat get();
+ method public String getAssetSignature();
+ method public int getEmojiEnd(CharSequence, @IntRange(from=0) int);
+ method public int getEmojiMatch(CharSequence, @IntRange(from=0) int);
+ method public int getEmojiStart(CharSequence, @IntRange(from=0) int);
+ method public int getLoadState();
+ method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+ method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+ method @Deprecated public boolean hasEmojiGlyph(CharSequence);
+ method @Deprecated public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+ method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+ method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+ method public static boolean isConfigured();
+ method public void load();
+ method @CheckResult public CharSequence? process(CharSequence?);
+ method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+ method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ method public void updateEditorInfo(android.view.inputmethod.EditorInfo);
+ field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+ field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+ field public static final int EMOJI_FALLBACK = 2; // 0x2
+ field public static final int EMOJI_SUPPORTED = 1; // 0x1
+ field public static final int EMOJI_UNSUPPORTED = 0; // 0x0
+ field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+ field public static final int LOAD_STATE_FAILED = 2; // 0x2
+ field public static final int LOAD_STATE_LOADING = 0; // 0x0
+ field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+ field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+ field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+ field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+ field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+ field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+ }
+
+ public abstract static class EmojiCompat.Config {
+ ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+ method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+ method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+ method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+ method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+ method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+ method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+ method public androidx.emoji2.text.EmojiCompat.Config setSpanFactory(androidx.emoji2.text.EmojiCompat.SpanFactory);
+ method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+ method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+ method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+ }
+
+ public static interface EmojiCompat.GlyphChecker {
+ method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+ }
+
+ public abstract static class EmojiCompat.InitCallback {
+ ctor public EmojiCompat.InitCallback();
+ method public void onFailed(Throwable?);
+ method public void onInitialized();
+ }
+
+ public static interface EmojiCompat.MetadataRepoLoader {
+ method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+ }
+
+ public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+ ctor public EmojiCompat.MetadataRepoLoaderCallback();
+ method public abstract void onFailed(Throwable?);
+ method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+ }
+
+ public static interface EmojiCompat.SpanFactory {
+ method @RequiresApi(19) public androidx.emoji2.text.EmojiSpan createSpan(androidx.emoji2.text.TypefaceEmojiRasterizer);
+ }
+
+ public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+ ctor public EmojiCompatInitializer();
+ method public Boolean create(android.content.Context);
+ method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+ }
+
+ @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+ method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+ method public final androidx.emoji2.text.TypefaceEmojiRasterizer getTypefaceRasterizer();
+ }
+
+ public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+ ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+ method @Deprecated public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+ method public androidx.emoji2.text.FontRequestEmojiCompatConfig setLoadingExecutor(java.util.concurrent.Executor);
+ method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+ }
+
+ public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+ ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+ method public long getRetryDelay();
+ }
+
+ public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+ ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+ method public abstract long getRetryDelay();
+ }
+
+ @AnyThread @RequiresApi(19) public final class MetadataRepo {
+ method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+ method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+ method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+ }
+
+ @AnyThread @RequiresApi(19) public class TypefaceEmojiRasterizer {
+ method public void draw(android.graphics.Canvas, float, float, android.graphics.Paint);
+ method public int getCodepointAt(int);
+ method public int getCodepointsLength();
+ method public int getHeight();
+ method public android.graphics.Typeface getTypeface();
+ method public int getWidth();
+ method public boolean isDefaultEmoji();
+ method public boolean isPreferredSystemRender();
+ }
+
+}
+
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java
index 7de9f8f..18907b9 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiCompat.java
@@ -17,7 +17,6 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
import android.app.Application;
import android.content.Context;
@@ -43,6 +42,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.collection.ArraySet;
import androidx.core.util.Preconditions;
@@ -618,7 +618,7 @@
*
* @hide
*/
- @RestrictTo(TESTS)
+ @VisibleForTesting
@Nullable
public static EmojiCompat reset(@Nullable final EmojiCompat emojiCompat) {
synchronized (INSTANCE_LOCK) {
@@ -632,7 +632,7 @@
*
* @hide
*/
- @RestrictTo(TESTS)
+ @VisibleForTesting
public static void skipDefaultConfigurationLookup(boolean shouldSkip) {
synchronized (CONFIG_LOCK) {
sHasDoneDefaultConfigLookup = shouldSkip;
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java
index 7c334c6..5c0af43 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiSpan.java
@@ -16,7 +16,6 @@
package androidx.emoji2.text;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
import android.annotation.SuppressLint;
import android.graphics.Paint;
@@ -26,6 +25,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.core.util.Preconditions;
/**
@@ -125,7 +125,7 @@
*
* @hide
*/
- @RestrictTo(TESTS)
+ @VisibleForTesting
public final int getHeight() {
return mHeight;
}
@@ -143,7 +143,7 @@
*
* @hide
*/
- @RestrictTo(TESTS)
+ @VisibleForTesting
public final int getId() {
return getTypefaceRasterizer().getId();
}
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java
index f08ae06..35e7fb8 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/MetadataRepo.java
@@ -88,7 +88,7 @@
* @hide
*/
@NonNull
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
public static MetadataRepo create(@NonNull final Typeface typeface) {
try {
TraceCompat.beginSection(S_TRACE_CREATE_REPO);
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/TypefaceEmojiRasterizer.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/TypefaceEmojiRasterizer.java
index f120b3c..c505d27 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/TypefaceEmojiRasterizer.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/TypefaceEmojiRasterizer.java
@@ -17,7 +17,6 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static androidx.annotation.RestrictTo.Scope.TESTS;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
@@ -30,6 +29,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.emoji2.text.flatbuffer.MetadataItem;
import androidx.emoji2.text.flatbuffer.MetadataList;
@@ -235,7 +235,7 @@
*
* @hide
*/
- @RestrictTo(TESTS)
+ @VisibleForTesting
public void resetHasGlyphCache() {
boolean willExclude = isPreferredSystemRender();
if (willExclude) {
diff --git a/fragment/fragment-ktx/api/1.6.0-beta02.txt b/fragment/fragment-ktx/api/1.6.0-beta02.txt
new file mode 100644
index 0000000..b93e06b
--- /dev/null
+++ b/fragment/fragment-ktx/api/1.6.0-beta02.txt
@@ -0,0 +1,37 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+ public final class FragmentKt {
+ method public static void clearFragmentResult(androidx.fragment.app.Fragment, String requestKey);
+ method public static void clearFragmentResultListener(androidx.fragment.app.Fragment, String requestKey);
+ method public static void setFragmentResult(androidx.fragment.app.Fragment, String requestKey, android.os.Bundle result);
+ method public static void setFragmentResultListener(androidx.fragment.app.Fragment, String requestKey, kotlin.jvm.functions.Function2<? super java.lang.String,? super android.os.Bundle,kotlin.Unit> listener);
+ }
+
+ public final class FragmentManagerKt {
+ method public static inline void commit(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+ method @MainThread public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+ method @Deprecated public static inline void transaction(androidx.fragment.app.FragmentManager, optional boolean now, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+ }
+
+ public final class FragmentTransactionKt {
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, String tag, optional android.os.Bundle? args);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction replace(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+ }
+
+ public final class FragmentViewModelLazyKt {
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @Deprecated @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ }
+
+ public final class ViewKt {
+ method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+ }
+
+}
+
diff --git a/fragment/fragment-ktx/api/public_plus_experimental_1.6.0-beta02.txt b/fragment/fragment-ktx/api/public_plus_experimental_1.6.0-beta02.txt
new file mode 100644
index 0000000..b93e06b
--- /dev/null
+++ b/fragment/fragment-ktx/api/public_plus_experimental_1.6.0-beta02.txt
@@ -0,0 +1,37 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+ public final class FragmentKt {
+ method public static void clearFragmentResult(androidx.fragment.app.Fragment, String requestKey);
+ method public static void clearFragmentResultListener(androidx.fragment.app.Fragment, String requestKey);
+ method public static void setFragmentResult(androidx.fragment.app.Fragment, String requestKey, android.os.Bundle result);
+ method public static void setFragmentResultListener(androidx.fragment.app.Fragment, String requestKey, kotlin.jvm.functions.Function2<? super java.lang.String,? super android.os.Bundle,kotlin.Unit> listener);
+ }
+
+ public final class FragmentManagerKt {
+ method public static inline void commit(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+ method @MainThread public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+ method @Deprecated public static inline void transaction(androidx.fragment.app.FragmentManager, optional boolean now, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+ }
+
+ public final class FragmentTransactionKt {
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, String tag, optional android.os.Bundle? args);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction replace(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+ }
+
+ public final class FragmentViewModelLazyKt {
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @Deprecated @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ }
+
+ public final class ViewKt {
+ method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+ }
+
+}
+
diff --git a/fragment/fragment-ktx/api/res-1.6.0-beta02.txt b/fragment/fragment-ktx/api/res-1.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment-ktx/api/res-1.6.0-beta02.txt
diff --git a/fragment/fragment-ktx/api/restricted_1.6.0-beta02.txt b/fragment/fragment-ktx/api/restricted_1.6.0-beta02.txt
new file mode 100644
index 0000000..b93e06b
--- /dev/null
+++ b/fragment/fragment-ktx/api/restricted_1.6.0-beta02.txt
@@ -0,0 +1,37 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+ public final class FragmentKt {
+ method public static void clearFragmentResult(androidx.fragment.app.Fragment, String requestKey);
+ method public static void clearFragmentResultListener(androidx.fragment.app.Fragment, String requestKey);
+ method public static void setFragmentResult(androidx.fragment.app.Fragment, String requestKey, android.os.Bundle result);
+ method public static void setFragmentResultListener(androidx.fragment.app.Fragment, String requestKey, kotlin.jvm.functions.Function2<? super java.lang.String,? super android.os.Bundle,kotlin.Unit> listener);
+ }
+
+ public final class FragmentManagerKt {
+ method public static inline void commit(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+ method @MainThread public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+ method @Deprecated public static inline void transaction(androidx.fragment.app.FragmentManager, optional boolean now, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+ }
+
+ public final class FragmentTransactionKt {
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.FragmentTransaction, String tag, optional android.os.Bundle? args);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.FragmentTransaction replace(androidx.fragment.app.FragmentTransaction, @IdRes int containerViewId, optional String? tag, optional android.os.Bundle? args);
+ }
+
+ public final class FragmentViewModelLazyKt {
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> activityViewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras> extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @Deprecated @MainThread public static <VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM> viewModelClass, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore> storeProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> viewModels(androidx.fragment.app.Fragment, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ }
+
+ public final class ViewKt {
+ method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+ }
+
+}
+
diff --git a/fragment/fragment-testing-manifest/api/1.6.0-beta02.txt b/fragment/fragment-testing-manifest/api/1.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/1.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/fragment/fragment-testing-manifest/api/public_plus_experimental_1.6.0-beta02.txt b/fragment/fragment-testing-manifest/api/public_plus_experimental_1.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/public_plus_experimental_1.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/fragment/fragment-testing-manifest/api/res-1.6.0-beta02.txt b/fragment/fragment-testing-manifest/api/res-1.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/res-1.6.0-beta02.txt
diff --git a/fragment/fragment-testing-manifest/api/restricted_1.6.0-beta02.txt b/fragment/fragment-testing-manifest/api/restricted_1.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/fragment/fragment-testing-manifest/api/restricted_1.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/fragment/fragment-testing/api/1.6.0-beta02.txt b/fragment/fragment-testing/api/1.6.0-beta02.txt
new file mode 100644
index 0000000..d5c260e
--- /dev/null
+++ b/fragment/fragment-testing/api/1.6.0-beta02.txt
@@ -0,0 +1,60 @@
+// Signature format: 4.0
+package androidx.fragment.app.testing {
+
+ public final class FragmentScenario<F extends androidx.fragment.app.Fragment> implements java.io.Closeable {
+ method public void close();
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+ method public androidx.fragment.app.testing.FragmentScenario<F> moveToState(androidx.lifecycle.Lifecycle.State newState);
+ method public androidx.fragment.app.testing.FragmentScenario<F> onFragment(androidx.fragment.app.testing.FragmentScenario.FragmentAction<F> action);
+ method public androidx.fragment.app.testing.FragmentScenario<F> recreate();
+ field public static final androidx.fragment.app.testing.FragmentScenario.Companion Companion;
+ }
+
+ public static final class FragmentScenario.Companion {
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+ }
+
+ public static fun interface FragmentScenario.FragmentAction<F extends androidx.fragment.app.Fragment> {
+ method public void perform(F fragment);
+ }
+
+ public final class FragmentScenarioKt {
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method public static inline <reified F extends androidx.fragment.app.Fragment, T> T withFragment(androidx.fragment.app.testing.FragmentScenario<F>, kotlin.jvm.functions.Function1<? super F,? extends T> block);
+ }
+
+}
+
diff --git a/fragment/fragment-testing/api/public_plus_experimental_1.6.0-beta02.txt b/fragment/fragment-testing/api/public_plus_experimental_1.6.0-beta02.txt
new file mode 100644
index 0000000..d5c260e
--- /dev/null
+++ b/fragment/fragment-testing/api/public_plus_experimental_1.6.0-beta02.txt
@@ -0,0 +1,60 @@
+// Signature format: 4.0
+package androidx.fragment.app.testing {
+
+ public final class FragmentScenario<F extends androidx.fragment.app.Fragment> implements java.io.Closeable {
+ method public void close();
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+ method public androidx.fragment.app.testing.FragmentScenario<F> moveToState(androidx.lifecycle.Lifecycle.State newState);
+ method public androidx.fragment.app.testing.FragmentScenario<F> onFragment(androidx.fragment.app.testing.FragmentScenario.FragmentAction<F> action);
+ method public androidx.fragment.app.testing.FragmentScenario<F> recreate();
+ field public static final androidx.fragment.app.testing.FragmentScenario.Companion Companion;
+ }
+
+ public static final class FragmentScenario.Companion {
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+ }
+
+ public static fun interface FragmentScenario.FragmentAction<F extends androidx.fragment.app.Fragment> {
+ method public void perform(F fragment);
+ }
+
+ public final class FragmentScenarioKt {
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method public static inline <reified F extends androidx.fragment.app.Fragment, T> T withFragment(androidx.fragment.app.testing.FragmentScenario<F>, kotlin.jvm.functions.Function1<? super F,? extends T> block);
+ }
+
+}
+
diff --git a/fragment/fragment-testing/api/res-1.6.0-beta02.txt b/fragment/fragment-testing/api/res-1.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment-testing/api/res-1.6.0-beta02.txt
diff --git a/fragment/fragment-testing/api/restricted_1.6.0-beta02.txt b/fragment/fragment-testing/api/restricted_1.6.0-beta02.txt
new file mode 100644
index 0000000..d5c260e
--- /dev/null
+++ b/fragment/fragment-testing/api/restricted_1.6.0-beta02.txt
@@ -0,0 +1,60 @@
+// Signature format: 4.0
+package androidx.fragment.app.testing {
+
+ public final class FragmentScenario<F extends androidx.fragment.app.Fragment> implements java.io.Closeable {
+ method public void close();
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public static <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+ method public androidx.fragment.app.testing.FragmentScenario<F> moveToState(androidx.lifecycle.Lifecycle.State newState);
+ method public androidx.fragment.app.testing.FragmentScenario<F> onFragment(androidx.fragment.app.testing.FragmentScenario.FragmentAction<F> action);
+ method public androidx.fragment.app.testing.FragmentScenario<F> recreate();
+ field public static final androidx.fragment.app.testing.FragmentScenario.Companion Companion;
+ }
+
+ public static final class FragmentScenario.Companion {
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launch(Class<F> fragmentClass);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, android.os.Bundle? fragmentArgs, @StyleRes int themeResId, androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass, optional android.os.Bundle? fragmentArgs);
+ method public <F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchInContainer(Class<F> fragmentClass);
+ }
+
+ public static fun interface FragmentScenario.FragmentAction<F extends androidx.fragment.app.Fragment> {
+ method public void perform(F fragment);
+ }
+
+ public final class FragmentScenarioKt {
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragment(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.fragment.app.FragmentFactory? factory);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, optional androidx.fragment.app.FragmentFactory? factory);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> androidx.fragment.app.testing.FragmentScenario<F> launchFragmentInContainer(optional android.os.Bundle? fragmentArgs, optional @StyleRes int themeResId, optional androidx.lifecycle.Lifecycle.State initialState, kotlin.jvm.functions.Function0<? extends F> instantiate);
+ method public static inline <reified F extends androidx.fragment.app.Fragment, T> T withFragment(androidx.fragment.app.testing.FragmentScenario<F>, kotlin.jvm.functions.Function1<? super F,? extends T> block);
+ }
+
+}
+
diff --git a/fragment/fragment/api/1.6.0-beta02.txt b/fragment/fragment/api/1.6.0-beta02.txt
new file mode 100644
index 0000000..4d44df2
--- /dev/null
+++ b/fragment/fragment/api/1.6.0-beta02.txt
@@ -0,0 +1,554 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+ public class DialogFragment extends androidx.fragment.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+ ctor public DialogFragment();
+ ctor public DialogFragment(@LayoutRes int);
+ method public void dismiss();
+ method public void dismissAllowingStateLoss();
+ method @MainThread public void dismissNow();
+ method public android.app.Dialog? getDialog();
+ method public boolean getShowsDialog();
+ method @StyleRes public int getTheme();
+ method public boolean isCancelable();
+ method public void onCancel(android.content.DialogInterface);
+ method @MainThread public android.app.Dialog onCreateDialog(android.os.Bundle?);
+ method @CallSuper public void onDismiss(android.content.DialogInterface);
+ method public final androidx.activity.ComponentDialog requireComponentDialog();
+ method public final android.app.Dialog requireDialog();
+ method public void setCancelable(boolean);
+ method public void setShowsDialog(boolean);
+ method public void setStyle(int, @StyleRes int);
+ method public void show(androidx.fragment.app.FragmentManager, String?);
+ method public int show(androidx.fragment.app.FragmentTransaction, String?);
+ method public void showNow(androidx.fragment.app.FragmentManager, String?);
+ field public static final int STYLE_NORMAL = 0; // 0x0
+ field public static final int STYLE_NO_FRAME = 2; // 0x2
+ field public static final int STYLE_NO_INPUT = 3; // 0x3
+ field public static final int STYLE_NO_TITLE = 1; // 0x1
+ }
+
+ public class Fragment implements androidx.activity.result.ActivityResultCaller android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+ ctor public Fragment();
+ ctor @ContentView public Fragment(@LayoutRes int);
+ method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method public final boolean equals(Object?);
+ method public final androidx.fragment.app.FragmentActivity? getActivity();
+ method public boolean getAllowEnterTransitionOverlap();
+ method public boolean getAllowReturnTransitionOverlap();
+ method public final android.os.Bundle? getArguments();
+ method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
+ method public android.content.Context? getContext();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ method public Object? getEnterTransition();
+ method public Object? getExitTransition();
+ method @Deprecated public final androidx.fragment.app.FragmentManager? getFragmentManager();
+ method public final Object? getHost();
+ method public final int getId();
+ method public final android.view.LayoutInflater getLayoutInflater();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method @Deprecated public androidx.loader.app.LoaderManager getLoaderManager();
+ method public final androidx.fragment.app.Fragment? getParentFragment();
+ method public final androidx.fragment.app.FragmentManager getParentFragmentManager();
+ method public Object? getReenterTransition();
+ method public final android.content.res.Resources getResources();
+ method @Deprecated public final boolean getRetainInstance();
+ method public Object? getReturnTransition();
+ method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+ method public Object? getSharedElementEnterTransition();
+ method public Object? getSharedElementReturnTransition();
+ method public final String getString(@StringRes int);
+ method public final String getString(@StringRes int, java.lang.Object!...);
+ method public final String? getTag();
+ method @Deprecated public final androidx.fragment.app.Fragment? getTargetFragment();
+ method @Deprecated public final int getTargetRequestCode();
+ method public final CharSequence getText(@StringRes int);
+ method @Deprecated public boolean getUserVisibleHint();
+ method public android.view.View? getView();
+ method @MainThread public androidx.lifecycle.LifecycleOwner getViewLifecycleOwner();
+ method public androidx.lifecycle.LiveData<androidx.lifecycle.LifecycleOwner!> getViewLifecycleOwnerLiveData();
+ method public androidx.lifecycle.ViewModelStore getViewModelStore();
+ method public final int hashCode();
+ method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String);
+ method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+ method public final boolean isAdded();
+ method public final boolean isDetached();
+ method public final boolean isHidden();
+ method public final boolean isInLayout();
+ method public final boolean isRemoving();
+ method public final boolean isResumed();
+ method public final boolean isStateSaved();
+ method public final boolean isVisible();
+ method @Deprecated @CallSuper @MainThread public void onActivityCreated(android.os.Bundle?);
+ method @Deprecated public void onActivityResult(int, int, android.content.Intent?);
+ method @CallSuper @MainThread public void onAttach(android.content.Context);
+ method @Deprecated @CallSuper @MainThread public void onAttach(android.app.Activity);
+ method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+ method @CallSuper public void onConfigurationChanged(android.content.res.Configuration);
+ method @MainThread public boolean onContextItemSelected(android.view.MenuItem);
+ method @CallSuper @MainThread public void onCreate(android.os.Bundle?);
+ method @MainThread public android.view.animation.Animation? onCreateAnimation(int, boolean, int);
+ method @MainThread public android.animation.Animator? onCreateAnimator(int, boolean, int);
+ method @MainThread public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo?);
+ method @Deprecated @MainThread public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+ method @MainThread public android.view.View? onCreateView(android.view.LayoutInflater, android.view.ViewGroup?, android.os.Bundle?);
+ method @CallSuper @MainThread public void onDestroy();
+ method @Deprecated @MainThread public void onDestroyOptionsMenu();
+ method @CallSuper @MainThread public void onDestroyView();
+ method @CallSuper @MainThread public void onDetach();
+ method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle?);
+ method @MainThread public void onHiddenChanged(boolean);
+ method @CallSuper @UiThread public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle?);
+ method @Deprecated @CallSuper @UiThread public void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle?);
+ method @CallSuper @MainThread public void onLowMemory();
+ method public void onMultiWindowModeChanged(boolean);
+ method @Deprecated @MainThread public boolean onOptionsItemSelected(android.view.MenuItem);
+ method @Deprecated @MainThread public void onOptionsMenuClosed(android.view.Menu);
+ method @CallSuper @MainThread public void onPause();
+ method public void onPictureInPictureModeChanged(boolean);
+ method @Deprecated @MainThread public void onPrepareOptionsMenu(android.view.Menu);
+ method @MainThread public void onPrimaryNavigationFragmentChanged(boolean);
+ method @Deprecated public void onRequestPermissionsResult(int, String![], int[]);
+ method @CallSuper @MainThread public void onResume();
+ method @MainThread public void onSaveInstanceState(android.os.Bundle);
+ method @CallSuper @MainThread public void onStart();
+ method @CallSuper @MainThread public void onStop();
+ method @MainThread public void onViewCreated(android.view.View, android.os.Bundle?);
+ method @CallSuper @MainThread public void onViewStateRestored(android.os.Bundle?);
+ method public void postponeEnterTransition();
+ method public final void postponeEnterTransition(long, java.util.concurrent.TimeUnit);
+ method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultCallback<O!>);
+ method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultRegistry, androidx.activity.result.ActivityResultCallback<O!>);
+ method public void registerForContextMenu(android.view.View);
+ method @Deprecated public final void requestPermissions(String![], int);
+ method public final androidx.fragment.app.FragmentActivity requireActivity();
+ method public final android.os.Bundle requireArguments();
+ method public final android.content.Context requireContext();
+ method @Deprecated public final androidx.fragment.app.FragmentManager requireFragmentManager();
+ method public final Object requireHost();
+ method public final androidx.fragment.app.Fragment requireParentFragment();
+ method public final android.view.View requireView();
+ method public void setAllowEnterTransitionOverlap(boolean);
+ method public void setAllowReturnTransitionOverlap(boolean);
+ method public void setArguments(android.os.Bundle?);
+ method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void setEnterTransition(Object?);
+ method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void setExitTransition(Object?);
+ method @Deprecated public void setHasOptionsMenu(boolean);
+ method public void setInitialSavedState(androidx.fragment.app.Fragment.SavedState?);
+ method public void setMenuVisibility(boolean);
+ method public void setReenterTransition(Object?);
+ method @Deprecated public void setRetainInstance(boolean);
+ method public void setReturnTransition(Object?);
+ method public void setSharedElementEnterTransition(Object?);
+ method public void setSharedElementReturnTransition(Object?);
+ method @Deprecated public void setTargetFragment(androidx.fragment.app.Fragment?, int);
+ method @Deprecated public void setUserVisibleHint(boolean);
+ method public boolean shouldShowRequestPermissionRationale(String);
+ method public void startActivity(android.content.Intent);
+ method public void startActivity(android.content.Intent, android.os.Bundle?);
+ method @Deprecated public void startActivityForResult(android.content.Intent, int);
+ method @Deprecated public void startActivityForResult(android.content.Intent, int, android.os.Bundle?);
+ method @Deprecated public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public void startPostponedEnterTransition();
+ method public void unregisterForContextMenu(android.view.View);
+ }
+
+ public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+ ctor public Fragment.InstantiationException(String, Exception?);
+ }
+
+ public static class Fragment.SavedState implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<androidx.fragment.app.Fragment.SavedState!> CREATOR;
+ }
+
+ public class FragmentActivity extends androidx.activity.ComponentActivity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback androidx.lifecycle.LifecycleOwner {
+ ctor public FragmentActivity();
+ ctor @ContentView public FragmentActivity(@LayoutRes int);
+ method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+ method @Deprecated public androidx.loader.app.LoaderManager getSupportLoaderManager();
+ method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+ method protected void onResumeFragments();
+ method public void onStateNotSaved();
+ method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+ method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+ method @Deprecated public void startIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public void supportFinishAfterTransition();
+ method @Deprecated public void supportInvalidateOptionsMenu();
+ method public void supportPostponeEnterTransition();
+ method public void supportStartPostponedEnterTransition();
+ method @Deprecated public final void validateRequestPermissionsRequestCode(int);
+ }
+
+ public abstract class FragmentContainer {
+ ctor public FragmentContainer();
+ method @Deprecated public androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+ method public abstract android.view.View? onFindViewById(@IdRes int);
+ method public abstract boolean onHasView();
+ }
+
+ public final class FragmentContainerView extends android.widget.FrameLayout {
+ ctor public FragmentContainerView(android.content.Context context);
+ ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs, optional int defStyleAttr);
+ ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs);
+ method public <F extends androidx.fragment.app.Fragment> F getFragment();
+ }
+
+ public class FragmentController {
+ method public void attachHost(androidx.fragment.app.Fragment?);
+ method public static androidx.fragment.app.FragmentController createController(androidx.fragment.app.FragmentHostCallback<?>);
+ method public void dispatchActivityCreated();
+ method @Deprecated public void dispatchConfigurationChanged(android.content.res.Configuration);
+ method public boolean dispatchContextItemSelected(android.view.MenuItem);
+ method public void dispatchCreate();
+ method @Deprecated public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+ method public void dispatchDestroy();
+ method public void dispatchDestroyView();
+ method @Deprecated public void dispatchLowMemory();
+ method @Deprecated public void dispatchMultiWindowModeChanged(boolean);
+ method @Deprecated public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+ method @Deprecated public void dispatchOptionsMenuClosed(android.view.Menu);
+ method public void dispatchPause();
+ method @Deprecated public void dispatchPictureInPictureModeChanged(boolean);
+ method @Deprecated public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+ method @Deprecated public void dispatchReallyStop();
+ method public void dispatchResume();
+ method public void dispatchStart();
+ method public void dispatchStop();
+ method @Deprecated public void doLoaderDestroy();
+ method @Deprecated public void doLoaderRetain();
+ method @Deprecated public void doLoaderStart();
+ method @Deprecated public void doLoaderStop(boolean);
+ method @Deprecated public void dumpLoaders(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method public boolean execPendingActions();
+ method public androidx.fragment.app.Fragment? findFragmentByWho(String);
+ method public java.util.List<androidx.fragment.app.Fragment!> getActiveFragments(java.util.List<androidx.fragment.app.Fragment!>!);
+ method public int getActiveFragmentsCount();
+ method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+ method @Deprecated public androidx.loader.app.LoaderManager! getSupportLoaderManager();
+ method public void noteStateNotSaved();
+ method public android.view.View? onCreateView(android.view.View?, String, android.content.Context, android.util.AttributeSet);
+ method @Deprecated public void reportLoaderStart();
+ method @Deprecated public void restoreAllState(android.os.Parcelable?, java.util.List<androidx.fragment.app.Fragment!>?);
+ method @Deprecated public void restoreAllState(android.os.Parcelable?, androidx.fragment.app.FragmentManagerNonConfig?);
+ method @Deprecated public void restoreLoaderNonConfig(androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>!);
+ method @Deprecated public void restoreSaveState(android.os.Parcelable?);
+ method @Deprecated public androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>? retainLoaderNonConfig();
+ method @Deprecated public androidx.fragment.app.FragmentManagerNonConfig? retainNestedNonConfig();
+ method @Deprecated public java.util.List<androidx.fragment.app.Fragment!>? retainNonConfig();
+ method @Deprecated public android.os.Parcelable? saveAllState();
+ }
+
+ public class FragmentFactory {
+ ctor public FragmentFactory();
+ method public androidx.fragment.app.Fragment instantiate(ClassLoader, String);
+ method public static Class<? extends androidx.fragment.app.Fragment> loadFragmentClass(ClassLoader, String);
+ }
+
+ public abstract class FragmentHostCallback<E> extends androidx.fragment.app.FragmentContainer {
+ ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+ method public void onDump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method public android.view.View? onFindViewById(int);
+ method public abstract E? onGetHost();
+ method public android.view.LayoutInflater onGetLayoutInflater();
+ method public int onGetWindowAnimations();
+ method public boolean onHasView();
+ method public boolean onHasWindowAnimations();
+ method @Deprecated public void onRequestPermissionsFromFragment(androidx.fragment.app.Fragment, String![], int);
+ method public boolean onShouldSaveFragmentState(androidx.fragment.app.Fragment);
+ method public boolean onShouldShowRequestPermissionRationale(String);
+ method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+ method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+ method @Deprecated public void onStartIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public void onSupportInvalidateOptionsMenu();
+ }
+
+ public abstract class FragmentManager implements androidx.fragment.app.FragmentResultOwner {
+ ctor public FragmentManager();
+ method public void addFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+ method public void addOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+ method public androidx.fragment.app.FragmentTransaction beginTransaction();
+ method public void clearBackStack(String);
+ method public final void clearFragmentResult(String);
+ method public final void clearFragmentResultListener(String);
+ method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method @Deprecated public static void enableDebugLogging(boolean);
+ method @MainThread public boolean executePendingTransactions();
+ method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+ method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
+ method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
+ method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+ method public int getBackStackEntryCount();
+ method public androidx.fragment.app.Fragment? getFragment(android.os.Bundle, String);
+ method public androidx.fragment.app.FragmentFactory getFragmentFactory();
+ method public java.util.List<androidx.fragment.app.Fragment!> getFragments();
+ method public androidx.fragment.app.Fragment? getPrimaryNavigationFragment();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy? getStrictModePolicy();
+ method public boolean isDestroyed();
+ method public boolean isStateSaved();
+ method public void popBackStack();
+ method public void popBackStack(String?, int);
+ method public void popBackStack(int, int);
+ method @MainThread public boolean popBackStackImmediate();
+ method @MainThread public boolean popBackStackImmediate(String?, int);
+ method public boolean popBackStackImmediate(int, int);
+ method public void putFragment(android.os.Bundle, String, androidx.fragment.app.Fragment);
+ method public void registerFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+ method public void removeFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+ method public void removeOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+ method public void restoreBackStack(String);
+ method public void saveBackStack(String);
+ method public androidx.fragment.app.Fragment.SavedState? saveFragmentInstanceState(androidx.fragment.app.Fragment);
+ method public void setFragmentFactory(androidx.fragment.app.FragmentFactory);
+ method public final void setFragmentResult(String, android.os.Bundle);
+ method public final void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+ method public void setStrictModePolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy?);
+ method public void unregisterFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks);
+ field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+ }
+
+ public static interface FragmentManager.BackStackEntry {
+ method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+ method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+ method @Deprecated public CharSequence? getBreadCrumbTitle();
+ method @Deprecated @StringRes public int getBreadCrumbTitleRes();
+ method public int getId();
+ method public String? getName();
+ }
+
+ public abstract static class FragmentManager.FragmentLifecycleCallbacks {
+ ctor public FragmentManager.FragmentLifecycleCallbacks();
+ method @Deprecated public void onFragmentActivityCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+ method public void onFragmentAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+ method public void onFragmentCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+ method public void onFragmentDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentDetached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentPaused(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentPreAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+ method public void onFragmentPreCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+ method public void onFragmentResumed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentSaveInstanceState(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle);
+ method public void onFragmentStarted(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentStopped(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentViewCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.view.View, android.os.Bundle?);
+ method public void onFragmentViewDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ }
+
+ public static interface FragmentManager.OnBackStackChangedListener {
+ method @MainThread public default void onBackStackChangeCommitted(androidx.fragment.app.Fragment, boolean);
+ method @MainThread public default void onBackStackChangeStarted(androidx.fragment.app.Fragment, boolean);
+ method @MainThread public void onBackStackChanged();
+ }
+
+ @Deprecated public class FragmentManagerNonConfig {
+ }
+
+ public interface FragmentOnAttachListener {
+ method @MainThread public void onAttachFragment(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ }
+
+ @Deprecated public abstract class FragmentPagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+ ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager);
+ ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager, int);
+ method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+ method @Deprecated public long getItemId(int);
+ method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+ field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+ }
+
+ public interface FragmentResultListener {
+ method public void onFragmentResult(String, android.os.Bundle);
+ }
+
+ public interface FragmentResultOwner {
+ method public void clearFragmentResult(String);
+ method public void clearFragmentResultListener(String);
+ method public void setFragmentResult(String, android.os.Bundle);
+ method public void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+ }
+
+ @Deprecated public abstract class FragmentStatePagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+ ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager);
+ ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager, int);
+ method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+ method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+ field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+ }
+
+ @Deprecated public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+ ctor @Deprecated public FragmentTabHost(android.content.Context);
+ ctor @Deprecated public FragmentTabHost(android.content.Context, android.util.AttributeSet?);
+ method @Deprecated public void addTab(android.widget.TabHost.TabSpec, Class<?>, android.os.Bundle?);
+ method @Deprecated public void onTabChanged(String?);
+ method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager);
+ method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager, int);
+ }
+
+ public abstract class FragmentTransaction {
+ ctor @Deprecated public FragmentTransaction();
+ method public final androidx.fragment.app.FragmentTransaction add(Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+ method public androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.Fragment, String?);
+ method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+ method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment);
+ method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+ method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment, String?);
+ method public androidx.fragment.app.FragmentTransaction addSharedElement(android.view.View, String);
+ method public androidx.fragment.app.FragmentTransaction addToBackStack(String?);
+ method public androidx.fragment.app.FragmentTransaction attach(androidx.fragment.app.Fragment);
+ method public abstract int commit();
+ method public abstract int commitAllowingStateLoss();
+ method @MainThread public abstract void commitNow();
+ method @MainThread public abstract void commitNowAllowingStateLoss();
+ method public androidx.fragment.app.FragmentTransaction detach(androidx.fragment.app.Fragment);
+ method public androidx.fragment.app.FragmentTransaction disallowAddToBackStack();
+ method public androidx.fragment.app.FragmentTransaction hide(androidx.fragment.app.Fragment);
+ method public boolean isAddToBackStackAllowed();
+ method public boolean isEmpty();
+ method public androidx.fragment.app.FragmentTransaction remove(androidx.fragment.app.Fragment);
+ method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+ method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment);
+ method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+ method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
+ method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+ method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+ method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+ method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
+ method public androidx.fragment.app.FragmentTransaction setPrimaryNavigationFragment(androidx.fragment.app.Fragment?);
+ method public androidx.fragment.app.FragmentTransaction setReorderingAllowed(boolean);
+ method public androidx.fragment.app.FragmentTransaction setTransition(int);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setTransitionStyle(@StyleRes int);
+ method public androidx.fragment.app.FragmentTransaction show(androidx.fragment.app.Fragment);
+ field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+ field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+ field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+ field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+ field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_CLOSE = 8197; // 0x2005
+ field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN = 4100; // 0x1004
+ field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+ field public static final int TRANSIT_NONE = 0; // 0x0
+ field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+ }
+
+ public class ListFragment extends androidx.fragment.app.Fragment {
+ ctor public ListFragment();
+ method public android.widget.ListAdapter? getListAdapter();
+ method public android.widget.ListView getListView();
+ method public long getSelectedItemId();
+ method public int getSelectedItemPosition();
+ method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+ method public final android.widget.ListAdapter requireListAdapter();
+ method public void setEmptyText(CharSequence?);
+ method public void setListAdapter(android.widget.ListAdapter?);
+ method public void setListShown(boolean);
+ method public void setListShownNoAnimation(boolean);
+ method public void setSelection(int);
+ }
+
+}
+
+package androidx.fragment.app.strictmode {
+
+ public final class FragmentReuseViolation extends androidx.fragment.app.strictmode.Violation {
+ method public String getPreviousFragmentId();
+ property public final String previousFragmentId;
+ }
+
+ public final class FragmentStrictMode {
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
+ method @VisibleForTesting public void onPolicyViolation(androidx.fragment.app.strictmode.Violation violation);
+ method public void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
+ property public final androidx.fragment.app.strictmode.FragmentStrictMode.Policy defaultPolicy;
+ field public static final androidx.fragment.app.strictmode.FragmentStrictMode INSTANCE;
+ }
+
+ public static fun interface FragmentStrictMode.OnViolationListener {
+ method public void onViolation(androidx.fragment.app.strictmode.Violation violation);
+ }
+
+ public static final class FragmentStrictMode.Policy {
+ field public static final androidx.fragment.app.strictmode.FragmentStrictMode.Policy LAX;
+ }
+
+ public static final class FragmentStrictMode.Policy.Builder {
+ ctor public FragmentStrictMode.Policy.Builder();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(Class<? extends androidx.fragment.app.Fragment> fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(String fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentReuse();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectRetainInstanceUsage();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectSetUserVisibleHint();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectTargetFragmentUsage();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongFragmentContainer();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongNestedHierarchy();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyDeath();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyListener(androidx.fragment.app.strictmode.FragmentStrictMode.OnViolationListener listener);
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyLog();
+ }
+
+ public final class FragmentTagUsageViolation extends androidx.fragment.app.strictmode.Violation {
+ method public android.view.ViewGroup? getParentContainer();
+ property public final android.view.ViewGroup? parentContainer;
+ }
+
+ public final class GetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+ }
+
+ public final class GetTargetFragmentRequestCodeUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+ }
+
+ public final class GetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+ }
+
+ public abstract class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+ }
+
+ public final class SetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+ }
+
+ public final class SetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+ method public int getRequestCode();
+ method public androidx.fragment.app.Fragment getTargetFragment();
+ property public final int requestCode;
+ property public final androidx.fragment.app.Fragment targetFragment;
+ }
+
+ public final class SetUserVisibleHintViolation extends androidx.fragment.app.strictmode.Violation {
+ method public boolean isVisibleToUser();
+ property public final boolean isVisibleToUser;
+ }
+
+ public abstract class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
+ }
+
+ public abstract class Violation extends java.lang.RuntimeException {
+ method public final androidx.fragment.app.Fragment getFragment();
+ property public final androidx.fragment.app.Fragment fragment;
+ }
+
+ public final class WrongFragmentContainerViolation extends androidx.fragment.app.strictmode.Violation {
+ method public android.view.ViewGroup getContainer();
+ property public final android.view.ViewGroup container;
+ }
+
+ public final class WrongNestedHierarchyViolation extends androidx.fragment.app.strictmode.Violation {
+ method public int getContainerId();
+ method public androidx.fragment.app.Fragment getExpectedParentFragment();
+ property public final int containerId;
+ property public final androidx.fragment.app.Fragment expectedParentFragment;
+ }
+
+}
+
diff --git a/fragment/fragment/api/public_plus_experimental_1.6.0-beta02.txt b/fragment/fragment/api/public_plus_experimental_1.6.0-beta02.txt
new file mode 100644
index 0000000..4d44df2
--- /dev/null
+++ b/fragment/fragment/api/public_plus_experimental_1.6.0-beta02.txt
@@ -0,0 +1,554 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+ public class DialogFragment extends androidx.fragment.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+ ctor public DialogFragment();
+ ctor public DialogFragment(@LayoutRes int);
+ method public void dismiss();
+ method public void dismissAllowingStateLoss();
+ method @MainThread public void dismissNow();
+ method public android.app.Dialog? getDialog();
+ method public boolean getShowsDialog();
+ method @StyleRes public int getTheme();
+ method public boolean isCancelable();
+ method public void onCancel(android.content.DialogInterface);
+ method @MainThread public android.app.Dialog onCreateDialog(android.os.Bundle?);
+ method @CallSuper public void onDismiss(android.content.DialogInterface);
+ method public final androidx.activity.ComponentDialog requireComponentDialog();
+ method public final android.app.Dialog requireDialog();
+ method public void setCancelable(boolean);
+ method public void setShowsDialog(boolean);
+ method public void setStyle(int, @StyleRes int);
+ method public void show(androidx.fragment.app.FragmentManager, String?);
+ method public int show(androidx.fragment.app.FragmentTransaction, String?);
+ method public void showNow(androidx.fragment.app.FragmentManager, String?);
+ field public static final int STYLE_NORMAL = 0; // 0x0
+ field public static final int STYLE_NO_FRAME = 2; // 0x2
+ field public static final int STYLE_NO_INPUT = 3; // 0x3
+ field public static final int STYLE_NO_TITLE = 1; // 0x1
+ }
+
+ public class Fragment implements androidx.activity.result.ActivityResultCaller android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+ ctor public Fragment();
+ ctor @ContentView public Fragment(@LayoutRes int);
+ method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method public final boolean equals(Object?);
+ method public final androidx.fragment.app.FragmentActivity? getActivity();
+ method public boolean getAllowEnterTransitionOverlap();
+ method public boolean getAllowReturnTransitionOverlap();
+ method public final android.os.Bundle? getArguments();
+ method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
+ method public android.content.Context? getContext();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ method public Object? getEnterTransition();
+ method public Object? getExitTransition();
+ method @Deprecated public final androidx.fragment.app.FragmentManager? getFragmentManager();
+ method public final Object? getHost();
+ method public final int getId();
+ method public final android.view.LayoutInflater getLayoutInflater();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method @Deprecated public androidx.loader.app.LoaderManager getLoaderManager();
+ method public final androidx.fragment.app.Fragment? getParentFragment();
+ method public final androidx.fragment.app.FragmentManager getParentFragmentManager();
+ method public Object? getReenterTransition();
+ method public final android.content.res.Resources getResources();
+ method @Deprecated public final boolean getRetainInstance();
+ method public Object? getReturnTransition();
+ method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+ method public Object? getSharedElementEnterTransition();
+ method public Object? getSharedElementReturnTransition();
+ method public final String getString(@StringRes int);
+ method public final String getString(@StringRes int, java.lang.Object!...);
+ method public final String? getTag();
+ method @Deprecated public final androidx.fragment.app.Fragment? getTargetFragment();
+ method @Deprecated public final int getTargetRequestCode();
+ method public final CharSequence getText(@StringRes int);
+ method @Deprecated public boolean getUserVisibleHint();
+ method public android.view.View? getView();
+ method @MainThread public androidx.lifecycle.LifecycleOwner getViewLifecycleOwner();
+ method public androidx.lifecycle.LiveData<androidx.lifecycle.LifecycleOwner!> getViewLifecycleOwnerLiveData();
+ method public androidx.lifecycle.ViewModelStore getViewModelStore();
+ method public final int hashCode();
+ method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String);
+ method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+ method public final boolean isAdded();
+ method public final boolean isDetached();
+ method public final boolean isHidden();
+ method public final boolean isInLayout();
+ method public final boolean isRemoving();
+ method public final boolean isResumed();
+ method public final boolean isStateSaved();
+ method public final boolean isVisible();
+ method @Deprecated @CallSuper @MainThread public void onActivityCreated(android.os.Bundle?);
+ method @Deprecated public void onActivityResult(int, int, android.content.Intent?);
+ method @CallSuper @MainThread public void onAttach(android.content.Context);
+ method @Deprecated @CallSuper @MainThread public void onAttach(android.app.Activity);
+ method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+ method @CallSuper public void onConfigurationChanged(android.content.res.Configuration);
+ method @MainThread public boolean onContextItemSelected(android.view.MenuItem);
+ method @CallSuper @MainThread public void onCreate(android.os.Bundle?);
+ method @MainThread public android.view.animation.Animation? onCreateAnimation(int, boolean, int);
+ method @MainThread public android.animation.Animator? onCreateAnimator(int, boolean, int);
+ method @MainThread public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo?);
+ method @Deprecated @MainThread public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+ method @MainThread public android.view.View? onCreateView(android.view.LayoutInflater, android.view.ViewGroup?, android.os.Bundle?);
+ method @CallSuper @MainThread public void onDestroy();
+ method @Deprecated @MainThread public void onDestroyOptionsMenu();
+ method @CallSuper @MainThread public void onDestroyView();
+ method @CallSuper @MainThread public void onDetach();
+ method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle?);
+ method @MainThread public void onHiddenChanged(boolean);
+ method @CallSuper @UiThread public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle?);
+ method @Deprecated @CallSuper @UiThread public void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle?);
+ method @CallSuper @MainThread public void onLowMemory();
+ method public void onMultiWindowModeChanged(boolean);
+ method @Deprecated @MainThread public boolean onOptionsItemSelected(android.view.MenuItem);
+ method @Deprecated @MainThread public void onOptionsMenuClosed(android.view.Menu);
+ method @CallSuper @MainThread public void onPause();
+ method public void onPictureInPictureModeChanged(boolean);
+ method @Deprecated @MainThread public void onPrepareOptionsMenu(android.view.Menu);
+ method @MainThread public void onPrimaryNavigationFragmentChanged(boolean);
+ method @Deprecated public void onRequestPermissionsResult(int, String![], int[]);
+ method @CallSuper @MainThread public void onResume();
+ method @MainThread public void onSaveInstanceState(android.os.Bundle);
+ method @CallSuper @MainThread public void onStart();
+ method @CallSuper @MainThread public void onStop();
+ method @MainThread public void onViewCreated(android.view.View, android.os.Bundle?);
+ method @CallSuper @MainThread public void onViewStateRestored(android.os.Bundle?);
+ method public void postponeEnterTransition();
+ method public final void postponeEnterTransition(long, java.util.concurrent.TimeUnit);
+ method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultCallback<O!>);
+ method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultRegistry, androidx.activity.result.ActivityResultCallback<O!>);
+ method public void registerForContextMenu(android.view.View);
+ method @Deprecated public final void requestPermissions(String![], int);
+ method public final androidx.fragment.app.FragmentActivity requireActivity();
+ method public final android.os.Bundle requireArguments();
+ method public final android.content.Context requireContext();
+ method @Deprecated public final androidx.fragment.app.FragmentManager requireFragmentManager();
+ method public final Object requireHost();
+ method public final androidx.fragment.app.Fragment requireParentFragment();
+ method public final android.view.View requireView();
+ method public void setAllowEnterTransitionOverlap(boolean);
+ method public void setAllowReturnTransitionOverlap(boolean);
+ method public void setArguments(android.os.Bundle?);
+ method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void setEnterTransition(Object?);
+ method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void setExitTransition(Object?);
+ method @Deprecated public void setHasOptionsMenu(boolean);
+ method public void setInitialSavedState(androidx.fragment.app.Fragment.SavedState?);
+ method public void setMenuVisibility(boolean);
+ method public void setReenterTransition(Object?);
+ method @Deprecated public void setRetainInstance(boolean);
+ method public void setReturnTransition(Object?);
+ method public void setSharedElementEnterTransition(Object?);
+ method public void setSharedElementReturnTransition(Object?);
+ method @Deprecated public void setTargetFragment(androidx.fragment.app.Fragment?, int);
+ method @Deprecated public void setUserVisibleHint(boolean);
+ method public boolean shouldShowRequestPermissionRationale(String);
+ method public void startActivity(android.content.Intent);
+ method public void startActivity(android.content.Intent, android.os.Bundle?);
+ method @Deprecated public void startActivityForResult(android.content.Intent, int);
+ method @Deprecated public void startActivityForResult(android.content.Intent, int, android.os.Bundle?);
+ method @Deprecated public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public void startPostponedEnterTransition();
+ method public void unregisterForContextMenu(android.view.View);
+ }
+
+ public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+ ctor public Fragment.InstantiationException(String, Exception?);
+ }
+
+ public static class Fragment.SavedState implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<androidx.fragment.app.Fragment.SavedState!> CREATOR;
+ }
+
+ public class FragmentActivity extends androidx.activity.ComponentActivity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback androidx.lifecycle.LifecycleOwner {
+ ctor public FragmentActivity();
+ ctor @ContentView public FragmentActivity(@LayoutRes int);
+ method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+ method @Deprecated public androidx.loader.app.LoaderManager getSupportLoaderManager();
+ method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+ method protected void onResumeFragments();
+ method public void onStateNotSaved();
+ method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+ method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+ method @Deprecated public void startIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public void supportFinishAfterTransition();
+ method @Deprecated public void supportInvalidateOptionsMenu();
+ method public void supportPostponeEnterTransition();
+ method public void supportStartPostponedEnterTransition();
+ method @Deprecated public final void validateRequestPermissionsRequestCode(int);
+ }
+
+ public abstract class FragmentContainer {
+ ctor public FragmentContainer();
+ method @Deprecated public androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+ method public abstract android.view.View? onFindViewById(@IdRes int);
+ method public abstract boolean onHasView();
+ }
+
+ public final class FragmentContainerView extends android.widget.FrameLayout {
+ ctor public FragmentContainerView(android.content.Context context);
+ ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs, optional int defStyleAttr);
+ ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs);
+ method public <F extends androidx.fragment.app.Fragment> F getFragment();
+ }
+
+ public class FragmentController {
+ method public void attachHost(androidx.fragment.app.Fragment?);
+ method public static androidx.fragment.app.FragmentController createController(androidx.fragment.app.FragmentHostCallback<?>);
+ method public void dispatchActivityCreated();
+ method @Deprecated public void dispatchConfigurationChanged(android.content.res.Configuration);
+ method public boolean dispatchContextItemSelected(android.view.MenuItem);
+ method public void dispatchCreate();
+ method @Deprecated public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+ method public void dispatchDestroy();
+ method public void dispatchDestroyView();
+ method @Deprecated public void dispatchLowMemory();
+ method @Deprecated public void dispatchMultiWindowModeChanged(boolean);
+ method @Deprecated public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+ method @Deprecated public void dispatchOptionsMenuClosed(android.view.Menu);
+ method public void dispatchPause();
+ method @Deprecated public void dispatchPictureInPictureModeChanged(boolean);
+ method @Deprecated public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+ method @Deprecated public void dispatchReallyStop();
+ method public void dispatchResume();
+ method public void dispatchStart();
+ method public void dispatchStop();
+ method @Deprecated public void doLoaderDestroy();
+ method @Deprecated public void doLoaderRetain();
+ method @Deprecated public void doLoaderStart();
+ method @Deprecated public void doLoaderStop(boolean);
+ method @Deprecated public void dumpLoaders(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method public boolean execPendingActions();
+ method public androidx.fragment.app.Fragment? findFragmentByWho(String);
+ method public java.util.List<androidx.fragment.app.Fragment!> getActiveFragments(java.util.List<androidx.fragment.app.Fragment!>!);
+ method public int getActiveFragmentsCount();
+ method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+ method @Deprecated public androidx.loader.app.LoaderManager! getSupportLoaderManager();
+ method public void noteStateNotSaved();
+ method public android.view.View? onCreateView(android.view.View?, String, android.content.Context, android.util.AttributeSet);
+ method @Deprecated public void reportLoaderStart();
+ method @Deprecated public void restoreAllState(android.os.Parcelable?, java.util.List<androidx.fragment.app.Fragment!>?);
+ method @Deprecated public void restoreAllState(android.os.Parcelable?, androidx.fragment.app.FragmentManagerNonConfig?);
+ method @Deprecated public void restoreLoaderNonConfig(androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>!);
+ method @Deprecated public void restoreSaveState(android.os.Parcelable?);
+ method @Deprecated public androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>? retainLoaderNonConfig();
+ method @Deprecated public androidx.fragment.app.FragmentManagerNonConfig? retainNestedNonConfig();
+ method @Deprecated public java.util.List<androidx.fragment.app.Fragment!>? retainNonConfig();
+ method @Deprecated public android.os.Parcelable? saveAllState();
+ }
+
+ public class FragmentFactory {
+ ctor public FragmentFactory();
+ method public androidx.fragment.app.Fragment instantiate(ClassLoader, String);
+ method public static Class<? extends androidx.fragment.app.Fragment> loadFragmentClass(ClassLoader, String);
+ }
+
+ public abstract class FragmentHostCallback<E> extends androidx.fragment.app.FragmentContainer {
+ ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+ method public void onDump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method public android.view.View? onFindViewById(int);
+ method public abstract E? onGetHost();
+ method public android.view.LayoutInflater onGetLayoutInflater();
+ method public int onGetWindowAnimations();
+ method public boolean onHasView();
+ method public boolean onHasWindowAnimations();
+ method @Deprecated public void onRequestPermissionsFromFragment(androidx.fragment.app.Fragment, String![], int);
+ method public boolean onShouldSaveFragmentState(androidx.fragment.app.Fragment);
+ method public boolean onShouldShowRequestPermissionRationale(String);
+ method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+ method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+ method @Deprecated public void onStartIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public void onSupportInvalidateOptionsMenu();
+ }
+
+ public abstract class FragmentManager implements androidx.fragment.app.FragmentResultOwner {
+ ctor public FragmentManager();
+ method public void addFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+ method public void addOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+ method public androidx.fragment.app.FragmentTransaction beginTransaction();
+ method public void clearBackStack(String);
+ method public final void clearFragmentResult(String);
+ method public final void clearFragmentResultListener(String);
+ method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method @Deprecated public static void enableDebugLogging(boolean);
+ method @MainThread public boolean executePendingTransactions();
+ method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+ method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
+ method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
+ method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+ method public int getBackStackEntryCount();
+ method public androidx.fragment.app.Fragment? getFragment(android.os.Bundle, String);
+ method public androidx.fragment.app.FragmentFactory getFragmentFactory();
+ method public java.util.List<androidx.fragment.app.Fragment!> getFragments();
+ method public androidx.fragment.app.Fragment? getPrimaryNavigationFragment();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy? getStrictModePolicy();
+ method public boolean isDestroyed();
+ method public boolean isStateSaved();
+ method public void popBackStack();
+ method public void popBackStack(String?, int);
+ method public void popBackStack(int, int);
+ method @MainThread public boolean popBackStackImmediate();
+ method @MainThread public boolean popBackStackImmediate(String?, int);
+ method public boolean popBackStackImmediate(int, int);
+ method public void putFragment(android.os.Bundle, String, androidx.fragment.app.Fragment);
+ method public void registerFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+ method public void removeFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+ method public void removeOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+ method public void restoreBackStack(String);
+ method public void saveBackStack(String);
+ method public androidx.fragment.app.Fragment.SavedState? saveFragmentInstanceState(androidx.fragment.app.Fragment);
+ method public void setFragmentFactory(androidx.fragment.app.FragmentFactory);
+ method public final void setFragmentResult(String, android.os.Bundle);
+ method public final void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+ method public void setStrictModePolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy?);
+ method public void unregisterFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks);
+ field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+ }
+
+ public static interface FragmentManager.BackStackEntry {
+ method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+ method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+ method @Deprecated public CharSequence? getBreadCrumbTitle();
+ method @Deprecated @StringRes public int getBreadCrumbTitleRes();
+ method public int getId();
+ method public String? getName();
+ }
+
+ public abstract static class FragmentManager.FragmentLifecycleCallbacks {
+ ctor public FragmentManager.FragmentLifecycleCallbacks();
+ method @Deprecated public void onFragmentActivityCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+ method public void onFragmentAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+ method public void onFragmentCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+ method public void onFragmentDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentDetached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentPaused(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentPreAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+ method public void onFragmentPreCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+ method public void onFragmentResumed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentSaveInstanceState(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle);
+ method public void onFragmentStarted(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentStopped(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentViewCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.view.View, android.os.Bundle?);
+ method public void onFragmentViewDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ }
+
+ public static interface FragmentManager.OnBackStackChangedListener {
+ method @MainThread public default void onBackStackChangeCommitted(androidx.fragment.app.Fragment, boolean);
+ method @MainThread public default void onBackStackChangeStarted(androidx.fragment.app.Fragment, boolean);
+ method @MainThread public void onBackStackChanged();
+ }
+
+ @Deprecated public class FragmentManagerNonConfig {
+ }
+
+ public interface FragmentOnAttachListener {
+ method @MainThread public void onAttachFragment(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ }
+
+ @Deprecated public abstract class FragmentPagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+ ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager);
+ ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager, int);
+ method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+ method @Deprecated public long getItemId(int);
+ method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+ field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+ }
+
+ public interface FragmentResultListener {
+ method public void onFragmentResult(String, android.os.Bundle);
+ }
+
+ public interface FragmentResultOwner {
+ method public void clearFragmentResult(String);
+ method public void clearFragmentResultListener(String);
+ method public void setFragmentResult(String, android.os.Bundle);
+ method public void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+ }
+
+ @Deprecated public abstract class FragmentStatePagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+ ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager);
+ ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager, int);
+ method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+ method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+ field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+ }
+
+ @Deprecated public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+ ctor @Deprecated public FragmentTabHost(android.content.Context);
+ ctor @Deprecated public FragmentTabHost(android.content.Context, android.util.AttributeSet?);
+ method @Deprecated public void addTab(android.widget.TabHost.TabSpec, Class<?>, android.os.Bundle?);
+ method @Deprecated public void onTabChanged(String?);
+ method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager);
+ method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager, int);
+ }
+
+ public abstract class FragmentTransaction {
+ ctor @Deprecated public FragmentTransaction();
+ method public final androidx.fragment.app.FragmentTransaction add(Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+ method public androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.Fragment, String?);
+ method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+ method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment);
+ method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+ method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment, String?);
+ method public androidx.fragment.app.FragmentTransaction addSharedElement(android.view.View, String);
+ method public androidx.fragment.app.FragmentTransaction addToBackStack(String?);
+ method public androidx.fragment.app.FragmentTransaction attach(androidx.fragment.app.Fragment);
+ method public abstract int commit();
+ method public abstract int commitAllowingStateLoss();
+ method @MainThread public abstract void commitNow();
+ method @MainThread public abstract void commitNowAllowingStateLoss();
+ method public androidx.fragment.app.FragmentTransaction detach(androidx.fragment.app.Fragment);
+ method public androidx.fragment.app.FragmentTransaction disallowAddToBackStack();
+ method public androidx.fragment.app.FragmentTransaction hide(androidx.fragment.app.Fragment);
+ method public boolean isAddToBackStackAllowed();
+ method public boolean isEmpty();
+ method public androidx.fragment.app.FragmentTransaction remove(androidx.fragment.app.Fragment);
+ method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+ method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment);
+ method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+ method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
+ method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+ method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+ method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+ method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
+ method public androidx.fragment.app.FragmentTransaction setPrimaryNavigationFragment(androidx.fragment.app.Fragment?);
+ method public androidx.fragment.app.FragmentTransaction setReorderingAllowed(boolean);
+ method public androidx.fragment.app.FragmentTransaction setTransition(int);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setTransitionStyle(@StyleRes int);
+ method public androidx.fragment.app.FragmentTransaction show(androidx.fragment.app.Fragment);
+ field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+ field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+ field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+ field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+ field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_CLOSE = 8197; // 0x2005
+ field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN = 4100; // 0x1004
+ field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+ field public static final int TRANSIT_NONE = 0; // 0x0
+ field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+ }
+
+ public class ListFragment extends androidx.fragment.app.Fragment {
+ ctor public ListFragment();
+ method public android.widget.ListAdapter? getListAdapter();
+ method public android.widget.ListView getListView();
+ method public long getSelectedItemId();
+ method public int getSelectedItemPosition();
+ method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+ method public final android.widget.ListAdapter requireListAdapter();
+ method public void setEmptyText(CharSequence?);
+ method public void setListAdapter(android.widget.ListAdapter?);
+ method public void setListShown(boolean);
+ method public void setListShownNoAnimation(boolean);
+ method public void setSelection(int);
+ }
+
+}
+
+package androidx.fragment.app.strictmode {
+
+ public final class FragmentReuseViolation extends androidx.fragment.app.strictmode.Violation {
+ method public String getPreviousFragmentId();
+ property public final String previousFragmentId;
+ }
+
+ public final class FragmentStrictMode {
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
+ method @VisibleForTesting public void onPolicyViolation(androidx.fragment.app.strictmode.Violation violation);
+ method public void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
+ property public final androidx.fragment.app.strictmode.FragmentStrictMode.Policy defaultPolicy;
+ field public static final androidx.fragment.app.strictmode.FragmentStrictMode INSTANCE;
+ }
+
+ public static fun interface FragmentStrictMode.OnViolationListener {
+ method public void onViolation(androidx.fragment.app.strictmode.Violation violation);
+ }
+
+ public static final class FragmentStrictMode.Policy {
+ field public static final androidx.fragment.app.strictmode.FragmentStrictMode.Policy LAX;
+ }
+
+ public static final class FragmentStrictMode.Policy.Builder {
+ ctor public FragmentStrictMode.Policy.Builder();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(Class<? extends androidx.fragment.app.Fragment> fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(String fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentReuse();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectRetainInstanceUsage();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectSetUserVisibleHint();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectTargetFragmentUsage();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongFragmentContainer();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongNestedHierarchy();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyDeath();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyListener(androidx.fragment.app.strictmode.FragmentStrictMode.OnViolationListener listener);
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyLog();
+ }
+
+ public final class FragmentTagUsageViolation extends androidx.fragment.app.strictmode.Violation {
+ method public android.view.ViewGroup? getParentContainer();
+ property public final android.view.ViewGroup? parentContainer;
+ }
+
+ public final class GetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+ }
+
+ public final class GetTargetFragmentRequestCodeUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+ }
+
+ public final class GetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+ }
+
+ public abstract class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+ }
+
+ public final class SetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+ }
+
+ public final class SetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+ method public int getRequestCode();
+ method public androidx.fragment.app.Fragment getTargetFragment();
+ property public final int requestCode;
+ property public final androidx.fragment.app.Fragment targetFragment;
+ }
+
+ public final class SetUserVisibleHintViolation extends androidx.fragment.app.strictmode.Violation {
+ method public boolean isVisibleToUser();
+ property public final boolean isVisibleToUser;
+ }
+
+ public abstract class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
+ }
+
+ public abstract class Violation extends java.lang.RuntimeException {
+ method public final androidx.fragment.app.Fragment getFragment();
+ property public final androidx.fragment.app.Fragment fragment;
+ }
+
+ public final class WrongFragmentContainerViolation extends androidx.fragment.app.strictmode.Violation {
+ method public android.view.ViewGroup getContainer();
+ property public final android.view.ViewGroup container;
+ }
+
+ public final class WrongNestedHierarchyViolation extends androidx.fragment.app.strictmode.Violation {
+ method public int getContainerId();
+ method public androidx.fragment.app.Fragment getExpectedParentFragment();
+ property public final int containerId;
+ property public final androidx.fragment.app.Fragment expectedParentFragment;
+ }
+
+}
+
diff --git a/fragment/fragment/api/res-1.6.0-beta02.txt b/fragment/fragment/api/res-1.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fragment/fragment/api/res-1.6.0-beta02.txt
diff --git a/fragment/fragment/api/restricted_1.6.0-beta02.txt b/fragment/fragment/api/restricted_1.6.0-beta02.txt
new file mode 100644
index 0000000..369fe63
--- /dev/null
+++ b/fragment/fragment/api/restricted_1.6.0-beta02.txt
@@ -0,0 +1,583 @@
+// Signature format: 4.0
+package androidx.fragment.app {
+
+ public class DialogFragment extends androidx.fragment.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+ ctor public DialogFragment();
+ ctor public DialogFragment(@LayoutRes int);
+ method public void dismiss();
+ method public void dismissAllowingStateLoss();
+ method @MainThread public void dismissNow();
+ method public android.app.Dialog? getDialog();
+ method public boolean getShowsDialog();
+ method @StyleRes public int getTheme();
+ method public boolean isCancelable();
+ method public void onCancel(android.content.DialogInterface);
+ method @MainThread public android.app.Dialog onCreateDialog(android.os.Bundle?);
+ method @CallSuper public void onDismiss(android.content.DialogInterface);
+ method public final androidx.activity.ComponentDialog requireComponentDialog();
+ method public final android.app.Dialog requireDialog();
+ method public void setCancelable(boolean);
+ method public void setShowsDialog(boolean);
+ method public void setStyle(int, @StyleRes int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setupDialog(android.app.Dialog, int);
+ method public void show(androidx.fragment.app.FragmentManager, String?);
+ method public int show(androidx.fragment.app.FragmentTransaction, String?);
+ method public void showNow(androidx.fragment.app.FragmentManager, String?);
+ field public static final int STYLE_NORMAL = 0; // 0x0
+ field public static final int STYLE_NO_FRAME = 2; // 0x2
+ field public static final int STYLE_NO_INPUT = 3; // 0x3
+ field public static final int STYLE_NO_TITLE = 1; // 0x1
+ }
+
+ public class Fragment implements androidx.activity.result.ActivityResultCaller android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+ ctor public Fragment();
+ ctor @ContentView public Fragment(@LayoutRes int);
+ method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method public final boolean equals(Object?);
+ method public final androidx.fragment.app.FragmentActivity? getActivity();
+ method public boolean getAllowEnterTransitionOverlap();
+ method public boolean getAllowReturnTransitionOverlap();
+ method public final android.os.Bundle? getArguments();
+ method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
+ method public android.content.Context? getContext();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ method public Object? getEnterTransition();
+ method public Object? getExitTransition();
+ method @Deprecated public final androidx.fragment.app.FragmentManager? getFragmentManager();
+ method public final Object? getHost();
+ method public final int getId();
+ method public final android.view.LayoutInflater getLayoutInflater();
+ method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.view.LayoutInflater getLayoutInflater(android.os.Bundle?);
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method @Deprecated public androidx.loader.app.LoaderManager getLoaderManager();
+ method public final androidx.fragment.app.Fragment? getParentFragment();
+ method public final androidx.fragment.app.FragmentManager getParentFragmentManager();
+ method public Object? getReenterTransition();
+ method public final android.content.res.Resources getResources();
+ method @Deprecated public final boolean getRetainInstance();
+ method public Object? getReturnTransition();
+ method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+ method public Object? getSharedElementEnterTransition();
+ method public Object? getSharedElementReturnTransition();
+ method public final String getString(@StringRes int);
+ method public final String getString(@StringRes int, java.lang.Object!...);
+ method public final String? getTag();
+ method @Deprecated public final androidx.fragment.app.Fragment? getTargetFragment();
+ method @Deprecated public final int getTargetRequestCode();
+ method public final CharSequence getText(@StringRes int);
+ method @Deprecated public boolean getUserVisibleHint();
+ method public android.view.View? getView();
+ method @MainThread public androidx.lifecycle.LifecycleOwner getViewLifecycleOwner();
+ method public androidx.lifecycle.LiveData<androidx.lifecycle.LifecycleOwner!> getViewLifecycleOwnerLiveData();
+ method public androidx.lifecycle.ViewModelStore getViewModelStore();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final boolean hasOptionsMenu();
+ method public final int hashCode();
+ method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String);
+ method @Deprecated public static androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+ method public final boolean isAdded();
+ method public final boolean isDetached();
+ method public final boolean isHidden();
+ method public final boolean isInLayout();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final boolean isMenuVisible();
+ method public final boolean isRemoving();
+ method public final boolean isResumed();
+ method public final boolean isStateSaved();
+ method public final boolean isVisible();
+ method @Deprecated @CallSuper @MainThread public void onActivityCreated(android.os.Bundle?);
+ method @Deprecated public void onActivityResult(int, int, android.content.Intent?);
+ method @CallSuper @MainThread public void onAttach(android.content.Context);
+ method @Deprecated @CallSuper @MainThread public void onAttach(android.app.Activity);
+ method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+ method @CallSuper public void onConfigurationChanged(android.content.res.Configuration);
+ method @MainThread public boolean onContextItemSelected(android.view.MenuItem);
+ method @CallSuper @MainThread public void onCreate(android.os.Bundle?);
+ method @MainThread public android.view.animation.Animation? onCreateAnimation(int, boolean, int);
+ method @MainThread public android.animation.Animator? onCreateAnimator(int, boolean, int);
+ method @MainThread public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo?);
+ method @Deprecated @MainThread public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+ method @MainThread public android.view.View? onCreateView(android.view.LayoutInflater, android.view.ViewGroup?, android.os.Bundle?);
+ method @CallSuper @MainThread public void onDestroy();
+ method @Deprecated @MainThread public void onDestroyOptionsMenu();
+ method @CallSuper @MainThread public void onDestroyView();
+ method @CallSuper @MainThread public void onDetach();
+ method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle?);
+ method @MainThread public void onHiddenChanged(boolean);
+ method @CallSuper @UiThread public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle?);
+ method @Deprecated @CallSuper @UiThread public void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle?);
+ method @CallSuper @MainThread public void onLowMemory();
+ method public void onMultiWindowModeChanged(boolean);
+ method @Deprecated @MainThread public boolean onOptionsItemSelected(android.view.MenuItem);
+ method @Deprecated @MainThread public void onOptionsMenuClosed(android.view.Menu);
+ method @CallSuper @MainThread public void onPause();
+ method public void onPictureInPictureModeChanged(boolean);
+ method @Deprecated @MainThread public void onPrepareOptionsMenu(android.view.Menu);
+ method @MainThread public void onPrimaryNavigationFragmentChanged(boolean);
+ method @Deprecated public void onRequestPermissionsResult(int, String![], int[]);
+ method @CallSuper @MainThread public void onResume();
+ method @MainThread public void onSaveInstanceState(android.os.Bundle);
+ method @CallSuper @MainThread public void onStart();
+ method @CallSuper @MainThread public void onStop();
+ method @MainThread public void onViewCreated(android.view.View, android.os.Bundle?);
+ method @CallSuper @MainThread public void onViewStateRestored(android.os.Bundle?);
+ method public void postponeEnterTransition();
+ method public final void postponeEnterTransition(long, java.util.concurrent.TimeUnit);
+ method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultCallback<O!>);
+ method @MainThread public final <I, O> androidx.activity.result.ActivityResultLauncher<I!> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I!,O!>, androidx.activity.result.ActivityResultRegistry, androidx.activity.result.ActivityResultCallback<O!>);
+ method public void registerForContextMenu(android.view.View);
+ method @Deprecated public final void requestPermissions(String![], int);
+ method public final androidx.fragment.app.FragmentActivity requireActivity();
+ method public final android.os.Bundle requireArguments();
+ method public final android.content.Context requireContext();
+ method @Deprecated public final androidx.fragment.app.FragmentManager requireFragmentManager();
+ method public final Object requireHost();
+ method public final androidx.fragment.app.Fragment requireParentFragment();
+ method public final android.view.View requireView();
+ method public void setAllowEnterTransitionOverlap(boolean);
+ method public void setAllowReturnTransitionOverlap(boolean);
+ method public void setArguments(android.os.Bundle?);
+ method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void setEnterTransition(Object?);
+ method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void setExitTransition(Object?);
+ method @Deprecated public void setHasOptionsMenu(boolean);
+ method public void setInitialSavedState(androidx.fragment.app.Fragment.SavedState?);
+ method public void setMenuVisibility(boolean);
+ method public void setReenterTransition(Object?);
+ method @Deprecated public void setRetainInstance(boolean);
+ method public void setReturnTransition(Object?);
+ method public void setSharedElementEnterTransition(Object?);
+ method public void setSharedElementReturnTransition(Object?);
+ method @Deprecated public void setTargetFragment(androidx.fragment.app.Fragment?, int);
+ method @Deprecated public void setUserVisibleHint(boolean);
+ method public boolean shouldShowRequestPermissionRationale(String);
+ method public void startActivity(android.content.Intent);
+ method public void startActivity(android.content.Intent, android.os.Bundle?);
+ method @Deprecated public void startActivityForResult(android.content.Intent, int);
+ method @Deprecated public void startActivityForResult(android.content.Intent, int, android.os.Bundle?);
+ method @Deprecated public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public void startPostponedEnterTransition();
+ method public void unregisterForContextMenu(android.view.View);
+ }
+
+ public static class Fragment.InstantiationException extends java.lang.RuntimeException {
+ ctor public Fragment.InstantiationException(String, Exception?);
+ }
+
+ public static class Fragment.SavedState implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<androidx.fragment.app.Fragment.SavedState!> CREATOR;
+ }
+
+ public class FragmentActivity extends androidx.activity.ComponentActivity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback androidx.core.app.ActivityCompat.RequestPermissionsRequestCodeValidator {
+ ctor public FragmentActivity();
+ ctor @ContentView public FragmentActivity(@LayoutRes int);
+ method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+ method @Deprecated public androidx.loader.app.LoaderManager getSupportLoaderManager();
+ method @Deprecated @MainThread public void onAttachFragment(androidx.fragment.app.Fragment);
+ method protected void onResumeFragments();
+ method public void onStateNotSaved();
+ method public void setEnterSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void setExitSharedElementCallback(androidx.core.app.SharedElementCallback?);
+ method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+ method public void startActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+ method @Deprecated public void startIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public void supportFinishAfterTransition();
+ method @Deprecated public void supportInvalidateOptionsMenu();
+ method public void supportPostponeEnterTransition();
+ method public void supportStartPostponedEnterTransition();
+ method @Deprecated public final void validateRequestPermissionsRequestCode(int);
+ }
+
+ public abstract class FragmentContainer {
+ ctor public FragmentContainer();
+ method @Deprecated public androidx.fragment.app.Fragment instantiate(android.content.Context, String, android.os.Bundle?);
+ method public abstract android.view.View? onFindViewById(@IdRes int);
+ method public abstract boolean onHasView();
+ }
+
+ public final class FragmentContainerView extends android.widget.FrameLayout {
+ ctor public FragmentContainerView(android.content.Context context);
+ ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs, optional int defStyleAttr);
+ ctor public FragmentContainerView(android.content.Context context, android.util.AttributeSet? attrs);
+ method public <F extends androidx.fragment.app.Fragment> F getFragment();
+ }
+
+ public class FragmentController {
+ method public void attachHost(androidx.fragment.app.Fragment?);
+ method public static androidx.fragment.app.FragmentController createController(androidx.fragment.app.FragmentHostCallback<?>);
+ method public void dispatchActivityCreated();
+ method @Deprecated public void dispatchConfigurationChanged(android.content.res.Configuration);
+ method public boolean dispatchContextItemSelected(android.view.MenuItem);
+ method public void dispatchCreate();
+ method @Deprecated public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+ method public void dispatchDestroy();
+ method public void dispatchDestroyView();
+ method @Deprecated public void dispatchLowMemory();
+ method @Deprecated public void dispatchMultiWindowModeChanged(boolean);
+ method @Deprecated public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+ method @Deprecated public void dispatchOptionsMenuClosed(android.view.Menu);
+ method public void dispatchPause();
+ method @Deprecated public void dispatchPictureInPictureModeChanged(boolean);
+ method @Deprecated public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+ method @Deprecated public void dispatchReallyStop();
+ method public void dispatchResume();
+ method public void dispatchStart();
+ method public void dispatchStop();
+ method @Deprecated public void doLoaderDestroy();
+ method @Deprecated public void doLoaderRetain();
+ method @Deprecated public void doLoaderStart();
+ method @Deprecated public void doLoaderStop(boolean);
+ method @Deprecated public void dumpLoaders(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method public boolean execPendingActions();
+ method public androidx.fragment.app.Fragment? findFragmentByWho(String);
+ method public java.util.List<androidx.fragment.app.Fragment!> getActiveFragments(java.util.List<androidx.fragment.app.Fragment!>!);
+ method public int getActiveFragmentsCount();
+ method public androidx.fragment.app.FragmentManager getSupportFragmentManager();
+ method @Deprecated public androidx.loader.app.LoaderManager! getSupportLoaderManager();
+ method public void noteStateNotSaved();
+ method public android.view.View? onCreateView(android.view.View?, String, android.content.Context, android.util.AttributeSet);
+ method @Deprecated public void reportLoaderStart();
+ method @Deprecated public void restoreAllState(android.os.Parcelable?, java.util.List<androidx.fragment.app.Fragment!>?);
+ method @Deprecated public void restoreAllState(android.os.Parcelable?, androidx.fragment.app.FragmentManagerNonConfig?);
+ method @Deprecated public void restoreLoaderNonConfig(androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>!);
+ method @Deprecated public void restoreSaveState(android.os.Parcelable?);
+ method @Deprecated public androidx.collection.SimpleArrayMap<java.lang.String!,androidx.loader.app.LoaderManager!>? retainLoaderNonConfig();
+ method @Deprecated public androidx.fragment.app.FragmentManagerNonConfig? retainNestedNonConfig();
+ method @Deprecated public java.util.List<androidx.fragment.app.Fragment!>? retainNonConfig();
+ method @Deprecated public android.os.Parcelable? saveAllState();
+ }
+
+ public class FragmentFactory {
+ ctor public FragmentFactory();
+ method public androidx.fragment.app.Fragment instantiate(ClassLoader, String);
+ method public static Class<? extends androidx.fragment.app.Fragment> loadFragmentClass(ClassLoader, String);
+ }
+
+ public abstract class FragmentHostCallback<E> extends androidx.fragment.app.FragmentContainer {
+ ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+ method public void onDump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method public android.view.View? onFindViewById(int);
+ method public abstract E? onGetHost();
+ method public android.view.LayoutInflater onGetLayoutInflater();
+ method public int onGetWindowAnimations();
+ method public boolean onHasView();
+ method public boolean onHasWindowAnimations();
+ method @Deprecated public void onRequestPermissionsFromFragment(androidx.fragment.app.Fragment, String![], int);
+ method public boolean onShouldSaveFragmentState(androidx.fragment.app.Fragment);
+ method public boolean onShouldShowRequestPermissionRationale(String);
+ method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int);
+ method public void onStartActivityFromFragment(androidx.fragment.app.Fragment, android.content.Intent, int, android.os.Bundle?);
+ method @Deprecated public void onStartIntentSenderFromFragment(androidx.fragment.app.Fragment, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+ method public void onSupportInvalidateOptionsMenu();
+ }
+
+ public abstract class FragmentManager implements androidx.fragment.app.FragmentResultOwner {
+ ctor public FragmentManager();
+ method public void addFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+ method public void addOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+ method public androidx.fragment.app.FragmentTransaction beginTransaction();
+ method public void clearBackStack(String);
+ method public final void clearFragmentResult(String);
+ method public final void clearFragmentResultListener(String);
+ method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
+ method @Deprecated public static void enableDebugLogging(boolean);
+ method @MainThread public boolean executePendingTransactions();
+ method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+ method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
+ method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
+ method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
+ method public int getBackStackEntryCount();
+ method public androidx.fragment.app.Fragment? getFragment(android.os.Bundle, String);
+ method public androidx.fragment.app.FragmentFactory getFragmentFactory();
+ method public java.util.List<androidx.fragment.app.Fragment!> getFragments();
+ method public androidx.fragment.app.Fragment? getPrimaryNavigationFragment();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy? getStrictModePolicy();
+ method public boolean isDestroyed();
+ method public boolean isStateSaved();
+ method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.fragment.app.FragmentTransaction openTransaction();
+ method public void popBackStack();
+ method public void popBackStack(String?, int);
+ method public void popBackStack(int, int);
+ method @MainThread public boolean popBackStackImmediate();
+ method @MainThread public boolean popBackStackImmediate(String?, int);
+ method public boolean popBackStackImmediate(int, int);
+ method public void putFragment(android.os.Bundle, String, androidx.fragment.app.Fragment);
+ method public void registerFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
+ method public void removeFragmentOnAttachListener(androidx.fragment.app.FragmentOnAttachListener);
+ method public void removeOnBackStackChangedListener(androidx.fragment.app.FragmentManager.OnBackStackChangedListener);
+ method public void restoreBackStack(String);
+ method public void saveBackStack(String);
+ method public androidx.fragment.app.Fragment.SavedState? saveFragmentInstanceState(androidx.fragment.app.Fragment);
+ method public void setFragmentFactory(androidx.fragment.app.FragmentFactory);
+ method public final void setFragmentResult(String, android.os.Bundle);
+ method public final void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+ method public void setStrictModePolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy?);
+ method public void unregisterFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks);
+ field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
+ }
+
+ public static interface FragmentManager.BackStackEntry {
+ method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+ method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+ method @Deprecated public CharSequence? getBreadCrumbTitle();
+ method @Deprecated @StringRes public int getBreadCrumbTitleRes();
+ method public int getId();
+ method public String? getName();
+ }
+
+ public abstract static class FragmentManager.FragmentLifecycleCallbacks {
+ ctor public FragmentManager.FragmentLifecycleCallbacks();
+ method @Deprecated public void onFragmentActivityCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+ method public void onFragmentAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+ method public void onFragmentCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+ method public void onFragmentDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentDetached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentPaused(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentPreAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context);
+ method public void onFragmentPreCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle?);
+ method public void onFragmentResumed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentSaveInstanceState(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.os.Bundle);
+ method public void onFragmentStarted(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentStopped(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ method public void onFragmentViewCreated(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.view.View, android.os.Bundle?);
+ method public void onFragmentViewDestroyed(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ }
+
+ public static interface FragmentManager.OnBackStackChangedListener {
+ method @MainThread public default void onBackStackChangeCommitted(androidx.fragment.app.Fragment, boolean);
+ method @MainThread public default void onBackStackChangeStarted(androidx.fragment.app.Fragment, boolean);
+ method @MainThread public void onBackStackChanged();
+ }
+
+ @Deprecated public class FragmentManagerNonConfig {
+ }
+
+ public interface FragmentOnAttachListener {
+ method @MainThread public void onAttachFragment(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment);
+ }
+
+ @Deprecated public abstract class FragmentPagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+ ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager);
+ ctor @Deprecated public FragmentPagerAdapter(androidx.fragment.app.FragmentManager, int);
+ method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+ method @Deprecated public long getItemId(int);
+ method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+ field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+ }
+
+ public interface FragmentResultListener {
+ method public void onFragmentResult(String, android.os.Bundle);
+ }
+
+ public interface FragmentResultOwner {
+ method public void clearFragmentResult(String);
+ method public void clearFragmentResultListener(String);
+ method public void setFragmentResult(String, android.os.Bundle);
+ method public void setFragmentResultListener(String, androidx.lifecycle.LifecycleOwner, androidx.fragment.app.FragmentResultListener);
+ }
+
+ @Deprecated public abstract class FragmentStatePagerAdapter extends androidx.viewpager.widget.PagerAdapter {
+ ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager);
+ ctor @Deprecated public FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager, int);
+ method @Deprecated public abstract androidx.fragment.app.Fragment getItem(int);
+ method @Deprecated public boolean isViewFromObject(android.view.View, Object);
+ field @Deprecated public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1; // 0x1
+ field @Deprecated public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0; // 0x0
+ }
+
+ @Deprecated public class FragmentTabHost extends android.widget.TabHost implements android.widget.TabHost.OnTabChangeListener {
+ ctor @Deprecated public FragmentTabHost(android.content.Context);
+ ctor @Deprecated public FragmentTabHost(android.content.Context, android.util.AttributeSet?);
+ method @Deprecated public void addTab(android.widget.TabHost.TabSpec, Class<?>, android.os.Bundle?);
+ method @Deprecated public void onTabChanged(String?);
+ method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager);
+ method @Deprecated public void setup(android.content.Context, androidx.fragment.app.FragmentManager, int);
+ }
+
+ public abstract class FragmentTransaction {
+ ctor @Deprecated public FragmentTransaction();
+ method public final androidx.fragment.app.FragmentTransaction add(Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+ method public androidx.fragment.app.FragmentTransaction add(androidx.fragment.app.Fragment, String?);
+ method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+ method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment);
+ method public final androidx.fragment.app.FragmentTransaction add(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+ method public androidx.fragment.app.FragmentTransaction add(@IdRes int, androidx.fragment.app.Fragment, String?);
+ method public androidx.fragment.app.FragmentTransaction addSharedElement(android.view.View, String);
+ method public androidx.fragment.app.FragmentTransaction addToBackStack(String?);
+ method public androidx.fragment.app.FragmentTransaction attach(androidx.fragment.app.Fragment);
+ method public abstract int commit();
+ method public abstract int commitAllowingStateLoss();
+ method @MainThread public abstract void commitNow();
+ method @MainThread public abstract void commitNowAllowingStateLoss();
+ method public androidx.fragment.app.FragmentTransaction detach(androidx.fragment.app.Fragment);
+ method public androidx.fragment.app.FragmentTransaction disallowAddToBackStack();
+ method public androidx.fragment.app.FragmentTransaction hide(androidx.fragment.app.Fragment);
+ method public boolean isAddToBackStackAllowed();
+ method public boolean isEmpty();
+ method public androidx.fragment.app.FragmentTransaction remove(androidx.fragment.app.Fragment);
+ method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?);
+ method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment);
+ method public final androidx.fragment.app.FragmentTransaction replace(@IdRes int, Class<? extends androidx.fragment.app.Fragment>, android.os.Bundle?, String?);
+ method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
+ method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+ method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+ method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int, @AnimRes @AnimatorRes int);
+ method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
+ method public androidx.fragment.app.FragmentTransaction setPrimaryNavigationFragment(androidx.fragment.app.Fragment?);
+ method public androidx.fragment.app.FragmentTransaction setReorderingAllowed(boolean);
+ method public androidx.fragment.app.FragmentTransaction setTransition(int);
+ method @Deprecated public androidx.fragment.app.FragmentTransaction setTransitionStyle(@StyleRes int);
+ method public androidx.fragment.app.FragmentTransaction show(androidx.fragment.app.Fragment);
+ field public static final int TRANSIT_ENTER_MASK = 4096; // 0x1000
+ field public static final int TRANSIT_EXIT_MASK = 8192; // 0x2000
+ field public static final int TRANSIT_FRAGMENT_CLOSE = 8194; // 0x2002
+ field public static final int TRANSIT_FRAGMENT_FADE = 4099; // 0x1003
+ field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_CLOSE = 8197; // 0x2005
+ field public static final int TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN = 4100; // 0x1004
+ field public static final int TRANSIT_FRAGMENT_OPEN = 4097; // 0x1001
+ field public static final int TRANSIT_NONE = 0; // 0x0
+ field public static final int TRANSIT_UNSET = -1; // 0xffffffff
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class FragmentTransitionImpl {
+ ctor public FragmentTransitionImpl();
+ method public abstract void addTarget(Object, android.view.View);
+ method public abstract void addTargets(Object, java.util.ArrayList<android.view.View!>);
+ method public abstract void beginDelayedTransition(android.view.ViewGroup, Object?);
+ method protected static void bfsAddViewChildren(java.util.List<android.view.View!>!, android.view.View!);
+ method public abstract boolean canHandle(Object);
+ method public abstract Object! cloneTransition(Object?);
+ method protected void getBoundsOnScreen(android.view.View!, android.graphics.Rect!);
+ method protected static boolean isNullOrEmpty(java.util.List!);
+ method public abstract Object! mergeTransitionsInSequence(Object?, Object?, Object?);
+ method public abstract Object! mergeTransitionsTogether(Object?, Object?, Object?);
+ method public abstract void removeTarget(Object, android.view.View);
+ method public abstract void replaceTargets(Object, java.util.ArrayList<android.view.View!>!, java.util.ArrayList<android.view.View!>!);
+ method public abstract void scheduleHideFragmentView(Object, android.view.View, java.util.ArrayList<android.view.View!>);
+ method public abstract void scheduleRemoveTargets(Object, Object?, java.util.ArrayList<android.view.View!>?, Object?, java.util.ArrayList<android.view.View!>?, Object?, java.util.ArrayList<android.view.View!>?);
+ method public abstract void setEpicenter(Object, android.view.View?);
+ method public abstract void setEpicenter(Object, android.graphics.Rect);
+ method public void setListenerForTransitionEnd(androidx.fragment.app.Fragment, Object, androidx.core.os.CancellationSignal, Runnable);
+ method public abstract void setSharedElementTargets(Object, android.view.View, java.util.ArrayList<android.view.View!>);
+ method public abstract void swapSharedElementTargets(Object?, java.util.ArrayList<android.view.View!>?, java.util.ArrayList<android.view.View!>?);
+ method public abstract Object! wrapTransitionInSet(Object?);
+ }
+
+ public class ListFragment extends androidx.fragment.app.Fragment {
+ ctor public ListFragment();
+ method public android.widget.ListAdapter? getListAdapter();
+ method public android.widget.ListView getListView();
+ method public long getSelectedItemId();
+ method public int getSelectedItemPosition();
+ method public void onListItemClick(android.widget.ListView, android.view.View, int, long);
+ method public final android.widget.ListAdapter requireListAdapter();
+ method public void setEmptyText(CharSequence?);
+ method public void setListAdapter(android.widget.ListAdapter?);
+ method public void setListShown(boolean);
+ method public void setListShownNoAnimation(boolean);
+ method public void setSelection(int);
+ }
+
+}
+
+package androidx.fragment.app.strictmode {
+
+ public final class FragmentReuseViolation extends androidx.fragment.app.strictmode.Violation {
+ method public String getPreviousFragmentId();
+ property public final String previousFragmentId;
+ }
+
+ public final class FragmentStrictMode {
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy getDefaultPolicy();
+ method @VisibleForTesting public void onPolicyViolation(androidx.fragment.app.strictmode.Violation violation);
+ method public void setDefaultPolicy(androidx.fragment.app.strictmode.FragmentStrictMode.Policy);
+ property public final androidx.fragment.app.strictmode.FragmentStrictMode.Policy defaultPolicy;
+ field public static final androidx.fragment.app.strictmode.FragmentStrictMode INSTANCE;
+ }
+
+ public static fun interface FragmentStrictMode.OnViolationListener {
+ method public void onViolation(androidx.fragment.app.strictmode.Violation violation);
+ }
+
+ public static final class FragmentStrictMode.Policy {
+ field public static final androidx.fragment.app.strictmode.FragmentStrictMode.Policy LAX;
+ }
+
+ public static final class FragmentStrictMode.Policy.Builder {
+ ctor public FragmentStrictMode.Policy.Builder();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(Class<? extends androidx.fragment.app.Fragment> fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(String fragmentClass, Class<? extends androidx.fragment.app.strictmode.Violation> violationClass);
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentReuse();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectRetainInstanceUsage();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectSetUserVisibleHint();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectTargetFragmentUsage();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongFragmentContainer();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectWrongNestedHierarchy();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyDeath();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyListener(androidx.fragment.app.strictmode.FragmentStrictMode.OnViolationListener listener);
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder penaltyLog();
+ }
+
+ public final class FragmentTagUsageViolation extends androidx.fragment.app.strictmode.Violation {
+ method public android.view.ViewGroup? getParentContainer();
+ property public final android.view.ViewGroup? parentContainer;
+ }
+
+ public final class GetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+ }
+
+ public final class GetTargetFragmentRequestCodeUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+ }
+
+ public final class GetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+ }
+
+ public abstract class RetainInstanceUsageViolation extends androidx.fragment.app.strictmode.Violation {
+ }
+
+ public final class SetRetainInstanceUsageViolation extends androidx.fragment.app.strictmode.RetainInstanceUsageViolation {
+ }
+
+ public final class SetTargetFragmentUsageViolation extends androidx.fragment.app.strictmode.TargetFragmentUsageViolation {
+ method public int getRequestCode();
+ method public androidx.fragment.app.Fragment getTargetFragment();
+ property public final int requestCode;
+ property public final androidx.fragment.app.Fragment targetFragment;
+ }
+
+ public final class SetUserVisibleHintViolation extends androidx.fragment.app.strictmode.Violation {
+ method public boolean isVisibleToUser();
+ property public final boolean isVisibleToUser;
+ }
+
+ public abstract class TargetFragmentUsageViolation extends androidx.fragment.app.strictmode.Violation {
+ }
+
+ public abstract class Violation extends java.lang.RuntimeException {
+ method public final androidx.fragment.app.Fragment getFragment();
+ property public final androidx.fragment.app.Fragment fragment;
+ }
+
+ public final class WrongFragmentContainerViolation extends androidx.fragment.app.strictmode.Violation {
+ method public android.view.ViewGroup getContainer();
+ property public final android.view.ViewGroup container;
+ }
+
+ public final class WrongNestedHierarchyViolation extends androidx.fragment.app.strictmode.Violation {
+ method public int getContainerId();
+ method public androidx.fragment.app.Fragment getExpectedParentFragment();
+ property public final int containerId;
+ property public final androidx.fragment.app.Fragment expectedParentFragment;
+ }
+
+}
+
diff --git a/glance/glance-appwidget-preview/api/1.0.0-beta01.txt b/glance/glance-appwidget-preview/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-appwidget-preview/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-appwidget-preview/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-appwidget-preview/api/res-1.0.0-beta01.txt b/glance/glance-appwidget-preview/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/res-1.0.0-beta01.txt
diff --git a/glance/glance-appwidget-preview/api/restricted_1.0.0-beta01.txt b/glance/glance-appwidget-preview/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-appwidget-preview/build.gradle b/glance/glance-appwidget-preview/build.gradle
index bb9faa2..07567f5 100644
--- a/glance/glance-appwidget-preview/build.gradle
+++ b/glance/glance-appwidget-preview/build.gradle
@@ -35,7 +35,7 @@
implementation("androidx.core:core:1.1.0")
- api(project(":compose:runtime:runtime"))
+ api("androidx.compose.runtime:runtime:1.2.1")
api(project(":glance:glance"))
api(project(":glance:glance-appwidget"))
@@ -54,6 +54,7 @@
androidx {
name = "Android Glance AppWidget Preview"
type = LibraryType.PUBLISHED_LIBRARY
+ mavenVersion = LibraryVersions.GLANCE_PREVIEW
inceptionYear = "2022"
description = "Glance tooling library. This library provides the API required for the " +
"GlanceAppWidget components and its Glance @Composable to be previewable in the IDE."
diff --git a/glance/glance-appwidget/api/1.0.0-beta01.txt b/glance/glance-appwidget/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..72717d0
--- /dev/null
+++ b/glance/glance-appwidget/api/1.0.0-beta01.txt
@@ -0,0 +1,264 @@
+// Signature format: 4.0
+package androidx.glance.appwidget {
+
+ public final class AndroidRemoteViewsKt {
+ method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, optional androidx.glance.GlanceModifier modifier);
+ method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, optional androidx.glance.GlanceModifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class AppWidgetBackgroundKt {
+ method public static androidx.glance.GlanceModifier appWidgetBackground(androidx.glance.GlanceModifier);
+ }
+
+ public final class AppWidgetComposerKt {
+ method public static suspend Object? compose(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId id, optional android.os.Bundle? options, optional androidx.compose.ui.unit.DpSize? size, optional Object? state, optional kotlin.coroutines.Continuation<? super android.widget.RemoteViews>);
+ }
+
+ public final class BackgroundKt {
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long day, long night);
+ }
+
+ public abstract sealed class CheckBoxColors {
+ }
+
+ public final class CheckBoxKt {
+ method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+ }
+
+ public final class CheckboxDefaults {
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(long checkedColor, long uncheckedColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors();
+ field public static final androidx.glance.appwidget.CheckboxDefaults INSTANCE;
+ }
+
+ public final class CircularProgressIndicatorKt {
+ method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color);
+ }
+
+ public final class CompositionLocalsKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> getLocalAppWidgetOptions();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> LocalAppWidgetOptions;
+ }
+
+ public final class CornerRadiusKt {
+ method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, float radius);
+ method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, @DimenRes int radius);
+ }
+
+ public abstract class GlanceAppWidget {
+ ctor public GlanceAppWidget(optional @LayoutRes int errorUiLayout);
+ method public androidx.glance.appwidget.SizeMode getSizeMode();
+ method public androidx.glance.state.GlanceStateDefinition<?>? getStateDefinition();
+ method public suspend Object? onDelete(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public abstract suspend Object? provideGlance(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public final suspend Object? update(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public androidx.glance.appwidget.SizeMode sizeMode;
+ property public androidx.glance.state.GlanceStateDefinition<?>? stateDefinition;
+ }
+
+ public final class GlanceAppWidgetKt {
+ method public static suspend Object? provideContent(androidx.glance.appwidget.GlanceAppWidget, kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.coroutines.Continuation<?>);
+ method public static suspend Object? updateAll(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend inline <reified State> void updateIf(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.jvm.functions.Function1<? super State,? extends java.lang.Boolean> predicate);
+ }
+
+ public final class GlanceAppWidgetManager {
+ ctor public GlanceAppWidgetManager(android.content.Context context);
+ method public int getAppWidgetId(androidx.glance.GlanceId glanceId);
+ method public suspend Object? getAppWidgetSizes(androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.compose.ui.unit.DpSize>>);
+ method public androidx.glance.GlanceId getGlanceIdBy(int appWidgetId);
+ method public androidx.glance.GlanceId? getGlanceIdBy(android.content.Intent configurationIntent);
+ method public suspend <T extends androidx.glance.appwidget.GlanceAppWidget> Object? getGlanceIds(Class<T> provider, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.glance.GlanceId>>);
+ method public suspend <T extends androidx.glance.appwidget.GlanceAppWidgetReceiver> Object? requestPinGlanceAppWidget(Class<T> receiver, optional androidx.glance.appwidget.GlanceAppWidget? preview, optional Object? previewState, optional android.app.PendingIntent? successCallback, optional kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ }
+
+ public abstract class GlanceAppWidgetReceiver extends android.appwidget.AppWidgetProvider {
+ ctor public GlanceAppWidgetReceiver();
+ method public abstract androidx.glance.appwidget.GlanceAppWidget getGlanceAppWidget();
+ property public abstract androidx.glance.appwidget.GlanceAppWidget glanceAppWidget;
+ field public static final String ACTION_DEBUG_UPDATE = "androidx.glance.appwidget.action.DEBUG_UPDATE";
+ field public static final androidx.glance.appwidget.GlanceAppWidgetReceiver.Companion Companion;
+ }
+
+ public static final class GlanceAppWidgetReceiver.Companion {
+ }
+
+ public final class ImageProvidersKt {
+ method public static androidx.glance.ImageProvider ImageProvider(android.net.Uri uri);
+ }
+
+ public final class LinearProgressIndicatorKt {
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+ }
+
+ public final class ProgressIndicatorDefaults {
+ method public androidx.glance.unit.ColorProvider getBackgroundColorProvider();
+ method public androidx.glance.unit.ColorProvider getIndicatorColorProvider();
+ property public final androidx.glance.unit.ColorProvider BackgroundColorProvider;
+ property public final androidx.glance.unit.ColorProvider IndicatorColorProvider;
+ field public static final androidx.glance.appwidget.ProgressIndicatorDefaults INSTANCE;
+ }
+
+ public final class RadioButtonColors {
+ }
+
+ public final class RadioButtonDefaults {
+ method public androidx.glance.appwidget.RadioButtonColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+ method public androidx.glance.appwidget.RadioButtonColors colors(long checkedColor, long uncheckedColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.RadioButtonColors colors();
+ field public static final androidx.glance.appwidget.RadioButtonDefaults INSTANCE;
+ }
+
+ public final class RadioButtonKt {
+ method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, androidx.glance.action.Action? onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+ method public static androidx.glance.GlanceModifier selectableGroup(androidx.glance.GlanceModifier);
+ }
+
+ public sealed interface SizeMode {
+ }
+
+ public static final class SizeMode.Exact implements androidx.glance.appwidget.SizeMode {
+ field public static final androidx.glance.appwidget.SizeMode.Exact INSTANCE;
+ }
+
+ public static final class SizeMode.Responsive implements androidx.glance.appwidget.SizeMode {
+ ctor public SizeMode.Responsive(java.util.Set<androidx.compose.ui.unit.DpSize> sizes);
+ method public java.util.Set<androidx.compose.ui.unit.DpSize> getSizes();
+ property public final java.util.Set<androidx.compose.ui.unit.DpSize> sizes;
+ }
+
+ public static final class SizeMode.Single implements androidx.glance.appwidget.SizeMode {
+ field public static final androidx.glance.appwidget.SizeMode.Single INSTANCE;
+ }
+
+ public abstract sealed class SwitchColors {
+ }
+
+ public final class SwitchDefaults {
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors(androidx.glance.unit.ColorProvider checkedThumbColor, androidx.glance.unit.ColorProvider uncheckedThumbColor, androidx.glance.unit.ColorProvider checkedTrackColor, androidx.glance.unit.ColorProvider uncheckedTrackColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors();
+ field public static final androidx.glance.appwidget.SwitchDefaults INSTANCE;
+ }
+
+ public final class SwitchKt {
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+ }
+
+}
+
+package androidx.glance.appwidget.action {
+
+ public interface ActionCallback {
+ method public suspend Object? onAction(android.content.Context context, androidx.glance.GlanceId glanceId, androidx.glance.action.ActionParameters parameters, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class RunCallbackActionKt {
+ method public static <T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(Class<T> callbackClass, optional androidx.glance.action.ActionParameters parameters);
+ method public static inline <reified T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(optional androidx.glance.action.ActionParameters parameters);
+ }
+
+ public final class SendBroadcastActionKt {
+ method public static androidx.glance.action.Action actionSendBroadcast(String action, optional android.content.ComponentName? componentName);
+ method public static androidx.glance.action.Action actionSendBroadcast(android.content.Intent intent);
+ method public static androidx.glance.action.Action actionSendBroadcast(android.content.ComponentName componentName);
+ method public static <T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast(Class<T> receiver);
+ method public static inline <reified T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast();
+ }
+
+ public final class StartActivityIntentActionKt {
+ method public static androidx.glance.action.Action actionStartActivity(android.content.Intent intent, optional androidx.glance.action.ActionParameters parameters);
+ }
+
+ public final class StartServiceActionKt {
+ method public static androidx.glance.action.Action actionStartService(android.content.Intent intent, optional boolean isForegroundService);
+ method public static androidx.glance.action.Action actionStartService(android.content.ComponentName componentName, optional boolean isForegroundService);
+ method public static <T extends android.app.Service> androidx.glance.action.Action actionStartService(Class<T> service, optional boolean isForegroundService);
+ method public static inline <reified T extends android.app.Service> androidx.glance.action.Action actionStartService(optional boolean isForegroundService);
+ }
+
+ public final class ToggleableKt {
+ method public static androidx.glance.action.ActionParameters.Key<java.lang.Boolean> getToggleableStateKey();
+ property public static final androidx.glance.action.ActionParameters.Key<java.lang.Boolean> ToggleableStateKey;
+ }
+
+}
+
+package androidx.glance.appwidget.lazy {
+
+ public abstract sealed class GridCells {
+ }
+
+ @RequiresApi(31) public static final class GridCells.Adaptive extends androidx.glance.appwidget.lazy.GridCells {
+ ctor public GridCells.Adaptive(float minSize);
+ method public float getMinSize();
+ property public final float minSize;
+ }
+
+ public static final class GridCells.Fixed extends androidx.glance.appwidget.lazy.GridCells {
+ ctor public GridCells.Fixed(int count);
+ method public int getCount();
+ property public final int count;
+ }
+
+ @androidx.glance.appwidget.lazy.LazyScopeMarker public interface LazyItemScope {
+ }
+
+ public final class LazyListKt {
+ method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyListScope,kotlin.Unit> content);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyListScope {
+ method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+ method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ field public static final androidx.glance.appwidget.lazy.LazyListScope.Companion Companion;
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ public static final class LazyListScope.Companion {
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ @kotlin.DslMarker public @interface LazyScopeMarker {
+ }
+
+ public final class LazyVerticalGridKt {
+ method @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.glance.appwidget.lazy.GridCells gridCells, optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyVerticalGridScope,kotlin.Unit> content);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyVerticalGridScope {
+ method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+ method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ field public static final androidx.glance.appwidget.lazy.LazyVerticalGridScope.Companion Companion;
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ public static final class LazyVerticalGridScope.Companion {
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+}
+
+package androidx.glance.appwidget.state {
+
+ public final class GlanceAppWidgetStateKt {
+ method public static suspend <T> Object? getAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+ method public static suspend <T> Object? getAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+ method public static suspend <T> Object? updateAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T>);
+ method public static suspend Object? updateAppWidgetState(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super androidx.datastore.preferences.core.MutablePreferences,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> updateState, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+}
+
diff --git a/glance/glance-appwidget/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-appwidget/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..4ac3223
--- /dev/null
+++ b/glance/glance-appwidget/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,278 @@
+// Signature format: 4.0
+package androidx.glance.appwidget {
+
+ public final class AndroidRemoteViewsKt {
+ method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, optional androidx.glance.GlanceModifier modifier);
+ method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, optional androidx.glance.GlanceModifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class AppWidgetBackgroundKt {
+ method public static androidx.glance.GlanceModifier appWidgetBackground(androidx.glance.GlanceModifier);
+ }
+
+ public final class AppWidgetComposerKt {
+ method public static suspend Object? compose(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId id, optional android.os.Bundle? options, optional androidx.compose.ui.unit.DpSize? size, optional Object? state, optional kotlin.coroutines.Continuation<? super android.widget.RemoteViews>);
+ }
+
+ public final class BackgroundKt {
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long day, long night);
+ }
+
+ public abstract sealed class CheckBoxColors {
+ }
+
+ public final class CheckBoxKt {
+ method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+ }
+
+ public final class CheckboxDefaults {
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(long checkedColor, long uncheckedColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors();
+ field public static final androidx.glance.appwidget.CheckboxDefaults INSTANCE;
+ }
+
+ public final class CircularProgressIndicatorKt {
+ method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color);
+ }
+
+ public final class CompositionLocalsKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> getLocalAppWidgetOptions();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> LocalAppWidgetOptions;
+ }
+
+ public final class CornerRadiusKt {
+ method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, float radius);
+ method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, @DimenRes int radius);
+ }
+
+ @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGlanceRemoteViewsApi {
+ }
+
+ public abstract class GlanceAppWidget {
+ ctor public GlanceAppWidget(optional @LayoutRes int errorUiLayout);
+ method public androidx.glance.appwidget.SizeMode getSizeMode();
+ method public androidx.glance.state.GlanceStateDefinition<?>? getStateDefinition();
+ method public suspend Object? onDelete(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public abstract suspend Object? provideGlance(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public final suspend Object? update(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public androidx.glance.appwidget.SizeMode sizeMode;
+ property public androidx.glance.state.GlanceStateDefinition<?>? stateDefinition;
+ }
+
+ public final class GlanceAppWidgetKt {
+ method public static suspend Object? provideContent(androidx.glance.appwidget.GlanceAppWidget, kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.coroutines.Continuation<?>);
+ method public static suspend Object? updateAll(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend inline <reified State> void updateIf(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.jvm.functions.Function1<? super State,? extends java.lang.Boolean> predicate);
+ }
+
+ public final class GlanceAppWidgetManager {
+ ctor public GlanceAppWidgetManager(android.content.Context context);
+ method public int getAppWidgetId(androidx.glance.GlanceId glanceId);
+ method public suspend Object? getAppWidgetSizes(androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.compose.ui.unit.DpSize>>);
+ method public androidx.glance.GlanceId getGlanceIdBy(int appWidgetId);
+ method public androidx.glance.GlanceId? getGlanceIdBy(android.content.Intent configurationIntent);
+ method public suspend <T extends androidx.glance.appwidget.GlanceAppWidget> Object? getGlanceIds(Class<T> provider, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.glance.GlanceId>>);
+ method public suspend <T extends androidx.glance.appwidget.GlanceAppWidgetReceiver> Object? requestPinGlanceAppWidget(Class<T> receiver, optional androidx.glance.appwidget.GlanceAppWidget? preview, optional Object? previewState, optional android.app.PendingIntent? successCallback, optional kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ }
+
+ public abstract class GlanceAppWidgetReceiver extends android.appwidget.AppWidgetProvider {
+ ctor public GlanceAppWidgetReceiver();
+ method public abstract androidx.glance.appwidget.GlanceAppWidget getGlanceAppWidget();
+ property public abstract androidx.glance.appwidget.GlanceAppWidget glanceAppWidget;
+ field public static final String ACTION_DEBUG_UPDATE = "androidx.glance.appwidget.action.DEBUG_UPDATE";
+ field public static final androidx.glance.appwidget.GlanceAppWidgetReceiver.Companion Companion;
+ }
+
+ public static final class GlanceAppWidgetReceiver.Companion {
+ }
+
+ @androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi public final class GlanceRemoteViews {
+ ctor public GlanceRemoteViews();
+ method public suspend Object? compose(android.content.Context context, long size, optional Object? state, optional android.os.Bundle appWidgetOptions, kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.coroutines.Continuation<? super androidx.glance.appwidget.RemoteViewsCompositionResult>);
+ }
+
+ public final class ImageProvidersKt {
+ method public static androidx.glance.ImageProvider ImageProvider(android.net.Uri uri);
+ }
+
+ public final class LinearProgressIndicatorKt {
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+ }
+
+ public final class ProgressIndicatorDefaults {
+ method public androidx.glance.unit.ColorProvider getBackgroundColorProvider();
+ method public androidx.glance.unit.ColorProvider getIndicatorColorProvider();
+ property public final androidx.glance.unit.ColorProvider BackgroundColorProvider;
+ property public final androidx.glance.unit.ColorProvider IndicatorColorProvider;
+ field public static final androidx.glance.appwidget.ProgressIndicatorDefaults INSTANCE;
+ }
+
+ public final class RadioButtonColors {
+ }
+
+ public final class RadioButtonDefaults {
+ method public androidx.glance.appwidget.RadioButtonColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+ method public androidx.glance.appwidget.RadioButtonColors colors(long checkedColor, long uncheckedColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.RadioButtonColors colors();
+ field public static final androidx.glance.appwidget.RadioButtonDefaults INSTANCE;
+ }
+
+ public final class RadioButtonKt {
+ method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, androidx.glance.action.Action? onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+ method public static androidx.glance.GlanceModifier selectableGroup(androidx.glance.GlanceModifier);
+ }
+
+ @androidx.glance.appwidget.ExperimentalGlanceRemoteViewsApi public final class RemoteViewsCompositionResult {
+ ctor public RemoteViewsCompositionResult(android.widget.RemoteViews remoteViews);
+ method public android.widget.RemoteViews getRemoteViews();
+ property public final android.widget.RemoteViews remoteViews;
+ }
+
+ public sealed interface SizeMode {
+ }
+
+ public static final class SizeMode.Exact implements androidx.glance.appwidget.SizeMode {
+ field public static final androidx.glance.appwidget.SizeMode.Exact INSTANCE;
+ }
+
+ public static final class SizeMode.Responsive implements androidx.glance.appwidget.SizeMode {
+ ctor public SizeMode.Responsive(java.util.Set<androidx.compose.ui.unit.DpSize> sizes);
+ method public java.util.Set<androidx.compose.ui.unit.DpSize> getSizes();
+ property public final java.util.Set<androidx.compose.ui.unit.DpSize> sizes;
+ }
+
+ public static final class SizeMode.Single implements androidx.glance.appwidget.SizeMode {
+ field public static final androidx.glance.appwidget.SizeMode.Single INSTANCE;
+ }
+
+ public abstract sealed class SwitchColors {
+ }
+
+ public final class SwitchDefaults {
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors(androidx.glance.unit.ColorProvider checkedThumbColor, androidx.glance.unit.ColorProvider uncheckedThumbColor, androidx.glance.unit.ColorProvider checkedTrackColor, androidx.glance.unit.ColorProvider uncheckedTrackColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors();
+ field public static final androidx.glance.appwidget.SwitchDefaults INSTANCE;
+ }
+
+ public final class SwitchKt {
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+ }
+
+}
+
+package androidx.glance.appwidget.action {
+
+ public interface ActionCallback {
+ method public suspend Object? onAction(android.content.Context context, androidx.glance.GlanceId glanceId, androidx.glance.action.ActionParameters parameters, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class RunCallbackActionKt {
+ method public static <T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(Class<T> callbackClass, optional androidx.glance.action.ActionParameters parameters);
+ method public static inline <reified T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(optional androidx.glance.action.ActionParameters parameters);
+ }
+
+ public final class SendBroadcastActionKt {
+ method public static androidx.glance.action.Action actionSendBroadcast(String action, optional android.content.ComponentName? componentName);
+ method public static androidx.glance.action.Action actionSendBroadcast(android.content.Intent intent);
+ method public static androidx.glance.action.Action actionSendBroadcast(android.content.ComponentName componentName);
+ method public static <T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast(Class<T> receiver);
+ method public static inline <reified T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast();
+ }
+
+ public final class StartActivityIntentActionKt {
+ method public static androidx.glance.action.Action actionStartActivity(android.content.Intent intent, optional androidx.glance.action.ActionParameters parameters);
+ }
+
+ public final class StartServiceActionKt {
+ method public static androidx.glance.action.Action actionStartService(android.content.Intent intent, optional boolean isForegroundService);
+ method public static androidx.glance.action.Action actionStartService(android.content.ComponentName componentName, optional boolean isForegroundService);
+ method public static <T extends android.app.Service> androidx.glance.action.Action actionStartService(Class<T> service, optional boolean isForegroundService);
+ method public static inline <reified T extends android.app.Service> androidx.glance.action.Action actionStartService(optional boolean isForegroundService);
+ }
+
+ public final class ToggleableKt {
+ method public static androidx.glance.action.ActionParameters.Key<java.lang.Boolean> getToggleableStateKey();
+ property public static final androidx.glance.action.ActionParameters.Key<java.lang.Boolean> ToggleableStateKey;
+ }
+
+}
+
+package androidx.glance.appwidget.lazy {
+
+ public abstract sealed class GridCells {
+ }
+
+ @RequiresApi(31) public static final class GridCells.Adaptive extends androidx.glance.appwidget.lazy.GridCells {
+ ctor public GridCells.Adaptive(float minSize);
+ method public float getMinSize();
+ property public final float minSize;
+ }
+
+ public static final class GridCells.Fixed extends androidx.glance.appwidget.lazy.GridCells {
+ ctor public GridCells.Fixed(int count);
+ method public int getCount();
+ property public final int count;
+ }
+
+ @androidx.glance.appwidget.lazy.LazyScopeMarker public interface LazyItemScope {
+ }
+
+ public final class LazyListKt {
+ method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyListScope,kotlin.Unit> content);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyListScope {
+ method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+ method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ field public static final androidx.glance.appwidget.lazy.LazyListScope.Companion Companion;
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ public static final class LazyListScope.Companion {
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ @kotlin.DslMarker public @interface LazyScopeMarker {
+ }
+
+ public final class LazyVerticalGridKt {
+ method @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.glance.appwidget.lazy.GridCells gridCells, optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyVerticalGridScope,kotlin.Unit> content);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyVerticalGridScope {
+ method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+ method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ field public static final androidx.glance.appwidget.lazy.LazyVerticalGridScope.Companion Companion;
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ public static final class LazyVerticalGridScope.Companion {
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+}
+
+package androidx.glance.appwidget.state {
+
+ public final class GlanceAppWidgetStateKt {
+ method public static suspend <T> Object? getAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+ method public static suspend <T> Object? getAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+ method public static suspend <T> Object? updateAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T>);
+ method public static suspend Object? updateAppWidgetState(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super androidx.datastore.preferences.core.MutablePreferences,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> updateState, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+}
+
diff --git a/glance/glance-appwidget/api/res-1.0.0-beta01.txt b/glance/glance-appwidget/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..dba4906
--- /dev/null
+++ b/glance/glance-appwidget/api/res-1.0.0-beta01.txt
@@ -0,0 +1,2 @@
+bool glance_appwidget_available
+layout glance_default_loading_layout
diff --git a/glance/glance-appwidget/api/restricted_1.0.0-beta01.txt b/glance/glance-appwidget/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..72717d0
--- /dev/null
+++ b/glance/glance-appwidget/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,264 @@
+// Signature format: 4.0
+package androidx.glance.appwidget {
+
+ public final class AndroidRemoteViewsKt {
+ method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, optional androidx.glance.GlanceModifier modifier);
+ method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, optional androidx.glance.GlanceModifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class AppWidgetBackgroundKt {
+ method public static androidx.glance.GlanceModifier appWidgetBackground(androidx.glance.GlanceModifier);
+ }
+
+ public final class AppWidgetComposerKt {
+ method public static suspend Object? compose(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId id, optional android.os.Bundle? options, optional androidx.compose.ui.unit.DpSize? size, optional Object? state, optional kotlin.coroutines.Continuation<? super android.widget.RemoteViews>);
+ }
+
+ public final class BackgroundKt {
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long day, long night);
+ }
+
+ public abstract sealed class CheckBoxColors {
+ }
+
+ public final class CheckBoxKt {
+ method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors, optional int maxLines);
+ }
+
+ public final class CheckboxDefaults {
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors(long checkedColor, long uncheckedColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.CheckBoxColors colors();
+ field public static final androidx.glance.appwidget.CheckboxDefaults INSTANCE;
+ }
+
+ public final class CircularProgressIndicatorKt {
+ method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color);
+ }
+
+ public final class CompositionLocalsKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> getLocalAppWidgetOptions();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> LocalAppWidgetOptions;
+ }
+
+ public final class CornerRadiusKt {
+ method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, float radius);
+ method public static androidx.glance.GlanceModifier cornerRadius(androidx.glance.GlanceModifier, @DimenRes int radius);
+ }
+
+ public abstract class GlanceAppWidget {
+ ctor public GlanceAppWidget(optional @LayoutRes int errorUiLayout);
+ method public androidx.glance.appwidget.SizeMode getSizeMode();
+ method public androidx.glance.state.GlanceStateDefinition<?>? getStateDefinition();
+ method public suspend Object? onDelete(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public abstract suspend Object? provideGlance(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public final suspend Object? update(android.content.Context context, androidx.glance.GlanceId id, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public androidx.glance.appwidget.SizeMode sizeMode;
+ property public androidx.glance.state.GlanceStateDefinition<?>? stateDefinition;
+ }
+
+ public final class GlanceAppWidgetKt {
+ method public static suspend Object? provideContent(androidx.glance.appwidget.GlanceAppWidget, kotlin.jvm.functions.Function0<kotlin.Unit> content, kotlin.coroutines.Continuation<?>);
+ method public static suspend Object? updateAll(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend inline <reified State> void updateIf(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.jvm.functions.Function1<? super State,? extends java.lang.Boolean> predicate);
+ }
+
+ public final class GlanceAppWidgetManager {
+ ctor public GlanceAppWidgetManager(android.content.Context context);
+ method public int getAppWidgetId(androidx.glance.GlanceId glanceId);
+ method public suspend Object? getAppWidgetSizes(androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.compose.ui.unit.DpSize>>);
+ method public androidx.glance.GlanceId getGlanceIdBy(int appWidgetId);
+ method public androidx.glance.GlanceId? getGlanceIdBy(android.content.Intent configurationIntent);
+ method public suspend <T extends androidx.glance.appwidget.GlanceAppWidget> Object? getGlanceIds(Class<T> provider, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.glance.GlanceId>>);
+ method public suspend <T extends androidx.glance.appwidget.GlanceAppWidgetReceiver> Object? requestPinGlanceAppWidget(Class<T> receiver, optional androidx.glance.appwidget.GlanceAppWidget? preview, optional Object? previewState, optional android.app.PendingIntent? successCallback, optional kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ }
+
+ public abstract class GlanceAppWidgetReceiver extends android.appwidget.AppWidgetProvider {
+ ctor public GlanceAppWidgetReceiver();
+ method public abstract androidx.glance.appwidget.GlanceAppWidget getGlanceAppWidget();
+ property public abstract androidx.glance.appwidget.GlanceAppWidget glanceAppWidget;
+ field public static final String ACTION_DEBUG_UPDATE = "androidx.glance.appwidget.action.DEBUG_UPDATE";
+ field public static final androidx.glance.appwidget.GlanceAppWidgetReceiver.Companion Companion;
+ }
+
+ public static final class GlanceAppWidgetReceiver.Companion {
+ }
+
+ public final class ImageProvidersKt {
+ method public static androidx.glance.ImageProvider ImageProvider(android.net.Uri uri);
+ }
+
+ public final class LinearProgressIndicatorKt {
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.unit.ColorProvider color, optional androidx.glance.unit.ColorProvider backgroundColor);
+ }
+
+ public final class ProgressIndicatorDefaults {
+ method public androidx.glance.unit.ColorProvider getBackgroundColorProvider();
+ method public androidx.glance.unit.ColorProvider getIndicatorColorProvider();
+ property public final androidx.glance.unit.ColorProvider BackgroundColorProvider;
+ property public final androidx.glance.unit.ColorProvider IndicatorColorProvider;
+ field public static final androidx.glance.appwidget.ProgressIndicatorDefaults INSTANCE;
+ }
+
+ public final class RadioButtonColors {
+ }
+
+ public final class RadioButtonDefaults {
+ method public androidx.glance.appwidget.RadioButtonColors colors(androidx.glance.unit.ColorProvider checkedColor, androidx.glance.unit.ColorProvider uncheckedColor);
+ method public androidx.glance.appwidget.RadioButtonColors colors(long checkedColor, long uncheckedColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.RadioButtonColors colors();
+ field public static final androidx.glance.appwidget.RadioButtonDefaults INSTANCE;
+ }
+
+ public final class RadioButtonKt {
+ method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, androidx.glance.action.Action? onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void RadioButton(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.RadioButtonColors colors, optional int maxLines);
+ method public static androidx.glance.GlanceModifier selectableGroup(androidx.glance.GlanceModifier);
+ }
+
+ public sealed interface SizeMode {
+ }
+
+ public static final class SizeMode.Exact implements androidx.glance.appwidget.SizeMode {
+ field public static final androidx.glance.appwidget.SizeMode.Exact INSTANCE;
+ }
+
+ public static final class SizeMode.Responsive implements androidx.glance.appwidget.SizeMode {
+ ctor public SizeMode.Responsive(java.util.Set<androidx.compose.ui.unit.DpSize> sizes);
+ method public java.util.Set<androidx.compose.ui.unit.DpSize> getSizes();
+ property public final java.util.Set<androidx.compose.ui.unit.DpSize> sizes;
+ }
+
+ public static final class SizeMode.Single implements androidx.glance.appwidget.SizeMode {
+ field public static final androidx.glance.appwidget.SizeMode.Single INSTANCE;
+ }
+
+ public abstract sealed class SwitchColors {
+ }
+
+ public final class SwitchDefaults {
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors(androidx.glance.unit.ColorProvider checkedThumbColor, androidx.glance.unit.ColorProvider uncheckedThumbColor, androidx.glance.unit.ColorProvider checkedTrackColor, androidx.glance.unit.ColorProvider uncheckedTrackColor);
+ method @androidx.compose.runtime.Composable public androidx.glance.appwidget.SwitchColors colors();
+ field public static final androidx.glance.appwidget.SwitchDefaults INSTANCE;
+ }
+
+ public final class SwitchKt {
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function0<kotlin.Unit> onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors, optional int maxLines);
+ }
+
+}
+
+package androidx.glance.appwidget.action {
+
+ public interface ActionCallback {
+ method public suspend Object? onAction(android.content.Context context, androidx.glance.GlanceId glanceId, androidx.glance.action.ActionParameters parameters, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+ public final class RunCallbackActionKt {
+ method public static <T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(Class<T> callbackClass, optional androidx.glance.action.ActionParameters parameters);
+ method public static inline <reified T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action actionRunCallback(optional androidx.glance.action.ActionParameters parameters);
+ }
+
+ public final class SendBroadcastActionKt {
+ method public static androidx.glance.action.Action actionSendBroadcast(String action, optional android.content.ComponentName? componentName);
+ method public static androidx.glance.action.Action actionSendBroadcast(android.content.Intent intent);
+ method public static androidx.glance.action.Action actionSendBroadcast(android.content.ComponentName componentName);
+ method public static <T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast(Class<T> receiver);
+ method public static inline <reified T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionSendBroadcast();
+ }
+
+ public final class StartActivityIntentActionKt {
+ method public static androidx.glance.action.Action actionStartActivity(android.content.Intent intent, optional androidx.glance.action.ActionParameters parameters);
+ }
+
+ public final class StartServiceActionKt {
+ method public static androidx.glance.action.Action actionStartService(android.content.Intent intent, optional boolean isForegroundService);
+ method public static androidx.glance.action.Action actionStartService(android.content.ComponentName componentName, optional boolean isForegroundService);
+ method public static <T extends android.app.Service> androidx.glance.action.Action actionStartService(Class<T> service, optional boolean isForegroundService);
+ method public static inline <reified T extends android.app.Service> androidx.glance.action.Action actionStartService(optional boolean isForegroundService);
+ }
+
+ public final class ToggleableKt {
+ method public static androidx.glance.action.ActionParameters.Key<java.lang.Boolean> getToggleableStateKey();
+ property public static final androidx.glance.action.ActionParameters.Key<java.lang.Boolean> ToggleableStateKey;
+ }
+
+}
+
+package androidx.glance.appwidget.lazy {
+
+ public abstract sealed class GridCells {
+ }
+
+ @RequiresApi(31) public static final class GridCells.Adaptive extends androidx.glance.appwidget.lazy.GridCells {
+ ctor public GridCells.Adaptive(float minSize);
+ method public float getMinSize();
+ property public final float minSize;
+ }
+
+ public static final class GridCells.Fixed extends androidx.glance.appwidget.lazy.GridCells {
+ ctor public GridCells.Fixed(int count);
+ method public int getCount();
+ property public final int count;
+ }
+
+ @androidx.glance.appwidget.lazy.LazyScopeMarker public interface LazyItemScope {
+ }
+
+ public final class LazyListKt {
+ method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyListScope,kotlin.Unit> content);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyListScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyListScope {
+ method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+ method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ field public static final androidx.glance.appwidget.lazy.LazyListScope.Companion Companion;
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ public static final class LazyListScope.Companion {
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ @kotlin.DslMarker public @interface LazyScopeMarker {
+ }
+
+ public final class LazyVerticalGridKt {
+ method @androidx.compose.runtime.Composable public static void LazyVerticalGrid(androidx.glance.appwidget.lazy.GridCells gridCells, optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyVerticalGridScope,kotlin.Unit> content);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void items(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function1<? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ method public static inline <T> void itemsIndexed(androidx.glance.appwidget.lazy.LazyVerticalGridScope, T![] items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,java.lang.Long> itemId, kotlin.jvm.functions.Function3<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+ }
+
+ @androidx.glance.appwidget.lazy.LazyScopeMarker @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyVerticalGridScope {
+ method public void item(optional long itemId, kotlin.jvm.functions.Function1<? super androidx.glance.appwidget.lazy.LazyItemScope,kotlin.Unit> content);
+ method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Long> itemId, kotlin.jvm.functions.Function2<? super androidx.glance.appwidget.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+ field public static final androidx.glance.appwidget.lazy.LazyVerticalGridScope.Companion Companion;
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+ public static final class LazyVerticalGridScope.Companion {
+ field public static final long UnspecifiedItemId = -9223372036854775808L; // 0x8000000000000000L
+ }
+
+}
+
+package androidx.glance.appwidget.state {
+
+ public final class GlanceAppWidgetStateKt {
+ method public static suspend <T> Object? getAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+ method public static suspend <T> Object? getAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T>);
+ method public static suspend <T> Object? updateAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T>);
+ method public static suspend Object? updateAppWidgetState(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super androidx.datastore.preferences.core.MutablePreferences,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> updateState, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ }
+
+}
+
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index 3eaf94a..158c764 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -36,10 +36,7 @@
AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project, /* isMultiplatformEnabled= */ false)
dependencies {
- bundleInside(project(
- path: ":glance:glance-appwidget-proto",
- configuration: "export"
- ))
+ bundleInside(project(path: ":glance:glance-appwidget-proto", configuration: "export"))
api(project(":glance:glance"))
api("androidx.annotation:annotation:1.1.0")
@@ -73,6 +70,7 @@
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 2430d3f..01b903d 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,6 +36,7 @@
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
@@ -111,6 +112,8 @@
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-material/api/1.0.0-beta01.txt b/glance/glance-material/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..25d2aeb
--- /dev/null
+++ b/glance/glance-material/api/1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material {
+
+ public final class MaterialThemesKt {
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors light, androidx.compose.material.Colors dark);
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors colors);
+ }
+
+}
+
diff --git a/glance/glance-material/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-material/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..25d2aeb
--- /dev/null
+++ b/glance/glance-material/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material {
+
+ public final class MaterialThemesKt {
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors light, androidx.compose.material.Colors dark);
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors colors);
+ }
+
+}
+
diff --git a/glance/glance-material/api/res-1.0.0-beta01.txt b/glance/glance-material/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-material/api/res-1.0.0-beta01.txt
diff --git a/glance/glance-material/api/restricted_1.0.0-beta01.txt b/glance/glance-material/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..25d2aeb
--- /dev/null
+++ b/glance/glance-material/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material {
+
+ public final class MaterialThemesKt {
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors light, androidx.compose.material.Colors dark);
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material.Colors colors);
+ }
+
+}
+
diff --git a/glance/glance-material3/api/1.0.0-beta01.txt b/glance/glance-material3/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..8ff1aa6
--- /dev/null
+++ b/glance/glance-material3/api/1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material3 {
+
+ public final class Material3ThemesKt {
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme light, androidx.compose.material3.ColorScheme dark);
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme scheme);
+ }
+
+}
+
diff --git a/glance/glance-material3/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-material3/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..8ff1aa6
--- /dev/null
+++ b/glance/glance-material3/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material3 {
+
+ public final class Material3ThemesKt {
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme light, androidx.compose.material3.ColorScheme dark);
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme scheme);
+ }
+
+}
+
diff --git a/glance/glance-material3/api/res-1.0.0-beta01.txt b/glance/glance-material3/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-material3/api/res-1.0.0-beta01.txt
diff --git a/glance/glance-material3/api/restricted_1.0.0-beta01.txt b/glance/glance-material3/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..8ff1aa6
--- /dev/null
+++ b/glance/glance-material3/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,10 @@
+// Signature format: 4.0
+package androidx.glance.material3 {
+
+ public final class Material3ThemesKt {
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme light, androidx.compose.material3.ColorScheme dark);
+ method public static androidx.glance.color.ColorProviders ColorProviders(androidx.compose.material3.ColorScheme scheme);
+ }
+
+}
+
diff --git a/glance/glance-preview/api/1.0.0-beta01.txt b/glance/glance-preview/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-preview/api/1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-preview/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance-preview/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..1fc4543
--- /dev/null
+++ b/glance/glance-preview/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.glance.preview {
+
+ @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGlancePreviewApi {
+ }
+
+ @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface GlancePreview {
+ method public abstract String surface();
+ property public abstract String surface;
+ }
+
+ @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.SOURCE) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public static @interface GlancePreview.Container {
+ method public abstract androidx.glance.preview.GlancePreview[] value();
+ }
+
+ @androidx.glance.preview.ExperimentalGlancePreviewApi public final class Surfaces {
+ field public static final String APP_WIDGET = "AppWidget";
+ field public static final androidx.glance.preview.Surfaces INSTANCE;
+ field public static final String TILE = "Tile";
+ }
+
+}
+
diff --git a/glance/glance-preview/api/res-1.0.0-beta01.txt b/glance/glance-preview/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance-preview/api/res-1.0.0-beta01.txt
diff --git a/glance/glance-preview/api/restricted_1.0.0-beta01.txt b/glance/glance-preview/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-preview/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-preview/build.gradle b/glance/glance-preview/build.gradle
index 61462cc..844dcee 100644
--- a/glance/glance-preview/build.gradle
+++ b/glance/glance-preview/build.gradle
@@ -26,6 +26,7 @@
androidx {
name = "Android Glance Preview"
type = LibraryType.PUBLISHED_LIBRARY
+ mavenVersion = LibraryVersions.GLANCE_PREVIEW
inceptionYear = "2022"
description = "Glance preview library. This library provides the API required for marking the" +
"glance @Composable components that should have preview in the Android Studio."
diff --git a/glance/glance-template/build.gradle b/glance/glance-template/build.gradle
index 1fd8868..a74f078 100644
--- a/glance/glance-template/build.gradle
+++ b/glance/glance-template/build.gradle
@@ -42,7 +42,6 @@
implementation("androidx.work:work-runtime:2.7.1")
implementation("androidx.work:work-runtime-ktx:2.7.1")
implementation(libs.kotlinStdlib)
- implementation(project(":compose:runtime:runtime"))
// Force upgrade since 1.2.0 is not compatible with latest lint.
implementation("androidx.annotation:annotation-experimental:1.3.0")
@@ -76,6 +75,7 @@
androidx {
name = "Glance Templates Library"
type = LibraryType.PUBLISHED_LIBRARY
+ mavenVersion = LibraryVersions.GLANCE_TEMPLATE
inceptionYear = "2021"
description = "Glance allows developers to build layouts for remote surfaces using a Jetpack " +
"Compose-style API."
diff --git a/glance/glance-wear-tiles-preview/build.gradle b/glance/glance-wear-tiles-preview/build.gradle
index 1736a49..a5836b4 100644
--- a/glance/glance-wear-tiles-preview/build.gradle
+++ b/glance/glance-wear-tiles-preview/build.gradle
@@ -53,6 +53,7 @@
androidx {
name = "Android Glance Wear Tiles Preview"
type = LibraryType.PUBLISHED_LIBRARY
+ mavenVersion = LibraryVersions.GLANCE_WEAR_TILES
inceptionYear = "2022"
description = "Glance tooling library. This library provides the API required for the " +
"GlanceTileService components and its Glance @Composable to be previewable in the IDE."
diff --git a/glance/glance-wear-tiles/build.gradle b/glance/glance-wear-tiles/build.gradle
index bcfb47e..155bfab 100644
--- a/glance/glance-wear-tiles/build.gradle
+++ b/glance/glance-wear-tiles/build.gradle
@@ -38,9 +38,8 @@
implementation(libs.kotlinStdlib)
implementation(libs.kotlinCoroutinesGuava)
- implementation(projectOrArtifact(":lifecycle:lifecycle-runtime"))
- implementation(projectOrArtifact(":lifecycle:lifecycle-runtime-ktx"))
- implementation(projectOrArtifact(":lifecycle:lifecycle-service"))
+ implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
+ implementation("androidx.lifecycle:lifecycle-service:2.6.1")
testImplementation(libs.testCore)
testImplementation(libs.testRules)
@@ -92,6 +91,7 @@
androidx {
name = "Glance for Wear Tiles"
type = LibraryType.PUBLISHED_LIBRARY
+ mavenVersion = LibraryVersions.GLANCE_WEAR_TILES
inceptionYear = "2021"
description = "Glance allows developers to build layouts for Wear Tiles using a Jetpack " +
"Compose-style API."
diff --git a/glance/glance/api/1.0.0-beta01.txt b/glance/glance/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..47d790f
--- /dev/null
+++ b/glance/glance/api/1.0.0-beta01.txt
@@ -0,0 +1,569 @@
+// Signature format: 4.0
+package androidx.glance {
+
+ public final class BackgroundKt {
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long color);
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, @ColorRes int color);
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.unit.ColorProvider colorProvider);
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.ImageProvider imageProvider, optional int contentScale);
+ }
+
+ public final class ButtonColors {
+ method public androidx.glance.unit.ColorProvider getBackgroundColor();
+ method public androidx.glance.unit.ColorProvider getContentColor();
+ property public final androidx.glance.unit.ColorProvider backgroundColor;
+ property public final androidx.glance.unit.ColorProvider contentColor;
+ }
+
+ public final class ButtonDefaults {
+ method @androidx.compose.runtime.Composable public androidx.glance.ButtonColors buttonColors(optional androidx.glance.unit.ColorProvider backgroundColor, optional androidx.glance.unit.ColorProvider contentColor);
+ field public static final androidx.glance.ButtonDefaults INSTANCE;
+ }
+
+ public final class ButtonKt {
+ method @androidx.compose.runtime.Composable public static void Button(String text, androidx.glance.action.Action onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void Button(String text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+ }
+
+ public final class ColorFilter {
+ field public static final androidx.glance.ColorFilter.Companion Companion;
+ }
+
+ public static final class ColorFilter.Companion {
+ method public androidx.glance.ColorFilter tint(androidx.glance.unit.ColorProvider colorProvider);
+ }
+
+ public final class CombinedGlanceModifier implements androidx.glance.GlanceModifier {
+ ctor public CombinedGlanceModifier(androidx.glance.GlanceModifier outer, androidx.glance.GlanceModifier inner);
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ }
+
+ public final class CompositionLocalsKt {
+ method @androidx.compose.runtime.Composable public static inline <reified T> T currentState();
+ method @androidx.compose.runtime.Composable public static inline <reified T> T? currentState(androidx.datastore.preferences.core.Preferences.Key<T> key);
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> getLocalGlanceId();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> getLocalSize();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> getLocalState();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> LocalContext;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> LocalGlanceId;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> LocalSize;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> LocalState;
+ }
+
+ @androidx.compose.runtime.ComposableTargetMarker(description="Glance Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface GlanceComposable {
+ }
+
+ public interface GlanceId {
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface GlanceModifier {
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ method public default infix androidx.glance.GlanceModifier then(androidx.glance.GlanceModifier other);
+ field public static final androidx.glance.GlanceModifier.Companion Companion;
+ }
+
+ public static final class GlanceModifier.Companion implements androidx.glance.GlanceModifier {
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public static interface GlanceModifier.Element extends androidx.glance.GlanceModifier {
+ method public default boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public default boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public default <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public default <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ }
+
+ public final class GlanceTheme {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public androidx.glance.color.ColorProviders getColors();
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public final androidx.glance.color.ColorProviders colors;
+ field public static final androidx.glance.GlanceTheme INSTANCE;
+ }
+
+ public final class GlanceThemeKt {
+ method @androidx.compose.runtime.Composable public static void GlanceTheme(optional androidx.glance.color.ColorProviders colors, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class ImageKt {
+ method @androidx.compose.runtime.Composable public static void Image(androidx.glance.ImageProvider provider, String? contentDescription, optional androidx.glance.GlanceModifier modifier, optional int contentScale, optional androidx.glance.ColorFilter? colorFilter);
+ method public static androidx.glance.ImageProvider ImageProvider(@DrawableRes int resId);
+ method public static androidx.glance.ImageProvider ImageProvider(android.graphics.Bitmap bitmap);
+ method @RequiresApi(android.os.Build.VERSION_CODES.M) public static androidx.glance.ImageProvider ImageProvider(android.graphics.drawable.Icon icon);
+ }
+
+ public interface ImageProvider {
+ }
+
+ public enum Visibility {
+ method public static androidx.glance.Visibility valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.glance.Visibility[] values();
+ enum_constant public static final androidx.glance.Visibility Gone;
+ enum_constant public static final androidx.glance.Visibility Invisible;
+ enum_constant public static final androidx.glance.Visibility Visible;
+ }
+
+ public final class VisibilityKt {
+ method public static androidx.glance.GlanceModifier visibility(androidx.glance.GlanceModifier, androidx.glance.Visibility visibility);
+ }
+
+}
+
+package androidx.glance.action {
+
+ public interface Action {
+ }
+
+ public final class ActionKt {
+ method public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, androidx.glance.action.Action onClick);
+ method @androidx.compose.runtime.Composable public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ }
+
+ public abstract class ActionParameters {
+ method public abstract java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+ method public abstract operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+ method public abstract operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+ method public abstract <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+ method public abstract boolean isEmpty();
+ }
+
+ public static final class ActionParameters.Key<T> {
+ ctor public ActionParameters.Key(String name);
+ method public String getName();
+ method public infix androidx.glance.action.ActionParameters.Pair<T> to(T value);
+ property public final String name;
+ }
+
+ public static final class ActionParameters.Pair<T> {
+ }
+
+ public final class ActionParametersKt {
+ method public static androidx.glance.action.ActionParameters actionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+ method public static androidx.glance.action.MutableActionParameters mutableActionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+ method public static androidx.glance.action.MutableActionParameters toMutableParameters(androidx.glance.action.ActionParameters);
+ method public static androidx.glance.action.ActionParameters toParameters(androidx.glance.action.ActionParameters);
+ method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
+ }
+
+ public final class LambdaActionKt {
+ method @androidx.compose.runtime.Composable public static androidx.glance.action.Action action(optional String? key, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ }
+
+ public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
+ method public java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+ method public void clear();
+ method public operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+ method public operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+ method public <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+ method public boolean isEmpty();
+ method public <T> T? remove(androidx.glance.action.ActionParameters.Key<T> key);
+ method public operator <T> T? set(androidx.glance.action.ActionParameters.Key<T> key, T? value);
+ }
+
+ public final class StartActivityActionKt {
+ method public static androidx.glance.action.Action actionStartActivity(android.content.ComponentName componentName, optional androidx.glance.action.ActionParameters parameters);
+ method public static <T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(Class<T> activity, optional androidx.glance.action.ActionParameters parameters);
+ method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(optional androidx.glance.action.ActionParameters parameters);
+ }
+
+}
+
+package androidx.glance.color {
+
+ public abstract sealed class ColorProviders {
+ method public final androidx.glance.unit.ColorProvider getBackground();
+ method public final androidx.glance.unit.ColorProvider getError();
+ method public final androidx.glance.unit.ColorProvider getErrorContainer();
+ method public final androidx.glance.unit.ColorProvider getInverseOnSurface();
+ method public final androidx.glance.unit.ColorProvider getInversePrimary();
+ method public final androidx.glance.unit.ColorProvider getInverseSurface();
+ method public final androidx.glance.unit.ColorProvider getOnBackground();
+ method public final androidx.glance.unit.ColorProvider getOnError();
+ method public final androidx.glance.unit.ColorProvider getOnErrorContainer();
+ method public final androidx.glance.unit.ColorProvider getOnPrimary();
+ method public final androidx.glance.unit.ColorProvider getOnPrimaryContainer();
+ method public final androidx.glance.unit.ColorProvider getOnSecondary();
+ method public final androidx.glance.unit.ColorProvider getOnSecondaryContainer();
+ method public final androidx.glance.unit.ColorProvider getOnSurface();
+ method public final androidx.glance.unit.ColorProvider getOnSurfaceVariant();
+ method public final androidx.glance.unit.ColorProvider getOnTertiary();
+ method public final androidx.glance.unit.ColorProvider getOnTertiaryContainer();
+ method public final androidx.glance.unit.ColorProvider getOutline();
+ method public final androidx.glance.unit.ColorProvider getPrimary();
+ method public final androidx.glance.unit.ColorProvider getPrimaryContainer();
+ method public final androidx.glance.unit.ColorProvider getSecondary();
+ method public final androidx.glance.unit.ColorProvider getSecondaryContainer();
+ method public final androidx.glance.unit.ColorProvider getSurface();
+ method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
+ method public final androidx.glance.unit.ColorProvider getTertiary();
+ method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
+ property public final androidx.glance.unit.ColorProvider background;
+ property public final androidx.glance.unit.ColorProvider error;
+ property public final androidx.glance.unit.ColorProvider errorContainer;
+ property public final androidx.glance.unit.ColorProvider inverseOnSurface;
+ property public final androidx.glance.unit.ColorProvider inversePrimary;
+ property public final androidx.glance.unit.ColorProvider inverseSurface;
+ property public final androidx.glance.unit.ColorProvider onBackground;
+ property public final androidx.glance.unit.ColorProvider onError;
+ property public final androidx.glance.unit.ColorProvider onErrorContainer;
+ property public final androidx.glance.unit.ColorProvider onPrimary;
+ property public final androidx.glance.unit.ColorProvider onPrimaryContainer;
+ property public final androidx.glance.unit.ColorProvider onSecondary;
+ property public final androidx.glance.unit.ColorProvider onSecondaryContainer;
+ property public final androidx.glance.unit.ColorProvider onSurface;
+ property public final androidx.glance.unit.ColorProvider onSurfaceVariant;
+ property public final androidx.glance.unit.ColorProvider onTertiary;
+ property public final androidx.glance.unit.ColorProvider onTertiaryContainer;
+ property public final androidx.glance.unit.ColorProvider outline;
+ property public final androidx.glance.unit.ColorProvider primary;
+ property public final androidx.glance.unit.ColorProvider primaryContainer;
+ property public final androidx.glance.unit.ColorProvider secondary;
+ property public final androidx.glance.unit.ColorProvider secondaryContainer;
+ property public final androidx.glance.unit.ColorProvider surface;
+ property public final androidx.glance.unit.ColorProvider surfaceVariant;
+ property public final androidx.glance.unit.ColorProvider tertiary;
+ property public final androidx.glance.unit.ColorProvider tertiaryContainer;
+ }
+
+ public final class ColorProvidersKt {
+ method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
+ }
+
+ public final class DayNightColorProvidersKt {
+ method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
+ }
+
+}
+
+package androidx.glance.layout {
+
+ public final class Alignment {
+ ctor public Alignment(int horizontal, int vertical);
+ method public int getHorizontal();
+ method public int getVertical();
+ property public final int horizontal;
+ property public final int vertical;
+ field public static final androidx.glance.layout.Alignment.Companion Companion;
+ }
+
+ public static final class Alignment.Companion {
+ method public int getBottom();
+ method public androidx.glance.layout.Alignment getBottomCenter();
+ method public androidx.glance.layout.Alignment getBottomEnd();
+ method public androidx.glance.layout.Alignment getBottomStart();
+ method public androidx.glance.layout.Alignment getCenter();
+ method public androidx.glance.layout.Alignment getCenterEnd();
+ method public int getCenterHorizontally();
+ method public androidx.glance.layout.Alignment getCenterStart();
+ method public int getCenterVertically();
+ method public int getEnd();
+ method public int getStart();
+ method public int getTop();
+ method public androidx.glance.layout.Alignment getTopCenter();
+ method public androidx.glance.layout.Alignment getTopEnd();
+ method public androidx.glance.layout.Alignment getTopStart();
+ property public final int Bottom;
+ property public final androidx.glance.layout.Alignment BottomCenter;
+ property public final androidx.glance.layout.Alignment BottomEnd;
+ property public final androidx.glance.layout.Alignment BottomStart;
+ property public final androidx.glance.layout.Alignment Center;
+ property public final androidx.glance.layout.Alignment CenterEnd;
+ property public final int CenterHorizontally;
+ property public final androidx.glance.layout.Alignment CenterStart;
+ property public final int CenterVertically;
+ property public final int End;
+ property public final int Start;
+ property public final int Top;
+ property public final androidx.glance.layout.Alignment TopCenter;
+ property public final androidx.glance.layout.Alignment TopEnd;
+ property public final androidx.glance.layout.Alignment TopStart;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class Alignment.Horizontal {
+ field public static final androidx.glance.layout.Alignment.Horizontal.Companion Companion;
+ }
+
+ public static final class Alignment.Horizontal.Companion {
+ method public int getCenterHorizontally();
+ method public int getEnd();
+ method public int getStart();
+ property public final int CenterHorizontally;
+ property public final int End;
+ property public final int Start;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class Alignment.Vertical {
+ field public static final androidx.glance.layout.Alignment.Vertical.Companion Companion;
+ }
+
+ public static final class Alignment.Vertical.Companion {
+ method public int getBottom();
+ method public int getCenterVertically();
+ method public int getTop();
+ property public final int Bottom;
+ property public final int CenterVertically;
+ property public final int Top;
+ }
+
+ public final class BoxKt {
+ method @androidx.compose.runtime.Composable public static void Box(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.layout.Alignment contentAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class ColumnKt {
+ method @androidx.compose.runtime.Composable public static void Column(optional androidx.glance.GlanceModifier modifier, optional int verticalAlignment, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.ColumnScope,kotlin.Unit> content);
+ }
+
+ public interface ColumnScope {
+ method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+ }
+
+ @kotlin.jvm.JvmInline public final value class ContentScale {
+ ctor public ContentScale(int value);
+ field public static final androidx.glance.layout.ContentScale.Companion Companion;
+ }
+
+ public static final class ContentScale.Companion {
+ method public int getCrop();
+ method public int getFillBounds();
+ method public int getFit();
+ property public final int Crop;
+ property public final int FillBounds;
+ property public final int Fit;
+ }
+
+ public final class PaddingKt {
+ method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional float left, optional float top, optional float right, optional float bottom);
+ method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional @DimenRes int left, optional @DimenRes int top, optional @DimenRes int right, optional @DimenRes int bottom);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float start, optional float top, optional float end, optional float bottom);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int start, optional @DimenRes int top, optional @DimenRes int end, optional @DimenRes int bottom);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float horizontal, optional float vertical);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int horizontal, optional @DimenRes int vertical);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, float all);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, @DimenRes int all);
+ }
+
+ public final class RowKt {
+ method @androidx.compose.runtime.Composable public static void Row(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, optional int verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.RowScope,kotlin.Unit> content);
+ }
+
+ public interface RowScope {
+ method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+ }
+
+ public final class SizeModifiersKt {
+ method public static androidx.glance.GlanceModifier fillMaxHeight(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier fillMaxSize(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier fillMaxWidth(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, float height);
+ method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, @DimenRes int height);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float size);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int size);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float width, float height);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int width, @DimenRes int height);
+ method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, float width);
+ method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, @DimenRes int width);
+ method public static androidx.glance.GlanceModifier wrapContentHeight(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier wrapContentSize(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier wrapContentWidth(androidx.glance.GlanceModifier);
+ }
+
+ public final class SpacerKt {
+ method @androidx.compose.runtime.Composable public static void Spacer(optional androidx.glance.GlanceModifier modifier);
+ }
+
+}
+
+package androidx.glance.semantics {
+
+ public final class SemanticsConfiguration implements androidx.glance.semantics.SemanticsPropertyReceiver {
+ ctor public SemanticsConfiguration();
+ method public operator <T> T get(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+ method public <T> T? getOrElseNullable(androidx.glance.semantics.SemanticsPropertyKey<T> key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method public <T> T? getOrNull(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+ method public <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+ }
+
+ public final class SemanticsModifierKt {
+ method public static androidx.glance.GlanceModifier semantics(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function1<? super androidx.glance.semantics.SemanticsPropertyReceiver,kotlin.Unit> properties);
+ }
+
+ public final class SemanticsProperties {
+ method public androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> getContentDescription();
+ property public final androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> ContentDescription;
+ field public static final androidx.glance.semantics.SemanticsProperties INSTANCE;
+ }
+
+ public final class SemanticsPropertiesKt {
+ method public static String getContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver);
+ method public static void setContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver, String);
+ }
+
+ public final class SemanticsPropertyKey<T> {
+ ctor public SemanticsPropertyKey(String name, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends T> mergePolicy);
+ method public String getName();
+ method public T? merge(T? parentValue, T childValue);
+ property public final String name;
+ }
+
+ public interface SemanticsPropertyReceiver {
+ method public operator <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+ }
+
+}
+
+package androidx.glance.session {
+
+ public final class SessionManagerKt {
+ }
+
+}
+
+package androidx.glance.state {
+
+ public interface GlanceStateDefinition<T> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>>);
+ method public java.io.File getLocation(android.content.Context context, String fileKey);
+ }
+
+ public final class PreferencesGlanceStateDefinition implements androidx.glance.state.GlanceStateDefinition<androidx.datastore.preferences.core.Preferences> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>>);
+ method public java.io.File getLocation(android.content.Context context, String fileKey);
+ field public static final androidx.glance.state.PreferencesGlanceStateDefinition INSTANCE;
+ }
+
+}
+
+package androidx.glance.text {
+
+ public final class FontFamily {
+ ctor public FontFamily(String family);
+ method public String getFamily();
+ property public final String family;
+ field public static final androidx.glance.text.FontFamily.Companion Companion;
+ }
+
+ public static final class FontFamily.Companion {
+ method public androidx.glance.text.FontFamily getCursive();
+ method public androidx.glance.text.FontFamily getMonospace();
+ method public androidx.glance.text.FontFamily getSansSerif();
+ method public androidx.glance.text.FontFamily getSerif();
+ property public final androidx.glance.text.FontFamily Cursive;
+ property public final androidx.glance.text.FontFamily Monospace;
+ property public final androidx.glance.text.FontFamily SansSerif;
+ property public final androidx.glance.text.FontFamily Serif;
+ }
+
+ @kotlin.jvm.JvmInline public final value class FontStyle {
+ field public static final androidx.glance.text.FontStyle.Companion Companion;
+ }
+
+ public static final class FontStyle.Companion {
+ method public int getItalic();
+ method public int getNormal();
+ method public java.util.List<androidx.glance.text.FontStyle> values();
+ property public final int Italic;
+ property public final int Normal;
+ }
+
+ @kotlin.jvm.JvmInline public final value class FontWeight {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.glance.text.FontWeight.Companion Companion;
+ }
+
+ public static final class FontWeight.Companion {
+ method public int getBold();
+ method public int getMedium();
+ method public int getNormal();
+ property public final int Bold;
+ property public final int Medium;
+ property public final int Normal;
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextAlign {
+ field public static final androidx.glance.text.TextAlign.Companion Companion;
+ }
+
+ public static final class TextAlign.Companion {
+ method public int getCenter();
+ method public int getEnd();
+ method public int getLeft();
+ method public int getRight();
+ method public int getStart();
+ method public java.util.List<androidx.glance.text.TextAlign> values();
+ property public final int Center;
+ property public final int End;
+ property public final int Left;
+ property public final int Right;
+ property public final int Start;
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextDecoration {
+ method @androidx.compose.runtime.Stable public operator boolean contains(int other);
+ method @androidx.compose.runtime.Stable public operator int plus(int decoration);
+ field public static final androidx.glance.text.TextDecoration.Companion Companion;
+ }
+
+ public static final class TextDecoration.Companion {
+ method public int combine(java.util.List<androidx.glance.text.TextDecoration> decorations);
+ method public int getLineThrough();
+ method public int getNone();
+ method public int getUnderline();
+ property public final int LineThrough;
+ property public final int None;
+ property public final int Underline;
+ }
+
+ public final class TextDefaults {
+ method public androidx.glance.unit.ColorProvider getDefaultTextColor();
+ method public androidx.glance.text.TextStyle getDefaultTextStyle();
+ property public final androidx.glance.unit.ColorProvider defaultTextColor;
+ property public final androidx.glance.text.TextStyle defaultTextStyle;
+ field public static final androidx.glance.text.TextDefaults INSTANCE;
+ }
+
+ public final class TextKt {
+ method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.text.TextStyle style, optional int maxLines);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextStyle {
+ ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+ method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+ method public androidx.glance.unit.ColorProvider getColor();
+ method public androidx.glance.text.FontFamily? getFontFamily();
+ method public androidx.compose.ui.unit.TextUnit? getFontSize();
+ method public androidx.glance.text.FontStyle? getFontStyle();
+ method public androidx.glance.text.FontWeight? getFontWeight();
+ method public androidx.glance.text.TextAlign? getTextAlign();
+ method public androidx.glance.text.TextDecoration? getTextDecoration();
+ property public final androidx.glance.unit.ColorProvider color;
+ property public final androidx.glance.text.FontFamily? fontFamily;
+ property public final androidx.compose.ui.unit.TextUnit? fontSize;
+ property public final androidx.glance.text.FontStyle? fontStyle;
+ property public final androidx.glance.text.FontWeight? fontWeight;
+ property public final androidx.glance.text.TextAlign? textAlign;
+ property public final androidx.glance.text.TextDecoration? textDecoration;
+ }
+
+}
+
+package androidx.glance.unit {
+
+ public interface ColorProvider {
+ method public long getColor(android.content.Context context);
+ }
+
+ public final class ColorProviderKt {
+ method public static androidx.glance.unit.ColorProvider ColorProvider(long color);
+ }
+
+}
+
diff --git a/glance/glance/api/public_plus_experimental_1.0.0-beta01.txt b/glance/glance/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..47d790f
--- /dev/null
+++ b/glance/glance/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,569 @@
+// Signature format: 4.0
+package androidx.glance {
+
+ public final class BackgroundKt {
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long color);
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, @ColorRes int color);
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.unit.ColorProvider colorProvider);
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.ImageProvider imageProvider, optional int contentScale);
+ }
+
+ public final class ButtonColors {
+ method public androidx.glance.unit.ColorProvider getBackgroundColor();
+ method public androidx.glance.unit.ColorProvider getContentColor();
+ property public final androidx.glance.unit.ColorProvider backgroundColor;
+ property public final androidx.glance.unit.ColorProvider contentColor;
+ }
+
+ public final class ButtonDefaults {
+ method @androidx.compose.runtime.Composable public androidx.glance.ButtonColors buttonColors(optional androidx.glance.unit.ColorProvider backgroundColor, optional androidx.glance.unit.ColorProvider contentColor);
+ field public static final androidx.glance.ButtonDefaults INSTANCE;
+ }
+
+ public final class ButtonKt {
+ method @androidx.compose.runtime.Composable public static void Button(String text, androidx.glance.action.Action onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void Button(String text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+ }
+
+ public final class ColorFilter {
+ field public static final androidx.glance.ColorFilter.Companion Companion;
+ }
+
+ public static final class ColorFilter.Companion {
+ method public androidx.glance.ColorFilter tint(androidx.glance.unit.ColorProvider colorProvider);
+ }
+
+ public final class CombinedGlanceModifier implements androidx.glance.GlanceModifier {
+ ctor public CombinedGlanceModifier(androidx.glance.GlanceModifier outer, androidx.glance.GlanceModifier inner);
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ }
+
+ public final class CompositionLocalsKt {
+ method @androidx.compose.runtime.Composable public static inline <reified T> T currentState();
+ method @androidx.compose.runtime.Composable public static inline <reified T> T? currentState(androidx.datastore.preferences.core.Preferences.Key<T> key);
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> getLocalGlanceId();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> getLocalSize();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> getLocalState();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> LocalContext;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> LocalGlanceId;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> LocalSize;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> LocalState;
+ }
+
+ @androidx.compose.runtime.ComposableTargetMarker(description="Glance Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface GlanceComposable {
+ }
+
+ public interface GlanceId {
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface GlanceModifier {
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ method public default infix androidx.glance.GlanceModifier then(androidx.glance.GlanceModifier other);
+ field public static final androidx.glance.GlanceModifier.Companion Companion;
+ }
+
+ public static final class GlanceModifier.Companion implements androidx.glance.GlanceModifier {
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public static interface GlanceModifier.Element extends androidx.glance.GlanceModifier {
+ method public default boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public default boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public default <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public default <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ }
+
+ public final class GlanceTheme {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public androidx.glance.color.ColorProviders getColors();
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public final androidx.glance.color.ColorProviders colors;
+ field public static final androidx.glance.GlanceTheme INSTANCE;
+ }
+
+ public final class GlanceThemeKt {
+ method @androidx.compose.runtime.Composable public static void GlanceTheme(optional androidx.glance.color.ColorProviders colors, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class ImageKt {
+ method @androidx.compose.runtime.Composable public static void Image(androidx.glance.ImageProvider provider, String? contentDescription, optional androidx.glance.GlanceModifier modifier, optional int contentScale, optional androidx.glance.ColorFilter? colorFilter);
+ method public static androidx.glance.ImageProvider ImageProvider(@DrawableRes int resId);
+ method public static androidx.glance.ImageProvider ImageProvider(android.graphics.Bitmap bitmap);
+ method @RequiresApi(android.os.Build.VERSION_CODES.M) public static androidx.glance.ImageProvider ImageProvider(android.graphics.drawable.Icon icon);
+ }
+
+ public interface ImageProvider {
+ }
+
+ public enum Visibility {
+ method public static androidx.glance.Visibility valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.glance.Visibility[] values();
+ enum_constant public static final androidx.glance.Visibility Gone;
+ enum_constant public static final androidx.glance.Visibility Invisible;
+ enum_constant public static final androidx.glance.Visibility Visible;
+ }
+
+ public final class VisibilityKt {
+ method public static androidx.glance.GlanceModifier visibility(androidx.glance.GlanceModifier, androidx.glance.Visibility visibility);
+ }
+
+}
+
+package androidx.glance.action {
+
+ public interface Action {
+ }
+
+ public final class ActionKt {
+ method public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, androidx.glance.action.Action onClick);
+ method @androidx.compose.runtime.Composable public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ }
+
+ public abstract class ActionParameters {
+ method public abstract java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+ method public abstract operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+ method public abstract operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+ method public abstract <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+ method public abstract boolean isEmpty();
+ }
+
+ public static final class ActionParameters.Key<T> {
+ ctor public ActionParameters.Key(String name);
+ method public String getName();
+ method public infix androidx.glance.action.ActionParameters.Pair<T> to(T value);
+ property public final String name;
+ }
+
+ public static final class ActionParameters.Pair<T> {
+ }
+
+ public final class ActionParametersKt {
+ method public static androidx.glance.action.ActionParameters actionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+ method public static androidx.glance.action.MutableActionParameters mutableActionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+ method public static androidx.glance.action.MutableActionParameters toMutableParameters(androidx.glance.action.ActionParameters);
+ method public static androidx.glance.action.ActionParameters toParameters(androidx.glance.action.ActionParameters);
+ method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
+ }
+
+ public final class LambdaActionKt {
+ method @androidx.compose.runtime.Composable public static androidx.glance.action.Action action(optional String? key, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ }
+
+ public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
+ method public java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+ method public void clear();
+ method public operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+ method public operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+ method public <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+ method public boolean isEmpty();
+ method public <T> T? remove(androidx.glance.action.ActionParameters.Key<T> key);
+ method public operator <T> T? set(androidx.glance.action.ActionParameters.Key<T> key, T? value);
+ }
+
+ public final class StartActivityActionKt {
+ method public static androidx.glance.action.Action actionStartActivity(android.content.ComponentName componentName, optional androidx.glance.action.ActionParameters parameters);
+ method public static <T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(Class<T> activity, optional androidx.glance.action.ActionParameters parameters);
+ method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(optional androidx.glance.action.ActionParameters parameters);
+ }
+
+}
+
+package androidx.glance.color {
+
+ public abstract sealed class ColorProviders {
+ method public final androidx.glance.unit.ColorProvider getBackground();
+ method public final androidx.glance.unit.ColorProvider getError();
+ method public final androidx.glance.unit.ColorProvider getErrorContainer();
+ method public final androidx.glance.unit.ColorProvider getInverseOnSurface();
+ method public final androidx.glance.unit.ColorProvider getInversePrimary();
+ method public final androidx.glance.unit.ColorProvider getInverseSurface();
+ method public final androidx.glance.unit.ColorProvider getOnBackground();
+ method public final androidx.glance.unit.ColorProvider getOnError();
+ method public final androidx.glance.unit.ColorProvider getOnErrorContainer();
+ method public final androidx.glance.unit.ColorProvider getOnPrimary();
+ method public final androidx.glance.unit.ColorProvider getOnPrimaryContainer();
+ method public final androidx.glance.unit.ColorProvider getOnSecondary();
+ method public final androidx.glance.unit.ColorProvider getOnSecondaryContainer();
+ method public final androidx.glance.unit.ColorProvider getOnSurface();
+ method public final androidx.glance.unit.ColorProvider getOnSurfaceVariant();
+ method public final androidx.glance.unit.ColorProvider getOnTertiary();
+ method public final androidx.glance.unit.ColorProvider getOnTertiaryContainer();
+ method public final androidx.glance.unit.ColorProvider getOutline();
+ method public final androidx.glance.unit.ColorProvider getPrimary();
+ method public final androidx.glance.unit.ColorProvider getPrimaryContainer();
+ method public final androidx.glance.unit.ColorProvider getSecondary();
+ method public final androidx.glance.unit.ColorProvider getSecondaryContainer();
+ method public final androidx.glance.unit.ColorProvider getSurface();
+ method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
+ method public final androidx.glance.unit.ColorProvider getTertiary();
+ method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
+ property public final androidx.glance.unit.ColorProvider background;
+ property public final androidx.glance.unit.ColorProvider error;
+ property public final androidx.glance.unit.ColorProvider errorContainer;
+ property public final androidx.glance.unit.ColorProvider inverseOnSurface;
+ property public final androidx.glance.unit.ColorProvider inversePrimary;
+ property public final androidx.glance.unit.ColorProvider inverseSurface;
+ property public final androidx.glance.unit.ColorProvider onBackground;
+ property public final androidx.glance.unit.ColorProvider onError;
+ property public final androidx.glance.unit.ColorProvider onErrorContainer;
+ property public final androidx.glance.unit.ColorProvider onPrimary;
+ property public final androidx.glance.unit.ColorProvider onPrimaryContainer;
+ property public final androidx.glance.unit.ColorProvider onSecondary;
+ property public final androidx.glance.unit.ColorProvider onSecondaryContainer;
+ property public final androidx.glance.unit.ColorProvider onSurface;
+ property public final androidx.glance.unit.ColorProvider onSurfaceVariant;
+ property public final androidx.glance.unit.ColorProvider onTertiary;
+ property public final androidx.glance.unit.ColorProvider onTertiaryContainer;
+ property public final androidx.glance.unit.ColorProvider outline;
+ property public final androidx.glance.unit.ColorProvider primary;
+ property public final androidx.glance.unit.ColorProvider primaryContainer;
+ property public final androidx.glance.unit.ColorProvider secondary;
+ property public final androidx.glance.unit.ColorProvider secondaryContainer;
+ property public final androidx.glance.unit.ColorProvider surface;
+ property public final androidx.glance.unit.ColorProvider surfaceVariant;
+ property public final androidx.glance.unit.ColorProvider tertiary;
+ property public final androidx.glance.unit.ColorProvider tertiaryContainer;
+ }
+
+ public final class ColorProvidersKt {
+ method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
+ }
+
+ public final class DayNightColorProvidersKt {
+ method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
+ }
+
+}
+
+package androidx.glance.layout {
+
+ public final class Alignment {
+ ctor public Alignment(int horizontal, int vertical);
+ method public int getHorizontal();
+ method public int getVertical();
+ property public final int horizontal;
+ property public final int vertical;
+ field public static final androidx.glance.layout.Alignment.Companion Companion;
+ }
+
+ public static final class Alignment.Companion {
+ method public int getBottom();
+ method public androidx.glance.layout.Alignment getBottomCenter();
+ method public androidx.glance.layout.Alignment getBottomEnd();
+ method public androidx.glance.layout.Alignment getBottomStart();
+ method public androidx.glance.layout.Alignment getCenter();
+ method public androidx.glance.layout.Alignment getCenterEnd();
+ method public int getCenterHorizontally();
+ method public androidx.glance.layout.Alignment getCenterStart();
+ method public int getCenterVertically();
+ method public int getEnd();
+ method public int getStart();
+ method public int getTop();
+ method public androidx.glance.layout.Alignment getTopCenter();
+ method public androidx.glance.layout.Alignment getTopEnd();
+ method public androidx.glance.layout.Alignment getTopStart();
+ property public final int Bottom;
+ property public final androidx.glance.layout.Alignment BottomCenter;
+ property public final androidx.glance.layout.Alignment BottomEnd;
+ property public final androidx.glance.layout.Alignment BottomStart;
+ property public final androidx.glance.layout.Alignment Center;
+ property public final androidx.glance.layout.Alignment CenterEnd;
+ property public final int CenterHorizontally;
+ property public final androidx.glance.layout.Alignment CenterStart;
+ property public final int CenterVertically;
+ property public final int End;
+ property public final int Start;
+ property public final int Top;
+ property public final androidx.glance.layout.Alignment TopCenter;
+ property public final androidx.glance.layout.Alignment TopEnd;
+ property public final androidx.glance.layout.Alignment TopStart;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class Alignment.Horizontal {
+ field public static final androidx.glance.layout.Alignment.Horizontal.Companion Companion;
+ }
+
+ public static final class Alignment.Horizontal.Companion {
+ method public int getCenterHorizontally();
+ method public int getEnd();
+ method public int getStart();
+ property public final int CenterHorizontally;
+ property public final int End;
+ property public final int Start;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class Alignment.Vertical {
+ field public static final androidx.glance.layout.Alignment.Vertical.Companion Companion;
+ }
+
+ public static final class Alignment.Vertical.Companion {
+ method public int getBottom();
+ method public int getCenterVertically();
+ method public int getTop();
+ property public final int Bottom;
+ property public final int CenterVertically;
+ property public final int Top;
+ }
+
+ public final class BoxKt {
+ method @androidx.compose.runtime.Composable public static void Box(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.layout.Alignment contentAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class ColumnKt {
+ method @androidx.compose.runtime.Composable public static void Column(optional androidx.glance.GlanceModifier modifier, optional int verticalAlignment, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.ColumnScope,kotlin.Unit> content);
+ }
+
+ public interface ColumnScope {
+ method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+ }
+
+ @kotlin.jvm.JvmInline public final value class ContentScale {
+ ctor public ContentScale(int value);
+ field public static final androidx.glance.layout.ContentScale.Companion Companion;
+ }
+
+ public static final class ContentScale.Companion {
+ method public int getCrop();
+ method public int getFillBounds();
+ method public int getFit();
+ property public final int Crop;
+ property public final int FillBounds;
+ property public final int Fit;
+ }
+
+ public final class PaddingKt {
+ method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional float left, optional float top, optional float right, optional float bottom);
+ method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional @DimenRes int left, optional @DimenRes int top, optional @DimenRes int right, optional @DimenRes int bottom);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float start, optional float top, optional float end, optional float bottom);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int start, optional @DimenRes int top, optional @DimenRes int end, optional @DimenRes int bottom);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float horizontal, optional float vertical);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int horizontal, optional @DimenRes int vertical);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, float all);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, @DimenRes int all);
+ }
+
+ public final class RowKt {
+ method @androidx.compose.runtime.Composable public static void Row(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, optional int verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.RowScope,kotlin.Unit> content);
+ }
+
+ public interface RowScope {
+ method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+ }
+
+ public final class SizeModifiersKt {
+ method public static androidx.glance.GlanceModifier fillMaxHeight(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier fillMaxSize(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier fillMaxWidth(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, float height);
+ method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, @DimenRes int height);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float size);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int size);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float width, float height);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int width, @DimenRes int height);
+ method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, float width);
+ method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, @DimenRes int width);
+ method public static androidx.glance.GlanceModifier wrapContentHeight(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier wrapContentSize(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier wrapContentWidth(androidx.glance.GlanceModifier);
+ }
+
+ public final class SpacerKt {
+ method @androidx.compose.runtime.Composable public static void Spacer(optional androidx.glance.GlanceModifier modifier);
+ }
+
+}
+
+package androidx.glance.semantics {
+
+ public final class SemanticsConfiguration implements androidx.glance.semantics.SemanticsPropertyReceiver {
+ ctor public SemanticsConfiguration();
+ method public operator <T> T get(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+ method public <T> T? getOrElseNullable(androidx.glance.semantics.SemanticsPropertyKey<T> key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method public <T> T? getOrNull(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+ method public <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+ }
+
+ public final class SemanticsModifierKt {
+ method public static androidx.glance.GlanceModifier semantics(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function1<? super androidx.glance.semantics.SemanticsPropertyReceiver,kotlin.Unit> properties);
+ }
+
+ public final class SemanticsProperties {
+ method public androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> getContentDescription();
+ property public final androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> ContentDescription;
+ field public static final androidx.glance.semantics.SemanticsProperties INSTANCE;
+ }
+
+ public final class SemanticsPropertiesKt {
+ method public static String getContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver);
+ method public static void setContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver, String);
+ }
+
+ public final class SemanticsPropertyKey<T> {
+ ctor public SemanticsPropertyKey(String name, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends T> mergePolicy);
+ method public String getName();
+ method public T? merge(T? parentValue, T childValue);
+ property public final String name;
+ }
+
+ public interface SemanticsPropertyReceiver {
+ method public operator <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+ }
+
+}
+
+package androidx.glance.session {
+
+ public final class SessionManagerKt {
+ }
+
+}
+
+package androidx.glance.state {
+
+ public interface GlanceStateDefinition<T> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>>);
+ method public java.io.File getLocation(android.content.Context context, String fileKey);
+ }
+
+ public final class PreferencesGlanceStateDefinition implements androidx.glance.state.GlanceStateDefinition<androidx.datastore.preferences.core.Preferences> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>>);
+ method public java.io.File getLocation(android.content.Context context, String fileKey);
+ field public static final androidx.glance.state.PreferencesGlanceStateDefinition INSTANCE;
+ }
+
+}
+
+package androidx.glance.text {
+
+ public final class FontFamily {
+ ctor public FontFamily(String family);
+ method public String getFamily();
+ property public final String family;
+ field public static final androidx.glance.text.FontFamily.Companion Companion;
+ }
+
+ public static final class FontFamily.Companion {
+ method public androidx.glance.text.FontFamily getCursive();
+ method public androidx.glance.text.FontFamily getMonospace();
+ method public androidx.glance.text.FontFamily getSansSerif();
+ method public androidx.glance.text.FontFamily getSerif();
+ property public final androidx.glance.text.FontFamily Cursive;
+ property public final androidx.glance.text.FontFamily Monospace;
+ property public final androidx.glance.text.FontFamily SansSerif;
+ property public final androidx.glance.text.FontFamily Serif;
+ }
+
+ @kotlin.jvm.JvmInline public final value class FontStyle {
+ field public static final androidx.glance.text.FontStyle.Companion Companion;
+ }
+
+ public static final class FontStyle.Companion {
+ method public int getItalic();
+ method public int getNormal();
+ method public java.util.List<androidx.glance.text.FontStyle> values();
+ property public final int Italic;
+ property public final int Normal;
+ }
+
+ @kotlin.jvm.JvmInline public final value class FontWeight {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.glance.text.FontWeight.Companion Companion;
+ }
+
+ public static final class FontWeight.Companion {
+ method public int getBold();
+ method public int getMedium();
+ method public int getNormal();
+ property public final int Bold;
+ property public final int Medium;
+ property public final int Normal;
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextAlign {
+ field public static final androidx.glance.text.TextAlign.Companion Companion;
+ }
+
+ public static final class TextAlign.Companion {
+ method public int getCenter();
+ method public int getEnd();
+ method public int getLeft();
+ method public int getRight();
+ method public int getStart();
+ method public java.util.List<androidx.glance.text.TextAlign> values();
+ property public final int Center;
+ property public final int End;
+ property public final int Left;
+ property public final int Right;
+ property public final int Start;
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextDecoration {
+ method @androidx.compose.runtime.Stable public operator boolean contains(int other);
+ method @androidx.compose.runtime.Stable public operator int plus(int decoration);
+ field public static final androidx.glance.text.TextDecoration.Companion Companion;
+ }
+
+ public static final class TextDecoration.Companion {
+ method public int combine(java.util.List<androidx.glance.text.TextDecoration> decorations);
+ method public int getLineThrough();
+ method public int getNone();
+ method public int getUnderline();
+ property public final int LineThrough;
+ property public final int None;
+ property public final int Underline;
+ }
+
+ public final class TextDefaults {
+ method public androidx.glance.unit.ColorProvider getDefaultTextColor();
+ method public androidx.glance.text.TextStyle getDefaultTextStyle();
+ property public final androidx.glance.unit.ColorProvider defaultTextColor;
+ property public final androidx.glance.text.TextStyle defaultTextStyle;
+ field public static final androidx.glance.text.TextDefaults INSTANCE;
+ }
+
+ public final class TextKt {
+ method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.text.TextStyle style, optional int maxLines);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextStyle {
+ ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+ method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+ method public androidx.glance.unit.ColorProvider getColor();
+ method public androidx.glance.text.FontFamily? getFontFamily();
+ method public androidx.compose.ui.unit.TextUnit? getFontSize();
+ method public androidx.glance.text.FontStyle? getFontStyle();
+ method public androidx.glance.text.FontWeight? getFontWeight();
+ method public androidx.glance.text.TextAlign? getTextAlign();
+ method public androidx.glance.text.TextDecoration? getTextDecoration();
+ property public final androidx.glance.unit.ColorProvider color;
+ property public final androidx.glance.text.FontFamily? fontFamily;
+ property public final androidx.compose.ui.unit.TextUnit? fontSize;
+ property public final androidx.glance.text.FontStyle? fontStyle;
+ property public final androidx.glance.text.FontWeight? fontWeight;
+ property public final androidx.glance.text.TextAlign? textAlign;
+ property public final androidx.glance.text.TextDecoration? textDecoration;
+ }
+
+}
+
+package androidx.glance.unit {
+
+ public interface ColorProvider {
+ method public long getColor(android.content.Context context);
+ }
+
+ public final class ColorProviderKt {
+ method public static androidx.glance.unit.ColorProvider ColorProvider(long color);
+ }
+
+}
+
diff --git a/glance/glance/api/res-1.0.0-beta01.txt b/glance/glance/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/glance/glance/api/res-1.0.0-beta01.txt
diff --git a/glance/glance/api/restricted_1.0.0-beta01.txt b/glance/glance/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..47d790f
--- /dev/null
+++ b/glance/glance/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,569 @@
+// Signature format: 4.0
+package androidx.glance {
+
+ public final class BackgroundKt {
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, long color);
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, @ColorRes int color);
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.unit.ColorProvider colorProvider);
+ method public static androidx.glance.GlanceModifier background(androidx.glance.GlanceModifier, androidx.glance.ImageProvider imageProvider, optional int contentScale);
+ }
+
+ public final class ButtonColors {
+ method public androidx.glance.unit.ColorProvider getBackgroundColor();
+ method public androidx.glance.unit.ColorProvider getContentColor();
+ property public final androidx.glance.unit.ColorProvider backgroundColor;
+ property public final androidx.glance.unit.ColorProvider contentColor;
+ }
+
+ public final class ButtonDefaults {
+ method @androidx.compose.runtime.Composable public androidx.glance.ButtonColors buttonColors(optional androidx.glance.unit.ColorProvider backgroundColor, optional androidx.glance.unit.ColorProvider contentColor);
+ field public static final androidx.glance.ButtonDefaults INSTANCE;
+ }
+
+ public final class ButtonKt {
+ method @androidx.compose.runtime.Composable public static void Button(String text, androidx.glance.action.Action onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+ method @androidx.compose.runtime.Composable public static void Button(String text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.glance.GlanceModifier modifier, optional boolean enabled, optional androidx.glance.text.TextStyle? style, optional androidx.glance.ButtonColors colors, optional int maxLines);
+ }
+
+ public final class ColorFilter {
+ field public static final androidx.glance.ColorFilter.Companion Companion;
+ }
+
+ public static final class ColorFilter.Companion {
+ method public androidx.glance.ColorFilter tint(androidx.glance.unit.ColorProvider colorProvider);
+ }
+
+ public final class CombinedGlanceModifier implements androidx.glance.GlanceModifier {
+ ctor public CombinedGlanceModifier(androidx.glance.GlanceModifier outer, androidx.glance.GlanceModifier inner);
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ }
+
+ public final class CompositionLocalsKt {
+ method @androidx.compose.runtime.Composable public static inline <reified T> T currentState();
+ method @androidx.compose.runtime.Composable public static inline <reified T> T? currentState(androidx.datastore.preferences.core.Preferences.Key<T> key);
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> getLocalGlanceId();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> getLocalSize();
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> getLocalState();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> LocalContext;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.GlanceId> LocalGlanceId;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.DpSize> LocalSize;
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Object> LocalState;
+ }
+
+ @androidx.compose.runtime.ComposableTargetMarker(description="Glance Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface GlanceComposable {
+ }
+
+ public interface GlanceId {
+ }
+
+ @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface GlanceModifier {
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ method public default infix androidx.glance.GlanceModifier then(androidx.glance.GlanceModifier other);
+ field public static final androidx.glance.GlanceModifier.Companion Companion;
+ }
+
+ public static final class GlanceModifier.Companion implements androidx.glance.GlanceModifier {
+ method public boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ }
+
+ @kotlin.jvm.JvmDefaultWithCompatibility public static interface GlanceModifier.Element extends androidx.glance.GlanceModifier {
+ method public default boolean all(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public default boolean any(kotlin.jvm.functions.Function1<? super androidx.glance.GlanceModifier.Element,java.lang.Boolean> predicate);
+ method public default <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.glance.GlanceModifier.Element,? extends R> operation);
+ method public default <R> R foldOut(R initial, kotlin.jvm.functions.Function2<? super androidx.glance.GlanceModifier.Element,? super R,? extends R> operation);
+ }
+
+ public final class GlanceTheme {
+ method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public androidx.glance.color.ColorProviders getColors();
+ property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.glance.GlanceComposable public final androidx.glance.color.ColorProviders colors;
+ field public static final androidx.glance.GlanceTheme INSTANCE;
+ }
+
+ public final class GlanceThemeKt {
+ method @androidx.compose.runtime.Composable public static void GlanceTheme(optional androidx.glance.color.ColorProviders colors, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class ImageKt {
+ method @androidx.compose.runtime.Composable public static void Image(androidx.glance.ImageProvider provider, String? contentDescription, optional androidx.glance.GlanceModifier modifier, optional int contentScale, optional androidx.glance.ColorFilter? colorFilter);
+ method public static androidx.glance.ImageProvider ImageProvider(@DrawableRes int resId);
+ method public static androidx.glance.ImageProvider ImageProvider(android.graphics.Bitmap bitmap);
+ method @RequiresApi(android.os.Build.VERSION_CODES.M) public static androidx.glance.ImageProvider ImageProvider(android.graphics.drawable.Icon icon);
+ }
+
+ public interface ImageProvider {
+ }
+
+ public enum Visibility {
+ method public static androidx.glance.Visibility valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.glance.Visibility[] values();
+ enum_constant public static final androidx.glance.Visibility Gone;
+ enum_constant public static final androidx.glance.Visibility Invisible;
+ enum_constant public static final androidx.glance.Visibility Visible;
+ }
+
+ public final class VisibilityKt {
+ method public static androidx.glance.GlanceModifier visibility(androidx.glance.GlanceModifier, androidx.glance.Visibility visibility);
+ }
+
+}
+
+package androidx.glance.action {
+
+ public interface Action {
+ }
+
+ public final class ActionKt {
+ method public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, androidx.glance.action.Action onClick);
+ method @androidx.compose.runtime.Composable public static androidx.glance.GlanceModifier clickable(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ }
+
+ public abstract class ActionParameters {
+ method public abstract java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+ method public abstract operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+ method public abstract operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+ method public abstract <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+ method public abstract boolean isEmpty();
+ }
+
+ public static final class ActionParameters.Key<T> {
+ ctor public ActionParameters.Key(String name);
+ method public String getName();
+ method public infix androidx.glance.action.ActionParameters.Pair<T> to(T value);
+ property public final String name;
+ }
+
+ public static final class ActionParameters.Pair<T> {
+ }
+
+ public final class ActionParametersKt {
+ method public static androidx.glance.action.ActionParameters actionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+ method public static androidx.glance.action.MutableActionParameters mutableActionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
+ method public static androidx.glance.action.MutableActionParameters toMutableParameters(androidx.glance.action.ActionParameters);
+ method public static androidx.glance.action.ActionParameters toParameters(androidx.glance.action.ActionParameters);
+ method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
+ }
+
+ public final class LambdaActionKt {
+ method @androidx.compose.runtime.Composable public static androidx.glance.action.Action action(optional String? key, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+ }
+
+ public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
+ method public java.util.Map<androidx.glance.action.ActionParameters.Key<?>,java.lang.Object> asMap();
+ method public void clear();
+ method public operator <T> boolean contains(androidx.glance.action.ActionParameters.Key<T> key);
+ method public operator <T> T? get(androidx.glance.action.ActionParameters.Key<T> key);
+ method public <T> T getOrDefault(androidx.glance.action.ActionParameters.Key<T> key, T defaultValue);
+ method public boolean isEmpty();
+ method public <T> T? remove(androidx.glance.action.ActionParameters.Key<T> key);
+ method public operator <T> T? set(androidx.glance.action.ActionParameters.Key<T> key, T? value);
+ }
+
+ public final class StartActivityActionKt {
+ method public static androidx.glance.action.Action actionStartActivity(android.content.ComponentName componentName, optional androidx.glance.action.ActionParameters parameters);
+ method public static <T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(Class<T> activity, optional androidx.glance.action.ActionParameters parameters);
+ method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action actionStartActivity(optional androidx.glance.action.ActionParameters parameters);
+ }
+
+}
+
+package androidx.glance.color {
+
+ public abstract sealed class ColorProviders {
+ method public final androidx.glance.unit.ColorProvider getBackground();
+ method public final androidx.glance.unit.ColorProvider getError();
+ method public final androidx.glance.unit.ColorProvider getErrorContainer();
+ method public final androidx.glance.unit.ColorProvider getInverseOnSurface();
+ method public final androidx.glance.unit.ColorProvider getInversePrimary();
+ method public final androidx.glance.unit.ColorProvider getInverseSurface();
+ method public final androidx.glance.unit.ColorProvider getOnBackground();
+ method public final androidx.glance.unit.ColorProvider getOnError();
+ method public final androidx.glance.unit.ColorProvider getOnErrorContainer();
+ method public final androidx.glance.unit.ColorProvider getOnPrimary();
+ method public final androidx.glance.unit.ColorProvider getOnPrimaryContainer();
+ method public final androidx.glance.unit.ColorProvider getOnSecondary();
+ method public final androidx.glance.unit.ColorProvider getOnSecondaryContainer();
+ method public final androidx.glance.unit.ColorProvider getOnSurface();
+ method public final androidx.glance.unit.ColorProvider getOnSurfaceVariant();
+ method public final androidx.glance.unit.ColorProvider getOnTertiary();
+ method public final androidx.glance.unit.ColorProvider getOnTertiaryContainer();
+ method public final androidx.glance.unit.ColorProvider getOutline();
+ method public final androidx.glance.unit.ColorProvider getPrimary();
+ method public final androidx.glance.unit.ColorProvider getPrimaryContainer();
+ method public final androidx.glance.unit.ColorProvider getSecondary();
+ method public final androidx.glance.unit.ColorProvider getSecondaryContainer();
+ method public final androidx.glance.unit.ColorProvider getSurface();
+ method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
+ method public final androidx.glance.unit.ColorProvider getTertiary();
+ method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
+ property public final androidx.glance.unit.ColorProvider background;
+ property public final androidx.glance.unit.ColorProvider error;
+ property public final androidx.glance.unit.ColorProvider errorContainer;
+ property public final androidx.glance.unit.ColorProvider inverseOnSurface;
+ property public final androidx.glance.unit.ColorProvider inversePrimary;
+ property public final androidx.glance.unit.ColorProvider inverseSurface;
+ property public final androidx.glance.unit.ColorProvider onBackground;
+ property public final androidx.glance.unit.ColorProvider onError;
+ property public final androidx.glance.unit.ColorProvider onErrorContainer;
+ property public final androidx.glance.unit.ColorProvider onPrimary;
+ property public final androidx.glance.unit.ColorProvider onPrimaryContainer;
+ property public final androidx.glance.unit.ColorProvider onSecondary;
+ property public final androidx.glance.unit.ColorProvider onSecondaryContainer;
+ property public final androidx.glance.unit.ColorProvider onSurface;
+ property public final androidx.glance.unit.ColorProvider onSurfaceVariant;
+ property public final androidx.glance.unit.ColorProvider onTertiary;
+ property public final androidx.glance.unit.ColorProvider onTertiaryContainer;
+ property public final androidx.glance.unit.ColorProvider outline;
+ property public final androidx.glance.unit.ColorProvider primary;
+ property public final androidx.glance.unit.ColorProvider primaryContainer;
+ property public final androidx.glance.unit.ColorProvider secondary;
+ property public final androidx.glance.unit.ColorProvider secondaryContainer;
+ property public final androidx.glance.unit.ColorProvider surface;
+ property public final androidx.glance.unit.ColorProvider surfaceVariant;
+ property public final androidx.glance.unit.ColorProvider tertiary;
+ property public final androidx.glance.unit.ColorProvider tertiaryContainer;
+ }
+
+ public final class ColorProvidersKt {
+ method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
+ }
+
+ public final class DayNightColorProvidersKt {
+ method public static androidx.glance.unit.ColorProvider ColorProvider(long day, long night);
+ }
+
+}
+
+package androidx.glance.layout {
+
+ public final class Alignment {
+ ctor public Alignment(int horizontal, int vertical);
+ method public int getHorizontal();
+ method public int getVertical();
+ property public final int horizontal;
+ property public final int vertical;
+ field public static final androidx.glance.layout.Alignment.Companion Companion;
+ }
+
+ public static final class Alignment.Companion {
+ method public int getBottom();
+ method public androidx.glance.layout.Alignment getBottomCenter();
+ method public androidx.glance.layout.Alignment getBottomEnd();
+ method public androidx.glance.layout.Alignment getBottomStart();
+ method public androidx.glance.layout.Alignment getCenter();
+ method public androidx.glance.layout.Alignment getCenterEnd();
+ method public int getCenterHorizontally();
+ method public androidx.glance.layout.Alignment getCenterStart();
+ method public int getCenterVertically();
+ method public int getEnd();
+ method public int getStart();
+ method public int getTop();
+ method public androidx.glance.layout.Alignment getTopCenter();
+ method public androidx.glance.layout.Alignment getTopEnd();
+ method public androidx.glance.layout.Alignment getTopStart();
+ property public final int Bottom;
+ property public final androidx.glance.layout.Alignment BottomCenter;
+ property public final androidx.glance.layout.Alignment BottomEnd;
+ property public final androidx.glance.layout.Alignment BottomStart;
+ property public final androidx.glance.layout.Alignment Center;
+ property public final androidx.glance.layout.Alignment CenterEnd;
+ property public final int CenterHorizontally;
+ property public final androidx.glance.layout.Alignment CenterStart;
+ property public final int CenterVertically;
+ property public final int End;
+ property public final int Start;
+ property public final int Top;
+ property public final androidx.glance.layout.Alignment TopCenter;
+ property public final androidx.glance.layout.Alignment TopEnd;
+ property public final androidx.glance.layout.Alignment TopStart;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class Alignment.Horizontal {
+ field public static final androidx.glance.layout.Alignment.Horizontal.Companion Companion;
+ }
+
+ public static final class Alignment.Horizontal.Companion {
+ method public int getCenterHorizontally();
+ method public int getEnd();
+ method public int getStart();
+ property public final int CenterHorizontally;
+ property public final int End;
+ property public final int Start;
+ }
+
+ @kotlin.jvm.JvmInline public static final value class Alignment.Vertical {
+ field public static final androidx.glance.layout.Alignment.Vertical.Companion Companion;
+ }
+
+ public static final class Alignment.Vertical.Companion {
+ method public int getBottom();
+ method public int getCenterVertically();
+ method public int getTop();
+ property public final int Bottom;
+ property public final int CenterVertically;
+ property public final int Top;
+ }
+
+ public final class BoxKt {
+ method @androidx.compose.runtime.Composable public static void Box(optional androidx.glance.GlanceModifier modifier, optional androidx.glance.layout.Alignment contentAlignment, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class ColumnKt {
+ method @androidx.compose.runtime.Composable public static void Column(optional androidx.glance.GlanceModifier modifier, optional int verticalAlignment, optional int horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.ColumnScope,kotlin.Unit> content);
+ }
+
+ public interface ColumnScope {
+ method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+ }
+
+ @kotlin.jvm.JvmInline public final value class ContentScale {
+ ctor public ContentScale(int value);
+ field public static final androidx.glance.layout.ContentScale.Companion Companion;
+ }
+
+ public static final class ContentScale.Companion {
+ method public int getCrop();
+ method public int getFillBounds();
+ method public int getFit();
+ property public final int Crop;
+ property public final int FillBounds;
+ property public final int Fit;
+ }
+
+ public final class PaddingKt {
+ method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional float left, optional float top, optional float right, optional float bottom);
+ method public static androidx.glance.GlanceModifier absolutePadding(androidx.glance.GlanceModifier, optional @DimenRes int left, optional @DimenRes int top, optional @DimenRes int right, optional @DimenRes int bottom);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float start, optional float top, optional float end, optional float bottom);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int start, optional @DimenRes int top, optional @DimenRes int end, optional @DimenRes int bottom);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional float horizontal, optional float vertical);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, optional @DimenRes int horizontal, optional @DimenRes int vertical);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, float all);
+ method public static androidx.glance.GlanceModifier padding(androidx.glance.GlanceModifier, @DimenRes int all);
+ }
+
+ public final class RowKt {
+ method @androidx.compose.runtime.Composable public static void Row(optional androidx.glance.GlanceModifier modifier, optional int horizontalAlignment, optional int verticalAlignment, kotlin.jvm.functions.Function1<? super androidx.glance.layout.RowScope,kotlin.Unit> content);
+ }
+
+ public interface RowScope {
+ method public androidx.glance.GlanceModifier defaultWeight(androidx.glance.GlanceModifier);
+ }
+
+ public final class SizeModifiersKt {
+ method public static androidx.glance.GlanceModifier fillMaxHeight(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier fillMaxSize(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier fillMaxWidth(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, float height);
+ method public static androidx.glance.GlanceModifier height(androidx.glance.GlanceModifier, @DimenRes int height);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float size);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int size);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, float width, float height);
+ method public static androidx.glance.GlanceModifier size(androidx.glance.GlanceModifier, @DimenRes int width, @DimenRes int height);
+ method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, float width);
+ method public static androidx.glance.GlanceModifier width(androidx.glance.GlanceModifier, @DimenRes int width);
+ method public static androidx.glance.GlanceModifier wrapContentHeight(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier wrapContentSize(androidx.glance.GlanceModifier);
+ method public static androidx.glance.GlanceModifier wrapContentWidth(androidx.glance.GlanceModifier);
+ }
+
+ public final class SpacerKt {
+ method @androidx.compose.runtime.Composable public static void Spacer(optional androidx.glance.GlanceModifier modifier);
+ }
+
+}
+
+package androidx.glance.semantics {
+
+ public final class SemanticsConfiguration implements androidx.glance.semantics.SemanticsPropertyReceiver {
+ ctor public SemanticsConfiguration();
+ method public operator <T> T get(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+ method public <T> T? getOrElseNullable(androidx.glance.semantics.SemanticsPropertyKey<T> key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+ method public <T> T? getOrNull(androidx.glance.semantics.SemanticsPropertyKey<T> key);
+ method public <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+ }
+
+ public final class SemanticsModifierKt {
+ method public static androidx.glance.GlanceModifier semantics(androidx.glance.GlanceModifier, kotlin.jvm.functions.Function1<? super androidx.glance.semantics.SemanticsPropertyReceiver,kotlin.Unit> properties);
+ }
+
+ public final class SemanticsProperties {
+ method public androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> getContentDescription();
+ property public final androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> ContentDescription;
+ field public static final androidx.glance.semantics.SemanticsProperties INSTANCE;
+ }
+
+ public final class SemanticsPropertiesKt {
+ method public static String getContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver);
+ method public static void setContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver, String);
+ }
+
+ public final class SemanticsPropertyKey<T> {
+ ctor public SemanticsPropertyKey(String name, optional kotlin.jvm.functions.Function2<? super T,? super T,? extends T> mergePolicy);
+ method public String getName();
+ method public T? merge(T? parentValue, T childValue);
+ property public final String name;
+ }
+
+ public interface SemanticsPropertyReceiver {
+ method public operator <T> void set(androidx.glance.semantics.SemanticsPropertyKey<T> key, T value);
+ }
+
+}
+
+package androidx.glance.session {
+
+ public final class SessionManagerKt {
+ }
+
+}
+
+package androidx.glance.state {
+
+ public interface GlanceStateDefinition<T> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>>);
+ method public java.io.File getLocation(android.content.Context context, String fileKey);
+ }
+
+ public final class PreferencesGlanceStateDefinition implements androidx.glance.state.GlanceStateDefinition<androidx.datastore.preferences.core.Preferences> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>>);
+ method public java.io.File getLocation(android.content.Context context, String fileKey);
+ field public static final androidx.glance.state.PreferencesGlanceStateDefinition INSTANCE;
+ }
+
+}
+
+package androidx.glance.text {
+
+ public final class FontFamily {
+ ctor public FontFamily(String family);
+ method public String getFamily();
+ property public final String family;
+ field public static final androidx.glance.text.FontFamily.Companion Companion;
+ }
+
+ public static final class FontFamily.Companion {
+ method public androidx.glance.text.FontFamily getCursive();
+ method public androidx.glance.text.FontFamily getMonospace();
+ method public androidx.glance.text.FontFamily getSansSerif();
+ method public androidx.glance.text.FontFamily getSerif();
+ property public final androidx.glance.text.FontFamily Cursive;
+ property public final androidx.glance.text.FontFamily Monospace;
+ property public final androidx.glance.text.FontFamily SansSerif;
+ property public final androidx.glance.text.FontFamily Serif;
+ }
+
+ @kotlin.jvm.JvmInline public final value class FontStyle {
+ field public static final androidx.glance.text.FontStyle.Companion Companion;
+ }
+
+ public static final class FontStyle.Companion {
+ method public int getItalic();
+ method public int getNormal();
+ method public java.util.List<androidx.glance.text.FontStyle> values();
+ property public final int Italic;
+ property public final int Normal;
+ }
+
+ @kotlin.jvm.JvmInline public final value class FontWeight {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.glance.text.FontWeight.Companion Companion;
+ }
+
+ public static final class FontWeight.Companion {
+ method public int getBold();
+ method public int getMedium();
+ method public int getNormal();
+ property public final int Bold;
+ property public final int Medium;
+ property public final int Normal;
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextAlign {
+ field public static final androidx.glance.text.TextAlign.Companion Companion;
+ }
+
+ public static final class TextAlign.Companion {
+ method public int getCenter();
+ method public int getEnd();
+ method public int getLeft();
+ method public int getRight();
+ method public int getStart();
+ method public java.util.List<androidx.glance.text.TextAlign> values();
+ property public final int Center;
+ property public final int End;
+ property public final int Left;
+ property public final int Right;
+ property public final int Start;
+ }
+
+ @kotlin.jvm.JvmInline public final value class TextDecoration {
+ method @androidx.compose.runtime.Stable public operator boolean contains(int other);
+ method @androidx.compose.runtime.Stable public operator int plus(int decoration);
+ field public static final androidx.glance.text.TextDecoration.Companion Companion;
+ }
+
+ public static final class TextDecoration.Companion {
+ method public int combine(java.util.List<androidx.glance.text.TextDecoration> decorations);
+ method public int getLineThrough();
+ method public int getNone();
+ method public int getUnderline();
+ property public final int LineThrough;
+ property public final int None;
+ property public final int Underline;
+ }
+
+ public final class TextDefaults {
+ method public androidx.glance.unit.ColorProvider getDefaultTextColor();
+ method public androidx.glance.text.TextStyle getDefaultTextStyle();
+ property public final androidx.glance.unit.ColorProvider defaultTextColor;
+ property public final androidx.glance.text.TextStyle defaultTextStyle;
+ field public static final androidx.glance.text.TextDefaults INSTANCE;
+ }
+
+ public final class TextKt {
+ method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.glance.GlanceModifier modifier, optional androidx.glance.text.TextStyle style, optional int maxLines);
+ }
+
+ @androidx.compose.runtime.Immutable public final class TextStyle {
+ ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+ method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+ method public androidx.glance.unit.ColorProvider getColor();
+ method public androidx.glance.text.FontFamily? getFontFamily();
+ method public androidx.compose.ui.unit.TextUnit? getFontSize();
+ method public androidx.glance.text.FontStyle? getFontStyle();
+ method public androidx.glance.text.FontWeight? getFontWeight();
+ method public androidx.glance.text.TextAlign? getTextAlign();
+ method public androidx.glance.text.TextDecoration? getTextDecoration();
+ property public final androidx.glance.unit.ColorProvider color;
+ property public final androidx.glance.text.FontFamily? fontFamily;
+ property public final androidx.compose.ui.unit.TextUnit? fontSize;
+ property public final androidx.glance.text.FontStyle? fontStyle;
+ property public final androidx.glance.text.FontWeight? fontWeight;
+ property public final androidx.glance.text.TextAlign? textAlign;
+ property public final androidx.glance.text.TextDecoration? textDecoration;
+ }
+
+}
+
+package androidx.glance.unit {
+
+ public interface ColorProvider {
+ method public long getColor(android.content.Context context);
+ }
+
+ public final class ColorProviderKt {
+ method public static androidx.glance.unit.ColorProvider ColorProvider(long color);
+ }
+
+}
+
diff --git a/glance/glance/build.gradle b/glance/glance/build.gradle
index b8fb797..68ec157 100644
--- a/glance/glance/build.gradle
+++ b/glance/glance/build.gradle
@@ -30,7 +30,7 @@
dependencies {
api("androidx.annotation:annotation:1.2.0")
- api("androidx.compose.runtime:runtime:1.1.1")
+ api("androidx.compose.runtime:runtime:1.2.1")
api("androidx.compose.ui:ui-graphics:1.1.1")
api("androidx.compose.ui:ui-unit:1.1.1")
api("androidx.datastore:datastore-core:1.0.0")
@@ -41,7 +41,6 @@
implementation("androidx.work:work-runtime:2.7.1")
implementation("androidx.work:work-runtime-ktx:2.7.1")
implementation(libs.kotlinStdlib)
- implementation(project(":compose:runtime:runtime"))
// Force upgrade since 1.2.0 is not compatible with latest lint.
implementation("androidx.annotation:annotation-experimental:1.3.0")
@@ -66,6 +65,7 @@
androidTestImplementation('androidx.test:monitor:1.5.0')
androidTestImplementation('androidx.core:core-ktx:1.7.0')
+ androidTestImplementation("androidx.room:room-runtime:2.4.3")
androidTestImplementation("androidx.work:work-testing:2.7.1")
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
androidTestImplementation(libs.kotlinCoroutinesTest)
diff --git a/glance/glance/src/androidAndroidTest/kotlin/androidx/glance/session/GlanceSessionManagerTest.kt b/glance/glance/src/androidAndroidTest/kotlin/androidx/glance/session/GlanceSessionManagerTest.kt
index 9db9374..78fc16b 100644
--- a/glance/glance/src/androidAndroidTest/kotlin/androidx/glance/session/GlanceSessionManagerTest.kt
+++ b/glance/glance/src/androidAndroidTest/kotlin/androidx/glance/session/GlanceSessionManagerTest.kt
@@ -33,6 +33,7 @@
import androidx.work.WorkManager
import androidx.work.WorkerFactory
import androidx.work.WorkerParameters
+import androidx.work.impl.WorkManagerImpl
import androidx.work.testing.SynchronousExecutor
import androidx.work.testing.WorkManagerTestInitHelper
import com.google.common.truth.Truth.assertThat
@@ -115,6 +116,8 @@
testScope.cancel()
workerStateScope.cancel()
WorkManager.getInstance(context).cancelAllWork()
+ // TODO(b/242026176): remove this once WorkManager allows closing the test database.
+ WorkManagerImpl.getInstance(context).workDatabase.close()
}
@Test
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt
index 36adf59..a23f842 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt
@@ -37,6 +37,7 @@
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
import org.junit.Assert
+import org.junit.Assert.fail
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -623,6 +624,57 @@
}
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+ @Test
+ fun testReleaseRemovedSurfaceCallbacks() {
+ val callbacks = object : CanvasFrontBufferedRenderer.Callback<Any> {
+ override fun onDrawFrontBufferedLayer(
+ canvas: Canvas,
+ bufferWidth: Int,
+ bufferHeight: Int,
+ param: Any
+ ) {
+ // no-op
+ }
+
+ override fun onDrawMultiBufferedLayer(
+ canvas: Canvas,
+ bufferWidth: Int,
+ bufferHeight: Int,
+ params: Collection<Any>
+ ) {
+ // no-op
+ }
+ }
+ var renderer: CanvasFrontBufferedRenderer<Any>? = null
+ var surfaceView: FrontBufferedRendererTestActivity.TestSurfaceView? = null
+ val createLatch = CountDownLatch(1)
+ ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+ .moveToState(Lifecycle.State.CREATED)
+ .onActivity {
+ surfaceView = it.getSurfaceView()
+ renderer = CanvasFrontBufferedRenderer(surfaceView!!, callbacks)
+ createLatch.countDown()
+ }
+
+ Assert.assertTrue(createLatch.await(3000, TimeUnit.MILLISECONDS))
+ // Capture surfaceView with local val to avoid Kotlin warnings regarding the surfaceView
+ // parameter changing potentially
+ val resolvedSurfaceView = surfaceView
+ try {
+ if (resolvedSurfaceView != null) {
+ Assert.assertEquals(1, resolvedSurfaceView.getCallbackCount())
+ renderer?.release(true)
+ renderer = null
+ Assert.assertEquals(0, resolvedSurfaceView.getCallbackCount())
+ } else {
+ fail("Unable to resolve SurfaceView, was the Activity created?")
+ }
+ } finally {
+ renderer?.release(true)
+ }
+ }
+
@RequiresApi(Build.VERSION_CODES.Q)
private fun CanvasFrontBufferedRenderer<*>?.blockingRelease(timeoutMillis: Long = 3000) {
if (this != null) {
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/FrontBufferedRendererTestActivity.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/FrontBufferedRendererTestActivity.kt
index addcfff..9fae68f 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/FrontBufferedRendererTestActivity.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/FrontBufferedRendererTestActivity.kt
@@ -17,24 +17,60 @@
package androidx.graphics.lowlatency
import android.app.Activity
+import android.content.Context
import android.os.Bundle
+import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.ViewGroup
class FrontBufferedRendererTestActivity : Activity() {
- private lateinit var mSurfaceView: SurfaceView
+ private lateinit var mSurfaceView: TestSurfaceView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val surfaceView = SurfaceView(this).also { mSurfaceView = it }
+ val surfaceView = TestSurfaceView(this).also { mSurfaceView = it }
setContentView(surfaceView, ViewGroup.LayoutParams(WIDTH, HEIGHT))
}
- fun getSurfaceView(): SurfaceView = mSurfaceView
+ fun getSurfaceView(): TestSurfaceView = mSurfaceView
companion object {
const val WIDTH = 100
const val HEIGHT = 100
}
+
+ class TestSurfaceView(context: Context) : SurfaceView(context) {
+
+ private var mHolderWrapper: HolderWrapper? = null
+
+ override fun getHolder(): SurfaceHolder {
+ var wrapper = mHolderWrapper
+ if (wrapper == null) {
+ wrapper = HolderWrapper(super.getHolder()).also { mHolderWrapper = it }
+ }
+ return wrapper
+ }
+
+ fun getCallbackCount(): Int = mHolderWrapper?.mCallbacks?.size ?: 0
+
+ class HolderWrapper(val wrapped: SurfaceHolder) : SurfaceHolder by wrapped {
+
+ val mCallbacks = ArrayList<SurfaceHolder.Callback>()
+
+ override fun addCallback(callback: SurfaceHolder.Callback) {
+ if (!mCallbacks.contains(callback)) {
+ mCallbacks.add(callback)
+ wrapped.addCallback(callback)
+ }
+ }
+
+ override fun removeCallback(callback: SurfaceHolder.Callback) {
+ if (mCallbacks.contains(callback)) {
+ mCallbacks.remove(callback)
+ wrapped.removeCallback(callback)
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
index 15fbf2e..a849854 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
@@ -25,6 +25,7 @@
import android.view.SurfaceHolder
import android.view.SurfaceView
import androidx.annotation.RequiresApi
+import androidx.graphics.opengl.GLRenderer
import androidx.graphics.opengl.egl.EGLManager
import androidx.graphics.surface.SurfaceControlCompat
import androidx.graphics.surface.SurfaceControlUtils
@@ -37,9 +38,11 @@
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
+import kotlin.IllegalStateException
import kotlin.math.roundToInt
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Ignore
@@ -1218,6 +1221,92 @@
}
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+ @Test
+ fun testReleaseRemovedSurfaceCallbacks() {
+ val callbacks = object : GLFrontBufferedRenderer.Callback<Any> {
+ override fun onDrawFrontBufferedLayer(
+ eglManager: EGLManager,
+ bufferInfo: BufferInfo,
+ transform: FloatArray,
+ param: Any
+ ) {
+ // NO-OP
+ }
+
+ override fun onDrawMultiBufferedLayer(
+ eglManager: EGLManager,
+ bufferInfo: BufferInfo,
+ transform: FloatArray,
+ params: Collection<Any>
+ ) {
+ // NO-OP
+ }
+ }
+ var renderer: GLFrontBufferedRenderer<Any>? = null
+ var surfaceView: FrontBufferedRendererTestActivity.TestSurfaceView? = null
+ val createLatch = CountDownLatch(1)
+ ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+ .moveToState(Lifecycle.State.CREATED)
+ .onActivity {
+ surfaceView = it.getSurfaceView()
+ renderer = GLFrontBufferedRenderer(surfaceView!!, callbacks)
+ createLatch.countDown()
+ }
+
+ assertTrue(createLatch.await(3000, TimeUnit.MILLISECONDS))
+ // Capture surfaceView with local val to avoid Kotlin warnings regarding the surfaceView
+ // parameter changing potentially
+ val resolvedSurfaceView = surfaceView
+ try {
+ if (resolvedSurfaceView != null) {
+ assertEquals(1, resolvedSurfaceView.getCallbackCount())
+ val releaseLatch = CountDownLatch(1)
+ renderer!!.release(true) {
+ releaseLatch.countDown()
+ }
+ assertTrue(releaseLatch.await(3000, TimeUnit.MILLISECONDS))
+ assertEquals(0, resolvedSurfaceView.getCallbackCount())
+ renderer = null
+ } else {
+ fail("Unable to resolve SurfaceView, was the test Activity created?")
+ }
+ } finally {
+ renderer?.blockingRelease()
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+ @Test
+ fun testGLFrontBufferedRendererCreationFromUnstartedGLRenderer() {
+ val callbacks = object : GLFrontBufferedRenderer.Callback<Any> {
+ override fun onDrawFrontBufferedLayer(
+ eglManager: EGLManager,
+ bufferInfo: BufferInfo,
+ transform: FloatArray,
+ param: Any
+ ) {
+ // NO-OP
+ }
+
+ override fun onDrawMultiBufferedLayer(
+ eglManager: EGLManager,
+ bufferInfo: BufferInfo,
+ transform: FloatArray,
+ params: Collection<Any>
+ ) {
+ // NO-OP
+ }
+ }
+ ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+ .moveToState(Lifecycle.State.CREATED)
+ .onActivity {
+ assertThrows(IllegalStateException::class.java) {
+ GLFrontBufferedRenderer(it.getSurfaceView(), callbacks, GLRenderer())
+ }
+ }
+ }
+
@RequiresApi(Build.VERSION_CODES.Q)
private fun GLFrontBufferedRenderer<*>?.blockingRelease(timeoutMillis: Long = 3000) {
if (this != null) {
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt
index 98cd2bc5..3a54bb1 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt
@@ -120,132 +120,133 @@
private var inverse = BufferTransformHintResolver.UNKNOWN_TRANSFORM
private val mBufferTransform = BufferTransformer()
private val mParentLayerTransform = android.graphics.Matrix()
+ private val mHolderCallback = object : SurfaceHolder.Callback2 {
+
+ private var mWidth = -1
+ private var mHeight = -1
+
+ private var transformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
+
+ private val mTransformResolver = BufferTransformHintResolver()
+
+ override fun surfaceCreated(p0: SurfaceHolder) {
+ // NO-OP
+ }
+
+ override fun surfaceChanged(
+ holder: SurfaceHolder,
+ format: Int,
+ width: Int,
+ height: Int
+ ) {
+ mWidth = width
+ mHeight = height
+ releaseInternal(true)
+ transformHint = mTransformResolver.getBufferTransformHint(surfaceView)
+ inverse = mBufferTransform.invertBufferTransform(transformHint)
+ mBufferTransform.computeTransform(width, height, inverse)
+ updateMatrixTransform(width.toFloat(), height.toFloat(), inverse)
+
+ mPersistedCanvasRenderer = SingleBufferedCanvasRenderer.create<T>(
+ width,
+ height,
+ mBufferTransform,
+ mExecutor,
+ object : SingleBufferedCanvasRenderer.RenderCallbacks<T> {
+
+ override fun render(canvas: Canvas, width: Int, height: Int, param: T) {
+ callback.onDrawFrontBufferedLayer(canvas, width, height, param)
+ }
+
+ @SuppressLint("WrongConstant")
+ override fun onBufferReady(
+ hardwareBuffer: HardwareBuffer,
+ syncFenceCompat: SyncFenceCompat?
+ ) {
+ mPersistedCanvasRenderer?.isVisible = true
+ mFrontBufferSurfaceControl?.let { frontBufferSurfaceControl ->
+ val transaction = SurfaceControlCompat.Transaction()
+ .setLayer(frontBufferSurfaceControl, Integer.MAX_VALUE)
+ .setBuffer(
+ frontBufferSurfaceControl,
+ hardwareBuffer,
+ syncFenceCompat
+ )
+ .setVisibility(frontBufferSurfaceControl, true)
+ .reparent(frontBufferSurfaceControl, mParentSurfaceControl)
+ if (inverse != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
+ transaction.setBufferTransform(
+ frontBufferSurfaceControl,
+ inverse
+ )
+ }
+ callback.onFrontBufferedLayerRenderComplete(
+ frontBufferSurfaceControl, transaction)
+ transaction.commit()
+ syncFenceCompat?.close()
+ }
+ }
+ })
+
+ val parentSurfaceControl = SurfaceControlCompat.Builder()
+ .setParent(surfaceView)
+ .setName("MultiBufferedLayer")
+ .build()
+ .apply {
+ // SurfaceControl is not visible by default so make it visible right
+ // after creation
+ SurfaceControlCompat.Transaction()
+ .setVisibility(this, true)
+ .commit()
+ }
+
+ val multiBufferNode = RenderNode("MultiBufferNode").apply {
+ setPosition(0, 0, mBufferTransform.glWidth, mBufferTransform.glHeight)
+ mMultiBufferNode = this
+ }
+ mMultiBufferedCanvasRenderer = MultiBufferedCanvasRenderer(
+ multiBufferNode,
+ mBufferTransform.glWidth,
+ mBufferTransform.glHeight
+ )
+
+ mFrontBufferSurfaceControl = SurfaceControlCompat.Builder()
+ .setParent(parentSurfaceControl)
+ .setName("FrontBufferedLayer")
+ .build()
+
+ mParentSurfaceControl = parentSurfaceControl
+ }
+
+ override fun surfaceDestroyed(p0: SurfaceHolder) {
+ releaseInternal(true)
+ }
+
+ override fun surfaceRedrawNeeded(holder: SurfaceHolder) {
+ val latch = CountDownLatch(1)
+ surfaceRedrawNeededAsync(holder) {
+ latch.countDown()
+ }
+ latch.await()
+ }
+
+ override fun surfaceRedrawNeededAsync(
+ holder: SurfaceHolder,
+ drawingFinished: Runnable
+ ) {
+ val renderer = mMultiBufferedCanvasRenderer
+ if (renderer != null) {
+ renderer.renderFrame(mExecutor) { buffer ->
+ setParentSurfaceControlBuffer(buffer, drawingFinished)
+ }
+ } else {
+ drawingFinished.run()
+ }
+ }
+ }
init {
- surfaceView.holder.addCallback(object : SurfaceHolder.Callback2 {
-
- private var mWidth = -1
- private var mHeight = -1
-
- private var transformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
-
- private val mTransformResolver = BufferTransformHintResolver()
-
- override fun surfaceCreated(p0: SurfaceHolder) {
- // NO-OP
- }
-
- override fun surfaceChanged(
- holder: SurfaceHolder,
- format: Int,
- width: Int,
- height: Int
- ) {
- mWidth = width
- mHeight = height
- releaseInternal(true)
- transformHint = mTransformResolver.getBufferTransformHint(surfaceView)
- inverse = mBufferTransform.invertBufferTransform(transformHint)
- mBufferTransform.computeTransform(width, height, inverse)
- updateMatrixTransform(width.toFloat(), height.toFloat(), inverse)
-
- mPersistedCanvasRenderer = SingleBufferedCanvasRenderer.create<T>(
- width,
- height,
- mBufferTransform,
- mExecutor,
- object : SingleBufferedCanvasRenderer.RenderCallbacks<T> {
-
- override fun render(canvas: Canvas, width: Int, height: Int, param: T) {
- callback.onDrawFrontBufferedLayer(canvas, width, height, param)
- }
-
- @SuppressLint("WrongConstant")
- override fun onBufferReady(
- hardwareBuffer: HardwareBuffer,
- syncFenceCompat: SyncFenceCompat?
- ) {
- mPersistedCanvasRenderer?.isVisible = true
- mFrontBufferSurfaceControl?.let { frontBufferSurfaceControl ->
- val transaction = SurfaceControlCompat.Transaction()
- .setLayer(frontBufferSurfaceControl, Integer.MAX_VALUE)
- .setBuffer(
- frontBufferSurfaceControl,
- hardwareBuffer,
- syncFenceCompat
- )
- .setVisibility(frontBufferSurfaceControl, true)
- .reparent(frontBufferSurfaceControl, mParentSurfaceControl)
- if (inverse != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
- transaction.setBufferTransform(
- frontBufferSurfaceControl,
- inverse
- )
- }
- callback.onFrontBufferedLayerRenderComplete(
- frontBufferSurfaceControl, transaction)
- transaction.commit()
- syncFenceCompat?.close()
- }
- }
- })
-
- val parentSurfaceControl = SurfaceControlCompat.Builder()
- .setParent(surfaceView)
- .setName("MultiBufferedLayer")
- .build()
- .apply {
- // SurfaceControl is not visible by default so make it visible right
- // after creation
- SurfaceControlCompat.Transaction()
- .setVisibility(this, true)
- .commit()
- }
-
- val multiBufferNode = RenderNode("MultiBufferNode").apply {
- setPosition(0, 0, mBufferTransform.glWidth, mBufferTransform.glHeight)
- mMultiBufferNode = this
- }
- mMultiBufferedCanvasRenderer = MultiBufferedCanvasRenderer(
- multiBufferNode,
- mBufferTransform.glWidth,
- mBufferTransform.glHeight
- )
-
- mFrontBufferSurfaceControl = SurfaceControlCompat.Builder()
- .setParent(parentSurfaceControl)
- .setName("FrontBufferedLayer")
- .build()
-
- mParentSurfaceControl = parentSurfaceControl
- }
-
- override fun surfaceDestroyed(p0: SurfaceHolder) {
- releaseInternal(true)
- }
-
- override fun surfaceRedrawNeeded(holder: SurfaceHolder) {
- val latch = CountDownLatch(1)
- surfaceRedrawNeededAsync(holder) {
- latch.countDown()
- }
- latch.await()
- }
-
- override fun surfaceRedrawNeededAsync(
- holder: SurfaceHolder,
- drawingFinished: Runnable
- ) {
- val renderer = mMultiBufferedCanvasRenderer
- if (renderer != null) {
- renderer.renderFrame(mExecutor) { buffer ->
- setParentSurfaceControlBuffer(buffer, drawingFinished)
- }
- } else {
- drawingFinished.run()
- }
- }
- })
+ surfaceView.holder.addCallback(mHolderCallback)
}
private inline fun RenderNode.record(block: (canvas: Canvas) -> Unit): RenderNode {
@@ -480,6 +481,7 @@
@JvmOverloads
fun release(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) {
if (!mIsReleased) {
+ surfaceView.holder.removeCallback(mHolderCallback)
releaseInternal(cancelPending) {
onReleaseComplete?.invoke()
mExecutor.shutdown()
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
index af54b29..65a5a40 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
@@ -35,6 +35,7 @@
import androidx.hardware.SyncFenceCompat
import androidx.opengl.EGLExt.Companion.EGL_ANDROID_NATIVE_FENCE_SYNC
import androidx.opengl.EGLExt.Companion.EGL_KHR_FENCE_SYNC
+import java.lang.IllegalStateException
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.Executors
@@ -288,6 +289,11 @@
} else {
// ... otherwise use the [GLRenderer] that is being provided for us
mIsManagingGLRenderer = false
+ if (!glRenderer.isRunning()) {
+ throw IllegalStateException("The provided GLRenderer must be running prior to " +
+ "creation of GLFrontBufferedRenderer, " +
+ "did you forget to call GLRenderer#start()?")
+ }
glRenderer
}
renderer.registerEGLContextCallback(mContextCallbacks)
@@ -496,38 +502,27 @@
* release SurfaceControl instances
*/
internal fun detachTargets(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) {
- // Wrap the callback into a separate lambda to ensure it is invoked only after
- // both the front and multi buffered layer target renderers are detached
- var callbackCount = 0
- var expectedCount = 0
- if (mFrontBufferedRenderTarget?.isAttached() == true) {
- expectedCount++
- }
+ // GLRenderer processes requests in order on a single thread. So detach the corresponding
+ // render targets then queue a request to teardown all resources
+ mFrontBufferedRenderTarget?.detach(cancelPending)
+ mMultiBufferedLayerRenderTarget?.detach(cancelPending)
- if (mMultiBufferedLayerRenderTarget?.isAttached() == true) {
- expectedCount++
- }
val frontBufferedLayerSurfaceControl = mFrontBufferedLayerSurfaceControl
- val wrappedCallback: (GLRenderer.RenderTarget) -> Unit = {
- callbackCount++
- if (callbackCount >= expectedCount) {
- mBufferPool?.let { releaseBuffers(it) }
- clearParamQueues()
-
- frontBufferedLayerSurfaceControl?.let {
- val transaction = SurfaceControlCompat.Transaction()
- .reparent(it, null)
- mParentRenderLayer.release(transaction)
- transaction.commit()
- it.release()
- }
-
- onReleaseComplete?.invoke()
- }
- }
mFrontBufferedLayerSurfaceControl = null
- mFrontBufferedRenderTarget?.detach(cancelPending, wrappedCallback)
- mMultiBufferedLayerRenderTarget?.detach(cancelPending, wrappedCallback)
+ mGLRenderer.execute {
+ mBufferPool?.let { releaseBuffers(it) }
+ clearParamQueues()
+
+ val transaction = SurfaceControlCompat.Transaction()
+ if (frontBufferedLayerSurfaceControl != null) {
+ transaction.reparent(frontBufferedLayerSurfaceControl, null)
+ }
+ mParentRenderLayer.release(transaction)
+ transaction.commit()
+ frontBufferedLayerSurfaceControl?.release()
+
+ onReleaseComplete?.invoke()
+ }
mFrontBufferedRenderTarget = null
mMultiBufferedLayerRenderTarget = null
mWidth = -1
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt
index b2407a3..ad99c0c 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt
@@ -48,36 +48,38 @@
private val mTransformResolver = BufferTransformHintResolver()
- private var transformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
- private var inverse = BufferTransformHintResolver.UNKNOWN_TRANSFORM
- init {
- surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
+ private var mTransformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
+ private var mInverse = BufferTransformHintResolver.UNKNOWN_TRANSFORM
+ private val mHolderCallback = object : SurfaceHolder.Callback {
- override fun surfaceCreated(holder: SurfaceHolder) {
- // NO-OP wait on surfaceChanged callback
- }
+ override fun surfaceCreated(holder: SurfaceHolder) {
+ // NO-OP wait on surfaceChanged callback
+ }
- override fun surfaceChanged(
- holder: SurfaceHolder,
- format: Int,
- width: Int,
- height: Int
- ) {
- transformHint = mTransformResolver.getBufferTransformHint(surfaceView)
- inverse = mBufferTransform.invertBufferTransform(transformHint)
- mBufferTransform.computeTransform(width, height, inverse)
- mParentSurfaceControl?.release()
- mParentSurfaceControl = createDoubleBufferedSurfaceControl()
- mLayerCallback?.onSizeChanged(width, height)
- }
+ override fun surfaceChanged(
+ holder: SurfaceHolder,
+ format: Int,
+ width: Int,
+ height: Int
+ ) {
+ mTransformHint = mTransformResolver.getBufferTransformHint(surfaceView)
+ mInverse = mBufferTransform.invertBufferTransform(mTransformHint)
+ mBufferTransform.computeTransform(width, height, mInverse)
+ mParentSurfaceControl?.release()
+ mParentSurfaceControl = createDoubleBufferedSurfaceControl()
+ mLayerCallback?.onSizeChanged(width, height)
+ }
- override fun surfaceDestroyed(p0: SurfaceHolder) {
- mLayerCallback?.onLayerDestroyed()
- }
- })
+ override fun surfaceDestroyed(p0: SurfaceHolder) {
+ mLayerCallback?.onLayerDestroyed()
+ }
}
- override fun getInverseBufferTransform(): Int = inverse
+ init {
+ surfaceView.holder.addCallback(mHolderCallback)
+ }
+
+ override fun getInverseBufferTransform(): Int = mInverse
override fun getBufferWidth(): Int = mBufferTransform.glWidth
@@ -151,8 +153,8 @@
.setBuffer(sc, frameBuffer.hardwareBuffer, syncFenceCompat) {
mLayerCallback?.getFrameBufferPool()?.release(frameBuffer)
}
- if (transformHint != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
- transaction.setBufferTransform(sc, inverse)
+ if (mTransformHint != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
+ transaction.setBufferTransform(sc, mInverse)
}
renderLayerCallback.onMultiBufferedLayerRenderComplete(
@@ -197,8 +199,8 @@
mRenderTarget?.requestRender()
}
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun release(transaction: SurfaceControlCompat.Transaction) {
+ surfaceView.holder.removeCallback(mHolderCallback)
mParentSurfaceControl?.let {
transaction.reparent(it, null)
it.release()
diff --git a/health/connect/connect-client/build.gradle b/health/connect/connect-client/build.gradle
index 8a8757a..06b657b 100644
--- a/health/connect/connect-client/build.gradle
+++ b/health/connect/connect-client/build.gradle
@@ -35,10 +35,7 @@
// Add dependencies here
api("androidx.activity:activity:1.2.0")
api("androidx.annotation:annotation:1.2.0")
- bundleInside(project(
- path: ":health:connect:connect-client-proto",
- configuration: "export"
- ))
+ bundleInside(project(path: ":health:connect:connect-client-proto", configuration: "export"))
implementation(libs.guavaListenableFuture)
implementation(libs.guavaAndroid)
implementation(libs.kotlinCoroutinesAndroid)
diff --git a/javascriptengine/javascriptengine/api/aidlRelease/current/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl b/javascriptengine/javascriptengine/api/aidlRelease/current/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl
index f7888a7..96b70225 100644
--- a/javascriptengine/javascriptengine/api/aidlRelease/current/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl
+++ b/javascriptengine/javascriptengine/api/aidlRelease/current/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl
@@ -36,9 +36,9 @@
interface IJsSandboxConsoleCallback {
void consoleMessage(int contextGroupId, int level, String message, String source, int line, int column, String trace) = 0;
void consoleClear(int contextGroupId) = 1;
- const int CONSOLE_MESSAGE_LEVEL_LOG = (1 << 0);
- const int CONSOLE_MESSAGE_LEVEL_DEBUG = (1 << 1);
- const int CONSOLE_MESSAGE_LEVEL_INFO = (1 << 2);
- const int CONSOLE_MESSAGE_LEVEL_ERROR = (1 << 3);
- const int CONSOLE_MESSAGE_LEVEL_WARNING = (1 << 4);
+ const int CONSOLE_MESSAGE_LEVEL_LOG = (1 << 0) /* 1 */;
+ const int CONSOLE_MESSAGE_LEVEL_DEBUG = (1 << 1) /* 2 */;
+ const int CONSOLE_MESSAGE_LEVEL_INFO = (1 << 2) /* 4 */;
+ const int CONSOLE_MESSAGE_LEVEL_ERROR = (1 << 3) /* 8 */;
+ const int CONSOLE_MESSAGE_LEVEL_WARNING = (1 << 4) /* 16 */;
}
diff --git a/libraryversions.toml b/libraryversions.toml
index ec838a5..c511569 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -14,12 +14,12 @@
BLUETOOTH = "1.0.0-alpha01"
BROWSER = "1.6.0-alpha01"
BUILDSRC_TESTS = "1.0.0-alpha01"
-CAMERA = "1.3.0-alpha06"
+CAMERA = "1.3.0-alpha07"
CAMERA_PIPE = "1.0.0-alpha01"
CARDVIEW = "1.1.0-alpha01"
CAR_APP = "1.4.0-alpha01"
COLLECTION = "1.3.0-alpha05"
-COMPOSE = "1.5.0-alpha03"
+COMPOSE = "1.5.0-alpha04"
COMPOSE_COMPILER = "1.4.6"
COMPOSE_MATERIAL3 = "1.2.0-alpha01"
COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha01"
@@ -29,7 +29,7 @@
CONSTRAINTLAYOUT_CORE = "1.1.0-alpha10"
CONTENTPAGER = "1.1.0-alpha01"
COORDINATORLAYOUT = "1.3.0-alpha01"
-CORE = "1.11.0-alpha03"
+CORE = "1.11.0-alpha04"
CORE_ANIMATION = "1.0.0-beta02"
CORE_ANIMATION_TESTING = "1.0.0-beta01"
CORE_APPDIGEST = "1.0.0-alpha01"
@@ -41,7 +41,7 @@
CORE_ROLE = "1.2.0-alpha01"
CORE_SPLASHSCREEN = "1.1.0-alpha01"
CORE_UWB = "1.0.0-alpha06"
-CREDENTIALS = "1.0.0-alpha07"
+CREDENTIALS = "1.0.0-alpha08"
CURSORADAPTER = "1.1.0-alpha01"
CUSTOMVIEW = "1.2.0-alpha03"
CUSTOMVIEW_POOLINGCONTAINER = "1.1.0-alpha01"
@@ -52,21 +52,23 @@
DYNAMICANIMATION = "1.1.0-alpha04"
DYNAMICANIMATION_KTX = "1.0.0-alpha04"
EMOJI = "1.2.0-alpha03"
-EMOJI2 = "1.4.0-beta02"
+EMOJI2 = "1.4.0-beta03"
ENTERPRISE = "1.1.0-rc01"
EXIFINTERFACE = "1.4.0-alpha01"
-FRAGMENT = "1.6.0-beta01"
+FRAGMENT = "1.6.0-rc01"
FUTURES = "1.2.0-alpha01"
-GLANCE = "1.0.0-alpha06"
-GLANCE_TEMPLATE = "1.0.0-alpha01"
+GLANCE = "1.0.0-beta01"
+GLANCE_PREVIEW = "1.0.0-alpha06"
+GLANCE_TEMPLATE = "1.0.0-alpha06"
+GLANCE_WEAR_TILES = "1.0.0-alpha06"
GRAPHICS_CORE = "1.0.0-alpha04"
GRAPHICS_FILTERS = "1.0.0-alpha01"
-GRAPHICS_SHAPES = "1.0.0-alpha02"
+GRAPHICS_SHAPES = "1.0.0-alpha03"
GRIDLAYOUT = "1.1.0-alpha02"
HEALTH_CONNECT = "1.0.0-alpha11"
HEALTH_SERVICES_CLIENT = "1.0.0-beta04"
HEIFWRITER = "1.1.0-alpha02"
-HILT = "1.1.0-alpha02"
+HILT = "1.1.0-alpha03"
HILT_NAVIGATION_COMPOSE = "1.1.0-alpha02"
INPUT_MOTIONPREDICTION = "1.0.0-beta02"
INSPECTION = "1.0.0"
@@ -86,18 +88,18 @@
MEDIA2 = "1.3.0-alpha01"
MEDIAROUTER = "1.5.0-alpha01"
METRICS = "1.0.0-alpha05"
-NAVIGATION = "2.6.0-beta01"
+NAVIGATION = "2.6.0-rc01"
PAGING = "3.2.0-alpha05"
PAGING_COMPOSE = "1.0.0-alpha19"
PALETTE = "1.1.0-alpha01"
PERCENTLAYOUT = "1.1.0-alpha01"
PREFERENCE = "1.3.0-alpha01"
PRINT = "1.1.0-beta01"
-PRIVACYSANDBOX_ADS = "1.0.0-beta03"
+PRIVACYSANDBOX_ADS = "1.0.0-beta04"
PRIVACYSANDBOX_PLUGINS = "1.0.0-alpha02"
PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha04"
PRIVACYSANDBOX_TOOLS = "1.0.0-alpha04"
-PRIVACYSANDBOX_UI = "1.0.0-alpha02"
+PRIVACYSANDBOX_UI = "1.0.0-alpha03"
PROFILEINSTALLER = "1.4.0-alpha01"
RECOMMENDATION = "1.1.0-alpha01"
RECYCLERVIEW = "1.4.0-alpha01"
@@ -106,7 +108,7 @@
RESOURCEINSPECTION = "1.1.0-alpha01"
ROOM = "2.6.0-alpha02"
SAVEDSTATE = "1.3.0-alpha01"
-SECURITY = "1.1.0-alpha06"
+SECURITY = "1.1.0-alpha07"
SECURITY_APP_AUTHENTICATOR = "1.0.0-alpha03"
SECURITY_APP_AUTHENTICATOR_TESTING = "1.0.0-alpha02"
SECURITY_BIOMETRIC = "1.0.0-alpha01"
@@ -124,7 +126,7 @@
SWIPEREFRESHLAYOUT = "1.2.0-alpha01"
TESTEXT = "1.0.0-alpha02"
TESTSCREENSHOT = "1.0.0-alpha01"
-TEST_UIAUTOMATOR = "2.3.0-alpha03"
+TEST_UIAUTOMATOR = "2.3.0-alpha04"
TEXT = "1.0.0-alpha01"
TRACING = "1.2.0-beta04"
TRACING_PERFETTO = "1.0.0-alpha15"
@@ -137,17 +139,17 @@
VERSIONED_PARCELABLE = "1.2.0-alpha01"
VIEWPAGER = "1.1.0-alpha02"
VIEWPAGER2 = "1.2.0-alpha01"
-WEAR = "1.3.0-alpha05"
-WEAR_COMPOSE = "1.2.0-alpha09"
-WEAR_COMPOSE_MATERIAL3 = "1.0.0-alpha03"
+WEAR = "1.3.0-alpha06"
+WEAR_COMPOSE = "1.2.0-alpha10"
+WEAR_COMPOSE_MATERIAL3 = "1.0.0-alpha04"
WEAR_INPUT = "1.2.0-alpha03"
WEAR_INPUT_TESTING = "1.2.0-alpha03"
WEAR_ONGOING = "1.1.0-alpha01"
WEAR_PHONE_INTERACTIONS = "1.1.0-alpha04"
-WEAR_PROTOLAYOUT = "1.0.0-alpha08"
+WEAR_PROTOLAYOUT = "1.0.0-alpha09"
WEAR_REMOTE_INTERACTIONS = "1.1.0-alpha01"
-WEAR_TILES = "1.2.0-alpha04"
-WEAR_WATCHFACE = "1.2.0-alpha08"
+WEAR_TILES = "1.2.0-alpha05"
+WEAR_WATCHFACE = "1.2.0-alpha09"
WEBKIT = "1.8.0-alpha01"
WINDOW = "1.2.0-alpha01"
WINDOW_EXTENSIONS = "1.2.0-alpha01"
@@ -204,7 +206,6 @@
EXIFINTERFACE = { group = "androidx.exifinterface", atomicGroupVersion = "versions.EXIFINTERFACE" }
FRAGMENT = { group = "androidx.fragment", atomicGroupVersion = "versions.FRAGMENT" }
GLANCE = { group = "androidx.glance", atomicGroupVersion = "versions.GLANCE" }
-GLANCE_TEMPLATE = { group = "androidx.template", atomicGroupVersion = "versions.GLANCE_TEMPLATE" }
GRAPHICS = { group = "androidx.graphics" }
GRAPHICS_FILTERS = { group = "androidx.graphics.filters", atomicGroupVersion = "versions.GRAPHICS_FILTERS" }
GRIDLAYOUT = { group = "androidx.gridlayout", atomicGroupVersion = "versions.GRIDLAYOUT" }
diff --git a/lifecycle/lifecycle-runtime/proguard-rules.pro b/lifecycle/lifecycle-runtime/proguard-rules.pro
index e4b2c95..95192c1 100644
--- a/lifecycle/lifecycle-runtime/proguard-rules.pro
+++ b/lifecycle/lifecycle-runtime/proguard-rules.pro
@@ -7,9 +7,6 @@
<fields>;
}
--keep !interface * implements androidx.lifecycle.LifecycleObserver {
-}
-
-keep class * implements androidx.lifecycle.GeneratedAdapter {
<init>(...);
}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageJava.java b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageJava.java
new file mode 100644
index 0000000..99fc876
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageJava.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx;
+
+import static androidx.annotation.RestrictTo.Scope.TESTS;
+
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+/** @noinspection unused*/
+public class RestrictToTestsAnnotationUsageJava {
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS)
+ public void testMethodFullyQualified() {}
+
+ @RestrictTo(RestrictTo.Scope.TESTS)
+ public void testMethodOuterClass() {}
+
+ @RestrictTo(Scope.TESTS)
+ public void testMethodInnerClass() {}
+
+ @RestrictTo(TESTS)
+ public void testMethodStaticImport() {}
+
+ @RestrictTo({Scope.TESTS, Scope.LIBRARY})
+ public void testMethodVarArg() {}
+}
diff --git a/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt
new file mode 100644
index 0000000..2d1622d
--- /dev/null
+++ b/lint-checks/integration-tests/src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt
@@ -0,0 +1,32 @@
+/*
+ * 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:Suppress("unused")
+
+package androidx
+
+import androidx.annotation.RestrictTo
+
+class RestrictToTestsAnnotationUsageKotlin {
+ @RestrictTo(RestrictTo.Scope.TESTS)
+ fun testMethod() {}
+
+ @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY)
+ fun testMethodVarArg() {}
+
+ @get:RestrictTo(RestrictTo.Scope.TESTS)
+ val testPropertyGet = "test"
+}
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index 9bf1025..7466c91 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -70,6 +70,7 @@
NullabilityAnnotationsDetector.ISSUE,
IgnoreClassLevelDetector.ISSUE,
ExperimentalPropertyAnnotationDetector.ISSUE,
+ BanRestrictToTestsScope.ISSUE,
// Temporarily disable AIDL lint check due to b/278871118.
// UnstableAidlAnnotationDetector.ISSUE,
// MissingJvmDefaultWithCompatibilityDetector is intentionally left out of the
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt b/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt
new file mode 100644
index 0000000..487fd61
--- /dev/null
+++ b/lint-checks/src/main/java/androidx/build/lint/BanRestrictToTestsScope.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 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:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import com.android.tools.lint.checks.getFqName
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Incident
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.kotlin.psi.KtAnnotationEntry
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UReferenceExpression
+import org.jetbrains.uast.util.isArrayInitializer
+
+class BanRestrictToTestsScope : Detector(), Detector.UastScanner {
+
+ override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
+
+ override fun createUastHandler(context: JavaContext): UElementHandler {
+ return AnnotationChecker(context)
+ }
+
+ private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
+ override fun visitAnnotation(node: UAnnotation) {
+ if (node.qualifiedName != "androidx.annotation.RestrictTo") return
+
+ // Resolve the FQN for all arguments to value parameter.
+ val scopes = node.findAttributeValue("value")?.let { value ->
+ if (value.isArrayInitializer()) {
+ (value as? UCallExpression)?.valueArguments?.mapNotNull { arg ->
+ arg as? UReferenceExpression
+ } ?: emptyList()
+ } else if (value is UReferenceExpression) {
+ listOfNotNull(value)
+ } else {
+ emptyList()
+ }
+ }?.mapNotNull { expr ->
+ expr.resolve()?.getFqName()
+ } ?: emptyList()
+
+ if (!scopes.contains("androidx.annotation.RestrictTo.Scope.TESTS")) return
+
+ val incident = Incident(context)
+ .issue(ISSUE)
+ .location(context.getNameLocation(node))
+ .message("Replace `@RestrictTo(TESTS)` with `@VisibleForTesting`")
+ .scope(node)
+
+ // If there's only one scope, suggest replacement.
+ if (scopes.size == 1) {
+ // Extract Kotlin use-site target, if available.
+ val useSiteTarget = (node.sourcePsi as? KtAnnotationEntry)
+ ?.useSiteTarget
+ ?.getAnnotationUseSiteTarget()
+ ?.renderName
+ ?.let { "$it:" } ?: ""
+
+ val fix = fix().name("Replace with `@${useSiteTarget}VisibleForTesting`")
+ .replace()
+ .with("@${useSiteTarget}androidx.annotation.VisibleForTesting")
+ .shortenNames()
+ .build()
+ incident.fix(fix)
+ }
+
+ context.report(incident)
+ }
+ }
+
+ companion object {
+ val ISSUE = Issue.create(
+ "UsesRestrictToTestsScope",
+ "Uses @RestrictTo(TESTS) restriction scope",
+ "Use of @RestrictTo(TESTS) restriction scope is not allowed, use " +
+ "@VisibleForTesting instead.",
+ Category.CORRECTNESS, 5, Severity.ERROR,
+ Implementation(BanRestrictToTestsScope::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+ }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt
new file mode 100644
index 0000000..08720a0
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanRestrictToTestsScopeTest.kt
@@ -0,0 +1,118 @@
+/*
+ * 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:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanRestrictToTestsScopeTest : AbstractLintDetectorTest(
+ useDetector = BanRestrictToTestsScope(),
+ useIssues = listOf(BanRestrictToTestsScope.ISSUE),
+ stubs = arrayOf(Stubs.RestrictTo),
+) {
+
+ @Test
+ fun `Detection of @RestrictTo(TESTS) usage in Java sources`() {
+ val input = arrayOf(
+ javaSample("androidx.RestrictToTestsAnnotationUsageJava"),
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/androidx/RestrictToTestsAnnotationUsageJava.java:26: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageJava.java:29: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @RestrictTo(RestrictTo.Scope.TESTS)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageJava.java:32: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @RestrictTo(Scope.TESTS)
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageJava.java:35: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @RestrictTo(TESTS)
+ ~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageJava.java:38: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @RestrictTo({Scope.TESTS, Scope.LIBRARY})
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+5 errors, 0 warnings
+ """.trimIndent()
+
+ val fixDiffs = """
+Fix for src/androidx/RestrictToTestsAnnotationUsageJava.java line 26: Replace with `@VisibleForTesting`:
+@@ -26 +26
+- @RestrictTo(androidx.annotation.RestrictTo.Scope.TESTS)
++ @androidx.annotation.VisibleForTesting
+Fix for src/androidx/RestrictToTestsAnnotationUsageJava.java line 29: Replace with `@VisibleForTesting`:
+@@ -29 +29
+- @RestrictTo(RestrictTo.Scope.TESTS)
++ @androidx.annotation.VisibleForTesting
+Fix for src/androidx/RestrictToTestsAnnotationUsageJava.java line 32: Replace with `@VisibleForTesting`:
+@@ -32 +32
+- @RestrictTo(Scope.TESTS)
++ @androidx.annotation.VisibleForTesting
+Fix for src/androidx/RestrictToTestsAnnotationUsageJava.java line 35: Replace with `@VisibleForTesting`:
+@@ -35 +35
+- @RestrictTo(TESTS)
++ @androidx.annotation.VisibleForTesting
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input)
+ .expect(expected)
+ .expectFixDiffs(fixDiffs)
+ }
+ @Test
+ fun `Detection of @RestrictTo(TESTS) usage in Kotlin sources`() {
+ val input = arrayOf(
+ ktSample("androidx.RestrictToTestsAnnotationUsageKotlin"),
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:24: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @RestrictTo(RestrictTo.Scope.TESTS)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:27: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/androidx/RestrictToTestsAnnotationUsageKotlin.kt:30: Error: Replace @RestrictTo(TESTS) with @VisibleForTesting [UsesRestrictToTestsScope]
+ @get:RestrictTo(RestrictTo.Scope.TESTS)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+3 errors, 0 warnings
+ """.trimIndent()
+
+ val fixDiffs = """
+Fix for src/androidx/RestrictToTestsAnnotationUsageKotlin.kt line 24: Replace with `@VisibleForTesting`:
+@@ -24 +24
+- @RestrictTo(RestrictTo.Scope.TESTS)
++ @androidx.annotation.VisibleForTesting
+Fix for src/androidx/RestrictToTestsAnnotationUsageKotlin.kt line 30: Replace with `@get:VisibleForTesting`:
+@@ -30 +30
+- @get:RestrictTo(RestrictTo.Scope.TESTS)
++ @get:androidx.annotation.VisibleForTesting
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(*input)
+ .expect(expected)
+ .expectFixDiffs(fixDiffs)
+ }
+}
diff --git a/media/media/build.gradle b/media/media/build.gradle
index 0787b8d..91a6177 100644
--- a/media/media/build.gradle
+++ b/media/media/build.gradle
@@ -19,7 +19,6 @@
plugins {
id("AndroidXPlugin")
id("com.android.library")
- id("androidx.stableaidl")
}
dependencies {
@@ -47,10 +46,6 @@
buildTypes.all {
consumerProguardFiles "proguard-rules.pro"
-
- stableAidl {
- version 1
- }
}
namespace "androidx.media"
}
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/MediaDescriptionCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/MediaDescriptionCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/MediaDescriptionCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/MediaDescriptionCompat.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/MediaMetadataCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/MediaMetadataCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/MediaMetadataCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/MediaMetadataCompat.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/RatingCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/RatingCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/RatingCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/RatingCompat.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/IMediaControllerCallback.aidl b/media/media/src/main/aidl/android/support/v4/media/session/IMediaControllerCallback.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/IMediaControllerCallback.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/IMediaControllerCallback.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/IMediaSession.aidl b/media/media/src/main/aidl/android/support/v4/media/session/IMediaSession.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/IMediaSession.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/IMediaSession.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/MediaSessionCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/session/MediaSessionCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/MediaSessionCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/MediaSessionCompat.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl b/media/media/src/main/aidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl
diff --git a/media/media/src/main/stableAidl/android/support/v4/media/session/PlaybackStateCompat.aidl b/media/media/src/main/aidl/android/support/v4/media/session/PlaybackStateCompat.aidl
similarity index 100%
rename from media/media/src/main/stableAidl/android/support/v4/media/session/PlaybackStateCompat.aidl
rename to media/media/src/main/aidl/android/support/v4/media/session/PlaybackStateCompat.aidl
diff --git a/media/media/src/main/stableAidlImports/android/content/Intent.aidl b/media/media/src/main/stableAidlImports/android/content/Intent.aidl
deleted file mode 100644
index 0c8c241..0000000
--- a/media/media/src/main/stableAidlImports/android/content/Intent.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.content;
-
-@JavaOnlyStableParcelable parcelable Intent;
diff --git a/media/media/src/main/stableAidlImports/android/os/Bundle.aidl b/media/media/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/media/media/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/media2/media2-common/api/aidlRelease/current/androidx/media2/common/ParcelImplListSlice.aidl b/media2/media2-common/api/aidlRelease/current/androidx/media2/common/ParcelImplListSlice.aidl
index a095df7..f4e5890 100644
--- a/media2/media2-common/api/aidlRelease/current/androidx/media2/common/ParcelImplListSlice.aidl
+++ b/media2/media2-common/api/aidlRelease/current/androidx/media2/common/ParcelImplListSlice.aidl
@@ -1,3 +1,18 @@
+/**
+ * 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.
+ */
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/media2/media2-session/src/main/stableAidlImports/android/net/Uri.aidl b/media2/media2-session/src/main/stableAidlImports/android/net/Uri.aidl
deleted file mode 100644
index 5ec5a66..0000000
--- a/media2/media2-session/src/main/stableAidlImports/android/net/Uri.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.net;
-
-@JavaOnlyStableParcelable parcelable Uri;
diff --git a/media2/media2-session/src/main/stableAidlImports/android/os/Bundle.aidl b/media2/media2-session/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/media2/media2-session/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/media2/media2-widget/src/main/res/values-am/strings.xml b/media2/media2-widget/src/main/res/values-am/strings.xml
index c25eb1d..e95ccd2 100644
--- a/media2/media2-widget/src/main/res/values-am/strings.xml
+++ b/media2/media2-widget/src/main/res/values-am/strings.xml
@@ -45,5 +45,5 @@
<string name="mcv2_next_button_desc" msgid="1204572886248099893">"ቀጣይ ማህደረ መረጃ"</string>
<string name="mcv2_rewind_button_desc" msgid="578809901971186362">"በ10 ሰከንዶች አጠንጥን"</string>
<string name="mcv2_ffwd_button_desc" msgid="611689280746097673">"ወደፊት በ30 ሰከንዶች ሂድ"</string>
- <string name="mcv2_full_screen_button_desc" msgid="1609817079594941003">"ሙሉ ማያ ገጽ"</string>
+ <string name="mcv2_full_screen_button_desc" msgid="1609817079594941003">"ሙሉ ማያ ገፅ"</string>
</resources>
diff --git a/media2/media2-widget/src/main/res/values-zh-rHK/strings.xml b/media2/media2-widget/src/main/res/values-zh-rHK/strings.xml
index 7145c45..05ec6bb 100644
--- a/media2/media2-widget/src/main/res/values-zh-rHK/strings.xml
+++ b/media2/media2-widget/src/main/res/values-zh-rHK/strings.xml
@@ -29,7 +29,7 @@
<string name="mcv2_non_music_title_unknown_text" msgid="2032814146738922144">"影片標題不明"</string>
<string name="mcv2_music_title_unknown_text" msgid="6037645626002038645">"歌名不明"</string>
<string name="mcv2_music_artist_unknown_text" msgid="5393558204040775454">"歌手不明"</string>
- <string name="mcv2_playback_error_text" msgid="6061787693725630293">"無法播放您要求的影片"</string>
+ <string name="mcv2_playback_error_text" msgid="6061787693725630293">"無法播放你要求的影片"</string>
<string name="mcv2_error_dialog_button" msgid="5940167897992933850">"好"</string>
<string name="mcv2_back_button_desc" msgid="1540894858499118373">"返回"</string>
<string name="mcv2_overflow_left_button_desc" msgid="2749567167276435888">"返回上一個按鈕清單"</string>
diff --git a/navigation/navigation-common-ktx/api/2.6.0-beta02.txt b/navigation/navigation-common-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-common-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-common-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-common-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-common-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-common-ktx/api/res-2.6.0-beta02.txt b/navigation/navigation-common-ktx/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-common-ktx/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-common-ktx/api/restricted_2.6.0-beta02.txt b/navigation/navigation-common-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-common-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-common/api/2.6.0-beta02.txt b/navigation/navigation-common/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6d0229
--- /dev/null
+++ b/navigation/navigation-common/api/2.6.0-beta02.txt
@@ -0,0 +1,530 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+ public final class ActionOnlyNavDirections implements androidx.navigation.NavDirections {
+ ctor public ActionOnlyNavDirections(int actionId);
+ method public int component1();
+ method public androidx.navigation.ActionOnlyNavDirections copy(int actionId);
+ method public int getActionId();
+ method public android.os.Bundle getArguments();
+ property public int actionId;
+ property public android.os.Bundle arguments;
+ }
+
+ @androidx.navigation.NavOptionsDsl public final class AnimBuilder {
+ ctor public AnimBuilder();
+ method public int getEnter();
+ method public int getExit();
+ method public int getPopEnter();
+ method public int getPopExit();
+ method public void setEnter(int);
+ method public void setExit(int);
+ method public void setPopEnter(int);
+ method public void setPopExit(int);
+ property public final int enter;
+ property public final int exit;
+ property public final int popEnter;
+ property public final int popExit;
+ }
+
+ public interface FloatingWindow {
+ }
+
+ public final class NamedNavArgument {
+ method public operator String component1();
+ method public operator androidx.navigation.NavArgument component2();
+ method public androidx.navigation.NavArgument getArgument();
+ method public String getName();
+ property public final androidx.navigation.NavArgument argument;
+ property public final String name;
+ }
+
+ public final class NamedNavArgumentKt {
+ method public static androidx.navigation.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavAction {
+ ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions, optional android.os.Bundle? defaultArguments);
+ ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions);
+ ctor public NavAction(@IdRes int destinationId);
+ method public android.os.Bundle? getDefaultArguments();
+ method public int getDestinationId();
+ method public androidx.navigation.NavOptions? getNavOptions();
+ method public void setDefaultArguments(android.os.Bundle?);
+ method public void setNavOptions(androidx.navigation.NavOptions?);
+ property public final android.os.Bundle? defaultArguments;
+ property public final int destinationId;
+ property public final androidx.navigation.NavOptions? navOptions;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class NavActionBuilder {
+ ctor public NavActionBuilder();
+ method public java.util.Map<java.lang.String,java.lang.Object> getDefaultArguments();
+ method public int getDestinationId();
+ method public void navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+ method public void setDestinationId(int);
+ property public final java.util.Map<java.lang.String,java.lang.Object> defaultArguments;
+ property public final int destinationId;
+ }
+
+ public interface NavArgs {
+ }
+
+ public final class NavArgsLazy<Args extends androidx.navigation.NavArgs> implements kotlin.Lazy<Args> {
+ ctor public NavArgsLazy(kotlin.reflect.KClass<Args> navArgsClass, kotlin.jvm.functions.Function0<android.os.Bundle> argumentProducer);
+ method public Args getValue();
+ method public boolean isInitialized();
+ property public Args value;
+ }
+
+ public final class NavArgument {
+ method public Object? getDefaultValue();
+ method public androidx.navigation.NavType<java.lang.Object> getType();
+ method public boolean isDefaultValuePresent();
+ method public boolean isNullable();
+ property public final Object? defaultValue;
+ property public final boolean isDefaultValuePresent;
+ property public final boolean isNullable;
+ property public final androidx.navigation.NavType<java.lang.Object> type;
+ }
+
+ public static final class NavArgument.Builder {
+ ctor public NavArgument.Builder();
+ method public androidx.navigation.NavArgument build();
+ method public androidx.navigation.NavArgument.Builder setDefaultValue(Object? defaultValue);
+ method public androidx.navigation.NavArgument.Builder setIsNullable(boolean isNullable);
+ method public <T> androidx.navigation.NavArgument.Builder setType(androidx.navigation.NavType<T> type);
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class NavArgumentBuilder {
+ ctor public NavArgumentBuilder();
+ method public androidx.navigation.NavArgument build();
+ method public Object? getDefaultValue();
+ method public boolean getNullable();
+ method public androidx.navigation.NavType<?> getType();
+ method public void setDefaultValue(Object?);
+ method public void setNullable(boolean);
+ method public void setType(androidx.navigation.NavType<?>);
+ property public final Object? defaultValue;
+ property public final boolean nullable;
+ property public final androidx.navigation.NavType<?> type;
+ }
+
+ public final class NavBackStackEntry implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+ method public android.os.Bundle? getArguments();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ method public androidx.navigation.NavDestination getDestination();
+ method public String getId();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method public androidx.lifecycle.SavedStateHandle getSavedStateHandle();
+ method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+ method public androidx.lifecycle.ViewModelStore getViewModelStore();
+ property public final android.os.Bundle? arguments;
+ property public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+ property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+ property public final androidx.navigation.NavDestination destination;
+ property public final String id;
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
+ property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+ property public androidx.lifecycle.ViewModelStore viewModelStore;
+ }
+
+ public final class NavDeepLink {
+ method public String? getAction();
+ method public String? getMimeType();
+ method public String? getUriPattern();
+ property public final String? action;
+ property public final String? mimeType;
+ property public final String? uriPattern;
+ }
+
+ public static final class NavDeepLink.Builder {
+ method public androidx.navigation.NavDeepLink build();
+ method public static androidx.navigation.NavDeepLink.Builder fromAction(String action);
+ method public static androidx.navigation.NavDeepLink.Builder fromMimeType(String mimeType);
+ method public static androidx.navigation.NavDeepLink.Builder fromUriPattern(String uriPattern);
+ method public androidx.navigation.NavDeepLink.Builder setAction(String action);
+ method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
+ method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
+ }
+
+ @kotlin.DslMarker public @interface NavDeepLinkDsl {
+ }
+
+ @androidx.navigation.NavDeepLinkDsl public final class NavDeepLinkDslBuilder {
+ ctor public NavDeepLinkDslBuilder();
+ method public String? getAction();
+ method public String? getMimeType();
+ method public String? getUriPattern();
+ method public void setAction(String?);
+ method public void setMimeType(String?);
+ method public void setUriPattern(String?);
+ property public final String? action;
+ property public final String? mimeType;
+ property public final String? uriPattern;
+ }
+
+ public final class NavDeepLinkDslBuilderKt {
+ method public static androidx.navigation.NavDeepLink navDeepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+ }
+
+ public class NavDeepLinkRequest {
+ method public String? getAction();
+ method public String? getMimeType();
+ method public android.net.Uri? getUri();
+ property public String? action;
+ property public String? mimeType;
+ property public android.net.Uri? uri;
+ }
+
+ public static final class NavDeepLinkRequest.Builder {
+ method public androidx.navigation.NavDeepLinkRequest build();
+ method public static androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+ method public static androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+ method public static androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+ method public androidx.navigation.NavDeepLinkRequest.Builder setAction(String action);
+ method public androidx.navigation.NavDeepLinkRequest.Builder setMimeType(String mimeType);
+ method public androidx.navigation.NavDeepLinkRequest.Builder setUri(android.net.Uri uri);
+ field public static final androidx.navigation.NavDeepLinkRequest.Builder.Companion Companion;
+ }
+
+ public static final class NavDeepLinkRequest.Builder.Companion {
+ method public androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+ method public androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+ method public androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+ }
+
+ public class NavDestination {
+ ctor public NavDestination(String navigatorName);
+ ctor public NavDestination(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method public final void addArgument(String argumentName, androidx.navigation.NavArgument argument);
+ method public final void addDeepLink(String uriPattern);
+ method public final void addDeepLink(androidx.navigation.NavDeepLink navDeepLink);
+ method public final String? fillInLabel(android.content.Context context, android.os.Bundle? bundle);
+ method public final androidx.navigation.NavAction? getAction(@IdRes int id);
+ method public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> getArguments();
+ method public static final kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+ method @IdRes public final int getId();
+ method public final CharSequence? getLabel();
+ method public final String getNavigatorName();
+ method public final androidx.navigation.NavGraph? getParent();
+ method public final String? getRoute();
+ method public boolean hasDeepLink(android.net.Uri deepLink);
+ method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+ method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
+ method protected static final <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+ method public final void putAction(@IdRes int actionId, @IdRes int destId);
+ method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
+ method public final void removeAction(@IdRes int actionId);
+ method public final void removeArgument(String argumentName);
+ method public final void setId(@IdRes int);
+ method public final void setLabel(CharSequence?);
+ method public final void setRoute(String?);
+ property public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> arguments;
+ property @IdRes public final int id;
+ property public final CharSequence? label;
+ property public final String navigatorName;
+ property public final androidx.navigation.NavGraph? parent;
+ property public final String? route;
+ field public static final androidx.navigation.NavDestination.Companion Companion;
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface NavDestination.ClassType {
+ method public abstract kotlin.reflect.KClass<?> value();
+ property public abstract kotlin.reflect.KClass<?> value;
+ }
+
+ public static final class NavDestination.Companion {
+ method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+ method protected <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+ }
+
+ @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
+ ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+ ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
+ method @Deprecated public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
+ method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
+ method public D build();
+ method public final void deepLink(String uriPattern);
+ method public final void deepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+ method public final int getId();
+ method public final CharSequence? getLabel();
+ method protected final androidx.navigation.Navigator<? extends D> getNavigator();
+ method public final String? getRoute();
+ method public final void setLabel(CharSequence?);
+ property public final int id;
+ property public final CharSequence? label;
+ property protected final androidx.navigation.Navigator<? extends D> navigator;
+ property public final String? route;
+ }
+
+ @kotlin.DslMarker public @interface NavDestinationDsl {
+ }
+
+ public interface NavDirections {
+ method @IdRes public int getActionId();
+ method public android.os.Bundle getArguments();
+ property @IdRes public abstract int actionId;
+ property public abstract android.os.Bundle arguments;
+ }
+
+ public class NavGraph extends androidx.navigation.NavDestination implements java.lang.Iterable<androidx.navigation.NavDestination> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public NavGraph(androidx.navigation.Navigator<? extends androidx.navigation.NavGraph> navGraphNavigator);
+ method public final void addAll(androidx.navigation.NavGraph other);
+ method public final void addDestination(androidx.navigation.NavDestination node);
+ method public final void addDestinations(java.util.Collection<? extends androidx.navigation.NavDestination> nodes);
+ method public final void addDestinations(androidx.navigation.NavDestination... nodes);
+ method public final void clear();
+ method public final androidx.navigation.NavDestination? findNode(@IdRes int resId);
+ method public final androidx.navigation.NavDestination? findNode(String? route);
+ method public static final androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+ method @Deprecated @IdRes public final int getStartDestination();
+ method @IdRes public final int getStartDestinationId();
+ method public final String? getStartDestinationRoute();
+ method public final java.util.Iterator<androidx.navigation.NavDestination> iterator();
+ method public final void remove(androidx.navigation.NavDestination node);
+ method public final void setStartDestination(int startDestId);
+ method public final void setStartDestination(String startDestRoute);
+ property @IdRes public final int startDestinationId;
+ property public final String? startDestinationRoute;
+ field public static final androidx.navigation.NavGraph.Companion Companion;
+ }
+
+ public static final class NavGraph.Companion {
+ method public androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+ }
+
+ @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
+ ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+ ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
+ method public final void addDestination(androidx.navigation.NavDestination destination);
+ method public androidx.navigation.NavGraph build();
+ method public final <D extends androidx.navigation.NavDestination> void destination(androidx.navigation.NavDestinationBuilder<? extends D> navDestination);
+ method public final androidx.navigation.NavigatorProvider getProvider();
+ method public final operator void unaryPlus(androidx.navigation.NavDestination);
+ property public final androidx.navigation.NavigatorProvider provider;
+ }
+
+ public final class NavGraphBuilderKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavGraphKt {
+ method public static operator boolean contains(androidx.navigation.NavGraph, @IdRes int id);
+ method public static operator boolean contains(androidx.navigation.NavGraph, String route);
+ method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);
+ method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, String route);
+ method public static inline operator void minusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+ method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+ method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavGraph other);
+ }
+
+ @androidx.navigation.Navigator.Name("navigation") public class NavGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.NavGraph> {
+ ctor public NavGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public androidx.navigation.NavGraph createDestination();
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+ }
+
+ public final class NavOptions {
+ method @AnimRes @AnimatorRes public int getEnterAnim();
+ method @AnimRes @AnimatorRes public int getExitAnim();
+ method @AnimRes @AnimatorRes public int getPopEnterAnim();
+ method @AnimRes @AnimatorRes public int getPopExitAnim();
+ method @Deprecated @IdRes public int getPopUpTo();
+ method @IdRes public int getPopUpToId();
+ method public String? getPopUpToRoute();
+ method public boolean isPopUpToInclusive();
+ method public boolean shouldLaunchSingleTop();
+ method public boolean shouldPopUpToSaveState();
+ method public boolean shouldRestoreState();
+ property @AnimRes @AnimatorRes public final int enterAnim;
+ property @AnimRes @AnimatorRes public final int exitAnim;
+ property @AnimRes @AnimatorRes public final int popEnterAnim;
+ property @AnimRes @AnimatorRes public final int popExitAnim;
+ property @IdRes public final int popUpToId;
+ property public final String? popUpToRoute;
+ }
+
+ public static final class NavOptions.Builder {
+ ctor public NavOptions.Builder();
+ method public androidx.navigation.NavOptions build();
+ method public androidx.navigation.NavOptions.Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim);
+ method public androidx.navigation.NavOptions.Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim);
+ method public androidx.navigation.NavOptions.Builder setLaunchSingleTop(boolean singleTop);
+ method public androidx.navigation.NavOptions.Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim);
+ method public androidx.navigation.NavOptions.Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive, optional boolean saveState);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive, optional boolean saveState);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive);
+ method public androidx.navigation.NavOptions.Builder setRestoreState(boolean restoreState);
+ }
+
+ @androidx.navigation.NavOptionsDsl public final class NavOptionsBuilder {
+ ctor public NavOptionsBuilder();
+ method public void anim(kotlin.jvm.functions.Function1<? super androidx.navigation.AnimBuilder,kotlin.Unit> animBuilder);
+ method public boolean getLaunchSingleTop();
+ method @Deprecated public int getPopUpTo();
+ method public int getPopUpToId();
+ method public String? getPopUpToRoute();
+ method public boolean getRestoreState();
+ method public void popUpTo(@IdRes int id, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+ method public void popUpTo(String route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+ method public void setLaunchSingleTop(boolean);
+ method @Deprecated public void setPopUpTo(int);
+ method public void setRestoreState(boolean);
+ property public final boolean launchSingleTop;
+ property @Deprecated public final int popUpTo;
+ property public final int popUpToId;
+ property public final String? popUpToRoute;
+ property public final boolean restoreState;
+ }
+
+ public final class NavOptionsBuilderKt {
+ method public static androidx.navigation.NavOptions navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+ }
+
+ @kotlin.DslMarker public @interface NavOptionsDsl {
+ }
+
+ public abstract class NavType<T> {
+ ctor public NavType(boolean isNullableAllowed);
+ method public static androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+ method public abstract operator T? get(android.os.Bundle bundle, String key);
+ method public String getName();
+ method public boolean isNullableAllowed();
+ method public abstract T parseValue(String value);
+ method public T parseValue(String value, T previousValue);
+ method public abstract void put(android.os.Bundle bundle, String key, T value);
+ method public String serializeAsValue(T value);
+ property public boolean isNullableAllowed;
+ property public String name;
+ field public static final androidx.navigation.NavType<boolean[]> BoolArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
+ field public static final androidx.navigation.NavType.Companion Companion;
+ field public static final androidx.navigation.NavType<float[]> FloatArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
+ field public static final androidx.navigation.NavType<int[]> IntArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
+ field public static final androidx.navigation.NavType<long[]> LongArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Long> LongType;
+ field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
+ field public static final androidx.navigation.NavType<java.lang.String[]> StringArrayType;
+ field public static final androidx.navigation.NavType<java.lang.String> StringType;
+ }
+
+ public static final class NavType.Companion {
+ method public androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+ }
+
+ public static final class NavType.EnumType<D extends java.lang.Enum<?>> extends androidx.navigation.NavType.SerializableType<D> {
+ ctor public NavType.EnumType(Class<D> type);
+ property public String name;
+ }
+
+ public static final class NavType.ParcelableArrayType<D extends android.os.Parcelable> extends androidx.navigation.NavType<D[]> {
+ ctor public NavType.ParcelableArrayType(Class<D> type);
+ method public D![]? get(android.os.Bundle bundle, String key);
+ method public D![] parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D![]? value);
+ property public String name;
+ }
+
+ public static final class NavType.ParcelableType<D> extends androidx.navigation.NavType<D> {
+ ctor public NavType.ParcelableType(Class<D> type);
+ method public D? get(android.os.Bundle bundle, String key);
+ method public D parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D value);
+ property public String name;
+ }
+
+ public static final class NavType.SerializableArrayType<D extends java.io.Serializable> extends androidx.navigation.NavType<D[]> {
+ ctor public NavType.SerializableArrayType(Class<D> type);
+ method public D![]? get(android.os.Bundle bundle, String key);
+ method public D![] parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D![]? value);
+ property public String name;
+ }
+
+ public static class NavType.SerializableType<D extends java.io.Serializable> extends androidx.navigation.NavType<D> {
+ ctor public NavType.SerializableType(Class<D> type);
+ method public D? get(android.os.Bundle bundle, String key);
+ method public D parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D value);
+ property public String name;
+ }
+
+ public abstract class Navigator<D extends androidx.navigation.NavDestination> {
+ ctor public Navigator();
+ method public abstract D createDestination();
+ method protected final androidx.navigation.NavigatorState getState();
+ method public final boolean isAttached();
+ method public void navigate(java.util.List<androidx.navigation.NavBackStackEntry> entries, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method public androidx.navigation.NavDestination? navigate(D destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @CallSuper public void onAttach(androidx.navigation.NavigatorState state);
+ method public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+ method public void onRestoreState(android.os.Bundle savedState);
+ method public android.os.Bundle? onSaveState();
+ method public void popBackStack(androidx.navigation.NavBackStackEntry popUpTo, boolean savedState);
+ method public boolean popBackStack();
+ property public final boolean isAttached;
+ property protected final androidx.navigation.NavigatorState state;
+ }
+
+ public static interface Navigator.Extras {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface Navigator.Name {
+ method public abstract String value();
+ property public abstract String value;
+ }
+
+ public class NavigatorProvider {
+ ctor public NavigatorProvider();
+ method public final androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method @CallSuper public androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method public final <T extends androidx.navigation.Navigator<?>> T getNavigator(Class<T> navigatorClass);
+ method @CallSuper public <T extends androidx.navigation.Navigator<?>> T getNavigator(String name);
+ }
+
+ public final class NavigatorProviderKt {
+ method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, String name);
+ method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);
+ method public static inline operator void plusAssign(androidx.navigation.NavigatorProvider, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method public static inline operator androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? set(androidx.navigation.NavigatorProvider, String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ }
+
+ public abstract class NavigatorState {
+ ctor public NavigatorState();
+ method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+ method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+ method @CallSuper public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+ method @CallSuper public void onLaunchSingleTopWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+ method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+ method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+ method @CallSuper public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+ method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
+ method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
+ }
+
+ @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
+ ctor public PopUpToBuilder();
+ method public boolean getInclusive();
+ method public boolean getSaveState();
+ method public void setInclusive(boolean);
+ method public void setSaveState(boolean);
+ property public final boolean inclusive;
+ property public final boolean saveState;
+ }
+
+}
+
diff --git a/navigation/navigation-common/api/current.ignore b/navigation/navigation-common/api/current.ignore
deleted file mode 100644
index 8eebd5d..0000000
--- a/navigation/navigation-common/api/current.ignore
+++ /dev/null
@@ -1,9 +0,0 @@
-// Baseline format: 1.0
-InvalidNullConversion: androidx.navigation.NavType#put(android.os.Bundle, String, T) parameter #2:
- Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.navigation.NavType.put(android.os.Bundle bundle, String key, T value)
-InvalidNullConversion: androidx.navigation.NavType.ParcelableType#put(android.os.Bundle, String, D) parameter #2:
- Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.navigation.NavType.ParcelableType.put(android.os.Bundle bundle, String key, D value)
-
-
-RemovedClass: androidx.navigation.NavArgsLazyKt:
- Removed class androidx.navigation.NavArgsLazyKt
diff --git a/navigation/navigation-common/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-common/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6d0229
--- /dev/null
+++ b/navigation/navigation-common/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,530 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+ public final class ActionOnlyNavDirections implements androidx.navigation.NavDirections {
+ ctor public ActionOnlyNavDirections(int actionId);
+ method public int component1();
+ method public androidx.navigation.ActionOnlyNavDirections copy(int actionId);
+ method public int getActionId();
+ method public android.os.Bundle getArguments();
+ property public int actionId;
+ property public android.os.Bundle arguments;
+ }
+
+ @androidx.navigation.NavOptionsDsl public final class AnimBuilder {
+ ctor public AnimBuilder();
+ method public int getEnter();
+ method public int getExit();
+ method public int getPopEnter();
+ method public int getPopExit();
+ method public void setEnter(int);
+ method public void setExit(int);
+ method public void setPopEnter(int);
+ method public void setPopExit(int);
+ property public final int enter;
+ property public final int exit;
+ property public final int popEnter;
+ property public final int popExit;
+ }
+
+ public interface FloatingWindow {
+ }
+
+ public final class NamedNavArgument {
+ method public operator String component1();
+ method public operator androidx.navigation.NavArgument component2();
+ method public androidx.navigation.NavArgument getArgument();
+ method public String getName();
+ property public final androidx.navigation.NavArgument argument;
+ property public final String name;
+ }
+
+ public final class NamedNavArgumentKt {
+ method public static androidx.navigation.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavAction {
+ ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions, optional android.os.Bundle? defaultArguments);
+ ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions);
+ ctor public NavAction(@IdRes int destinationId);
+ method public android.os.Bundle? getDefaultArguments();
+ method public int getDestinationId();
+ method public androidx.navigation.NavOptions? getNavOptions();
+ method public void setDefaultArguments(android.os.Bundle?);
+ method public void setNavOptions(androidx.navigation.NavOptions?);
+ property public final android.os.Bundle? defaultArguments;
+ property public final int destinationId;
+ property public final androidx.navigation.NavOptions? navOptions;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class NavActionBuilder {
+ ctor public NavActionBuilder();
+ method public java.util.Map<java.lang.String,java.lang.Object> getDefaultArguments();
+ method public int getDestinationId();
+ method public void navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+ method public void setDestinationId(int);
+ property public final java.util.Map<java.lang.String,java.lang.Object> defaultArguments;
+ property public final int destinationId;
+ }
+
+ public interface NavArgs {
+ }
+
+ public final class NavArgsLazy<Args extends androidx.navigation.NavArgs> implements kotlin.Lazy<Args> {
+ ctor public NavArgsLazy(kotlin.reflect.KClass<Args> navArgsClass, kotlin.jvm.functions.Function0<android.os.Bundle> argumentProducer);
+ method public Args getValue();
+ method public boolean isInitialized();
+ property public Args value;
+ }
+
+ public final class NavArgument {
+ method public Object? getDefaultValue();
+ method public androidx.navigation.NavType<java.lang.Object> getType();
+ method public boolean isDefaultValuePresent();
+ method public boolean isNullable();
+ property public final Object? defaultValue;
+ property public final boolean isDefaultValuePresent;
+ property public final boolean isNullable;
+ property public final androidx.navigation.NavType<java.lang.Object> type;
+ }
+
+ public static final class NavArgument.Builder {
+ ctor public NavArgument.Builder();
+ method public androidx.navigation.NavArgument build();
+ method public androidx.navigation.NavArgument.Builder setDefaultValue(Object? defaultValue);
+ method public androidx.navigation.NavArgument.Builder setIsNullable(boolean isNullable);
+ method public <T> androidx.navigation.NavArgument.Builder setType(androidx.navigation.NavType<T> type);
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class NavArgumentBuilder {
+ ctor public NavArgumentBuilder();
+ method public androidx.navigation.NavArgument build();
+ method public Object? getDefaultValue();
+ method public boolean getNullable();
+ method public androidx.navigation.NavType<?> getType();
+ method public void setDefaultValue(Object?);
+ method public void setNullable(boolean);
+ method public void setType(androidx.navigation.NavType<?>);
+ property public final Object? defaultValue;
+ property public final boolean nullable;
+ property public final androidx.navigation.NavType<?> type;
+ }
+
+ public final class NavBackStackEntry implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+ method public android.os.Bundle? getArguments();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ method public androidx.navigation.NavDestination getDestination();
+ method public String getId();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method public androidx.lifecycle.SavedStateHandle getSavedStateHandle();
+ method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+ method public androidx.lifecycle.ViewModelStore getViewModelStore();
+ property public final android.os.Bundle? arguments;
+ property public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+ property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+ property public final androidx.navigation.NavDestination destination;
+ property public final String id;
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
+ property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+ property public androidx.lifecycle.ViewModelStore viewModelStore;
+ }
+
+ public final class NavDeepLink {
+ method public String? getAction();
+ method public String? getMimeType();
+ method public String? getUriPattern();
+ property public final String? action;
+ property public final String? mimeType;
+ property public final String? uriPattern;
+ }
+
+ public static final class NavDeepLink.Builder {
+ method public androidx.navigation.NavDeepLink build();
+ method public static androidx.navigation.NavDeepLink.Builder fromAction(String action);
+ method public static androidx.navigation.NavDeepLink.Builder fromMimeType(String mimeType);
+ method public static androidx.navigation.NavDeepLink.Builder fromUriPattern(String uriPattern);
+ method public androidx.navigation.NavDeepLink.Builder setAction(String action);
+ method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
+ method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
+ }
+
+ @kotlin.DslMarker public @interface NavDeepLinkDsl {
+ }
+
+ @androidx.navigation.NavDeepLinkDsl public final class NavDeepLinkDslBuilder {
+ ctor public NavDeepLinkDslBuilder();
+ method public String? getAction();
+ method public String? getMimeType();
+ method public String? getUriPattern();
+ method public void setAction(String?);
+ method public void setMimeType(String?);
+ method public void setUriPattern(String?);
+ property public final String? action;
+ property public final String? mimeType;
+ property public final String? uriPattern;
+ }
+
+ public final class NavDeepLinkDslBuilderKt {
+ method public static androidx.navigation.NavDeepLink navDeepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+ }
+
+ public class NavDeepLinkRequest {
+ method public String? getAction();
+ method public String? getMimeType();
+ method public android.net.Uri? getUri();
+ property public String? action;
+ property public String? mimeType;
+ property public android.net.Uri? uri;
+ }
+
+ public static final class NavDeepLinkRequest.Builder {
+ method public androidx.navigation.NavDeepLinkRequest build();
+ method public static androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+ method public static androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+ method public static androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+ method public androidx.navigation.NavDeepLinkRequest.Builder setAction(String action);
+ method public androidx.navigation.NavDeepLinkRequest.Builder setMimeType(String mimeType);
+ method public androidx.navigation.NavDeepLinkRequest.Builder setUri(android.net.Uri uri);
+ field public static final androidx.navigation.NavDeepLinkRequest.Builder.Companion Companion;
+ }
+
+ public static final class NavDeepLinkRequest.Builder.Companion {
+ method public androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+ method public androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+ method public androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+ }
+
+ public class NavDestination {
+ ctor public NavDestination(String navigatorName);
+ ctor public NavDestination(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method public final void addArgument(String argumentName, androidx.navigation.NavArgument argument);
+ method public final void addDeepLink(String uriPattern);
+ method public final void addDeepLink(androidx.navigation.NavDeepLink navDeepLink);
+ method public final String? fillInLabel(android.content.Context context, android.os.Bundle? bundle);
+ method public final androidx.navigation.NavAction? getAction(@IdRes int id);
+ method public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> getArguments();
+ method public static final kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+ method @IdRes public final int getId();
+ method public final CharSequence? getLabel();
+ method public final String getNavigatorName();
+ method public final androidx.navigation.NavGraph? getParent();
+ method public final String? getRoute();
+ method public boolean hasDeepLink(android.net.Uri deepLink);
+ method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+ method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
+ method protected static final <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+ method public final void putAction(@IdRes int actionId, @IdRes int destId);
+ method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
+ method public final void removeAction(@IdRes int actionId);
+ method public final void removeArgument(String argumentName);
+ method public final void setId(@IdRes int);
+ method public final void setLabel(CharSequence?);
+ method public final void setRoute(String?);
+ property public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> arguments;
+ property @IdRes public final int id;
+ property public final CharSequence? label;
+ property public final String navigatorName;
+ property public final androidx.navigation.NavGraph? parent;
+ property public final String? route;
+ field public static final androidx.navigation.NavDestination.Companion Companion;
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface NavDestination.ClassType {
+ method public abstract kotlin.reflect.KClass<?> value();
+ property public abstract kotlin.reflect.KClass<?> value;
+ }
+
+ public static final class NavDestination.Companion {
+ method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+ method protected <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+ }
+
+ @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
+ ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+ ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
+ method @Deprecated public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
+ method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
+ method public D build();
+ method public final void deepLink(String uriPattern);
+ method public final void deepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+ method public final int getId();
+ method public final CharSequence? getLabel();
+ method protected final androidx.navigation.Navigator<? extends D> getNavigator();
+ method public final String? getRoute();
+ method public final void setLabel(CharSequence?);
+ property public final int id;
+ property public final CharSequence? label;
+ property protected final androidx.navigation.Navigator<? extends D> navigator;
+ property public final String? route;
+ }
+
+ @kotlin.DslMarker public @interface NavDestinationDsl {
+ }
+
+ public interface NavDirections {
+ method @IdRes public int getActionId();
+ method public android.os.Bundle getArguments();
+ property @IdRes public abstract int actionId;
+ property public abstract android.os.Bundle arguments;
+ }
+
+ public class NavGraph extends androidx.navigation.NavDestination implements java.lang.Iterable<androidx.navigation.NavDestination> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public NavGraph(androidx.navigation.Navigator<? extends androidx.navigation.NavGraph> navGraphNavigator);
+ method public final void addAll(androidx.navigation.NavGraph other);
+ method public final void addDestination(androidx.navigation.NavDestination node);
+ method public final void addDestinations(java.util.Collection<? extends androidx.navigation.NavDestination> nodes);
+ method public final void addDestinations(androidx.navigation.NavDestination... nodes);
+ method public final void clear();
+ method public final androidx.navigation.NavDestination? findNode(@IdRes int resId);
+ method public final androidx.navigation.NavDestination? findNode(String? route);
+ method public static final androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+ method @Deprecated @IdRes public final int getStartDestination();
+ method @IdRes public final int getStartDestinationId();
+ method public final String? getStartDestinationRoute();
+ method public final java.util.Iterator<androidx.navigation.NavDestination> iterator();
+ method public final void remove(androidx.navigation.NavDestination node);
+ method public final void setStartDestination(int startDestId);
+ method public final void setStartDestination(String startDestRoute);
+ property @IdRes public final int startDestinationId;
+ property public final String? startDestinationRoute;
+ field public static final androidx.navigation.NavGraph.Companion Companion;
+ }
+
+ public static final class NavGraph.Companion {
+ method public androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+ }
+
+ @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
+ ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+ ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
+ method public final void addDestination(androidx.navigation.NavDestination destination);
+ method public androidx.navigation.NavGraph build();
+ method public final <D extends androidx.navigation.NavDestination> void destination(androidx.navigation.NavDestinationBuilder<? extends D> navDestination);
+ method public final androidx.navigation.NavigatorProvider getProvider();
+ method public final operator void unaryPlus(androidx.navigation.NavDestination);
+ property public final androidx.navigation.NavigatorProvider provider;
+ }
+
+ public final class NavGraphBuilderKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavGraphKt {
+ method public static operator boolean contains(androidx.navigation.NavGraph, @IdRes int id);
+ method public static operator boolean contains(androidx.navigation.NavGraph, String route);
+ method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);
+ method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, String route);
+ method public static inline operator void minusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+ method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+ method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavGraph other);
+ }
+
+ @androidx.navigation.Navigator.Name("navigation") public class NavGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.NavGraph> {
+ ctor public NavGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public androidx.navigation.NavGraph createDestination();
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+ }
+
+ public final class NavOptions {
+ method @AnimRes @AnimatorRes public int getEnterAnim();
+ method @AnimRes @AnimatorRes public int getExitAnim();
+ method @AnimRes @AnimatorRes public int getPopEnterAnim();
+ method @AnimRes @AnimatorRes public int getPopExitAnim();
+ method @Deprecated @IdRes public int getPopUpTo();
+ method @IdRes public int getPopUpToId();
+ method public String? getPopUpToRoute();
+ method public boolean isPopUpToInclusive();
+ method public boolean shouldLaunchSingleTop();
+ method public boolean shouldPopUpToSaveState();
+ method public boolean shouldRestoreState();
+ property @AnimRes @AnimatorRes public final int enterAnim;
+ property @AnimRes @AnimatorRes public final int exitAnim;
+ property @AnimRes @AnimatorRes public final int popEnterAnim;
+ property @AnimRes @AnimatorRes public final int popExitAnim;
+ property @IdRes public final int popUpToId;
+ property public final String? popUpToRoute;
+ }
+
+ public static final class NavOptions.Builder {
+ ctor public NavOptions.Builder();
+ method public androidx.navigation.NavOptions build();
+ method public androidx.navigation.NavOptions.Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim);
+ method public androidx.navigation.NavOptions.Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim);
+ method public androidx.navigation.NavOptions.Builder setLaunchSingleTop(boolean singleTop);
+ method public androidx.navigation.NavOptions.Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim);
+ method public androidx.navigation.NavOptions.Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive, optional boolean saveState);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive, optional boolean saveState);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive);
+ method public androidx.navigation.NavOptions.Builder setRestoreState(boolean restoreState);
+ }
+
+ @androidx.navigation.NavOptionsDsl public final class NavOptionsBuilder {
+ ctor public NavOptionsBuilder();
+ method public void anim(kotlin.jvm.functions.Function1<? super androidx.navigation.AnimBuilder,kotlin.Unit> animBuilder);
+ method public boolean getLaunchSingleTop();
+ method @Deprecated public int getPopUpTo();
+ method public int getPopUpToId();
+ method public String? getPopUpToRoute();
+ method public boolean getRestoreState();
+ method public void popUpTo(@IdRes int id, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+ method public void popUpTo(String route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+ method public void setLaunchSingleTop(boolean);
+ method @Deprecated public void setPopUpTo(int);
+ method public void setRestoreState(boolean);
+ property public final boolean launchSingleTop;
+ property @Deprecated public final int popUpTo;
+ property public final int popUpToId;
+ property public final String? popUpToRoute;
+ property public final boolean restoreState;
+ }
+
+ public final class NavOptionsBuilderKt {
+ method public static androidx.navigation.NavOptions navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+ }
+
+ @kotlin.DslMarker public @interface NavOptionsDsl {
+ }
+
+ public abstract class NavType<T> {
+ ctor public NavType(boolean isNullableAllowed);
+ method public static androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+ method public abstract operator T? get(android.os.Bundle bundle, String key);
+ method public String getName();
+ method public boolean isNullableAllowed();
+ method public abstract T parseValue(String value);
+ method public T parseValue(String value, T previousValue);
+ method public abstract void put(android.os.Bundle bundle, String key, T value);
+ method public String serializeAsValue(T value);
+ property public boolean isNullableAllowed;
+ property public String name;
+ field public static final androidx.navigation.NavType<boolean[]> BoolArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
+ field public static final androidx.navigation.NavType.Companion Companion;
+ field public static final androidx.navigation.NavType<float[]> FloatArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
+ field public static final androidx.navigation.NavType<int[]> IntArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
+ field public static final androidx.navigation.NavType<long[]> LongArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Long> LongType;
+ field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
+ field public static final androidx.navigation.NavType<java.lang.String[]> StringArrayType;
+ field public static final androidx.navigation.NavType<java.lang.String> StringType;
+ }
+
+ public static final class NavType.Companion {
+ method public androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+ }
+
+ public static final class NavType.EnumType<D extends java.lang.Enum<?>> extends androidx.navigation.NavType.SerializableType<D> {
+ ctor public NavType.EnumType(Class<D> type);
+ property public String name;
+ }
+
+ public static final class NavType.ParcelableArrayType<D extends android.os.Parcelable> extends androidx.navigation.NavType<D[]> {
+ ctor public NavType.ParcelableArrayType(Class<D> type);
+ method public D![]? get(android.os.Bundle bundle, String key);
+ method public D![] parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D![]? value);
+ property public String name;
+ }
+
+ public static final class NavType.ParcelableType<D> extends androidx.navigation.NavType<D> {
+ ctor public NavType.ParcelableType(Class<D> type);
+ method public D? get(android.os.Bundle bundle, String key);
+ method public D parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D value);
+ property public String name;
+ }
+
+ public static final class NavType.SerializableArrayType<D extends java.io.Serializable> extends androidx.navigation.NavType<D[]> {
+ ctor public NavType.SerializableArrayType(Class<D> type);
+ method public D![]? get(android.os.Bundle bundle, String key);
+ method public D![] parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D![]? value);
+ property public String name;
+ }
+
+ public static class NavType.SerializableType<D extends java.io.Serializable> extends androidx.navigation.NavType<D> {
+ ctor public NavType.SerializableType(Class<D> type);
+ method public D? get(android.os.Bundle bundle, String key);
+ method public D parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D value);
+ property public String name;
+ }
+
+ public abstract class Navigator<D extends androidx.navigation.NavDestination> {
+ ctor public Navigator();
+ method public abstract D createDestination();
+ method protected final androidx.navigation.NavigatorState getState();
+ method public final boolean isAttached();
+ method public void navigate(java.util.List<androidx.navigation.NavBackStackEntry> entries, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method public androidx.navigation.NavDestination? navigate(D destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @CallSuper public void onAttach(androidx.navigation.NavigatorState state);
+ method public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+ method public void onRestoreState(android.os.Bundle savedState);
+ method public android.os.Bundle? onSaveState();
+ method public void popBackStack(androidx.navigation.NavBackStackEntry popUpTo, boolean savedState);
+ method public boolean popBackStack();
+ property public final boolean isAttached;
+ property protected final androidx.navigation.NavigatorState state;
+ }
+
+ public static interface Navigator.Extras {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface Navigator.Name {
+ method public abstract String value();
+ property public abstract String value;
+ }
+
+ public class NavigatorProvider {
+ ctor public NavigatorProvider();
+ method public final androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method @CallSuper public androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method public final <T extends androidx.navigation.Navigator<?>> T getNavigator(Class<T> navigatorClass);
+ method @CallSuper public <T extends androidx.navigation.Navigator<?>> T getNavigator(String name);
+ }
+
+ public final class NavigatorProviderKt {
+ method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, String name);
+ method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);
+ method public static inline operator void plusAssign(androidx.navigation.NavigatorProvider, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method public static inline operator androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? set(androidx.navigation.NavigatorProvider, String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ }
+
+ public abstract class NavigatorState {
+ ctor public NavigatorState();
+ method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+ method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+ method @CallSuper public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+ method @CallSuper public void onLaunchSingleTopWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+ method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+ method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+ method @CallSuper public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+ method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
+ method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
+ }
+
+ @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
+ ctor public PopUpToBuilder();
+ method public boolean getInclusive();
+ method public boolean getSaveState();
+ method public void setInclusive(boolean);
+ method public void setSaveState(boolean);
+ property public final boolean inclusive;
+ property public final boolean saveState;
+ }
+
+}
+
diff --git a/navigation/navigation-common/api/res-2.6.0-beta02.txt b/navigation/navigation-common/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-common/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-common/api/restricted_2.6.0-beta02.txt b/navigation/navigation-common/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6d0229
--- /dev/null
+++ b/navigation/navigation-common/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,530 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+ public final class ActionOnlyNavDirections implements androidx.navigation.NavDirections {
+ ctor public ActionOnlyNavDirections(int actionId);
+ method public int component1();
+ method public androidx.navigation.ActionOnlyNavDirections copy(int actionId);
+ method public int getActionId();
+ method public android.os.Bundle getArguments();
+ property public int actionId;
+ property public android.os.Bundle arguments;
+ }
+
+ @androidx.navigation.NavOptionsDsl public final class AnimBuilder {
+ ctor public AnimBuilder();
+ method public int getEnter();
+ method public int getExit();
+ method public int getPopEnter();
+ method public int getPopExit();
+ method public void setEnter(int);
+ method public void setExit(int);
+ method public void setPopEnter(int);
+ method public void setPopExit(int);
+ property public final int enter;
+ property public final int exit;
+ property public final int popEnter;
+ property public final int popExit;
+ }
+
+ public interface FloatingWindow {
+ }
+
+ public final class NamedNavArgument {
+ method public operator String component1();
+ method public operator androidx.navigation.NavArgument component2();
+ method public androidx.navigation.NavArgument getArgument();
+ method public String getName();
+ property public final androidx.navigation.NavArgument argument;
+ property public final String name;
+ }
+
+ public final class NamedNavArgumentKt {
+ method public static androidx.navigation.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavAction {
+ ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions, optional android.os.Bundle? defaultArguments);
+ ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions);
+ ctor public NavAction(@IdRes int destinationId);
+ method public android.os.Bundle? getDefaultArguments();
+ method public int getDestinationId();
+ method public androidx.navigation.NavOptions? getNavOptions();
+ method public void setDefaultArguments(android.os.Bundle?);
+ method public void setNavOptions(androidx.navigation.NavOptions?);
+ property public final android.os.Bundle? defaultArguments;
+ property public final int destinationId;
+ property public final androidx.navigation.NavOptions? navOptions;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class NavActionBuilder {
+ ctor public NavActionBuilder();
+ method public java.util.Map<java.lang.String,java.lang.Object> getDefaultArguments();
+ method public int getDestinationId();
+ method public void navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+ method public void setDestinationId(int);
+ property public final java.util.Map<java.lang.String,java.lang.Object> defaultArguments;
+ property public final int destinationId;
+ }
+
+ public interface NavArgs {
+ }
+
+ public final class NavArgsLazy<Args extends androidx.navigation.NavArgs> implements kotlin.Lazy<Args> {
+ ctor public NavArgsLazy(kotlin.reflect.KClass<Args> navArgsClass, kotlin.jvm.functions.Function0<android.os.Bundle> argumentProducer);
+ method public Args getValue();
+ method public boolean isInitialized();
+ property public Args value;
+ }
+
+ public final class NavArgument {
+ method public Object? getDefaultValue();
+ method public androidx.navigation.NavType<java.lang.Object> getType();
+ method public boolean isDefaultValuePresent();
+ method public boolean isNullable();
+ property public final Object? defaultValue;
+ property public final boolean isDefaultValuePresent;
+ property public final boolean isNullable;
+ property public final androidx.navigation.NavType<java.lang.Object> type;
+ }
+
+ public static final class NavArgument.Builder {
+ ctor public NavArgument.Builder();
+ method public androidx.navigation.NavArgument build();
+ method public androidx.navigation.NavArgument.Builder setDefaultValue(Object? defaultValue);
+ method public androidx.navigation.NavArgument.Builder setIsNullable(boolean isNullable);
+ method public <T> androidx.navigation.NavArgument.Builder setType(androidx.navigation.NavType<T> type);
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class NavArgumentBuilder {
+ ctor public NavArgumentBuilder();
+ method public androidx.navigation.NavArgument build();
+ method public Object? getDefaultValue();
+ method public boolean getNullable();
+ method public androidx.navigation.NavType<?> getType();
+ method public void setDefaultValue(Object?);
+ method public void setNullable(boolean);
+ method public void setType(androidx.navigation.NavType<?>);
+ property public final Object? defaultValue;
+ property public final boolean nullable;
+ property public final androidx.navigation.NavType<?> type;
+ }
+
+ public final class NavBackStackEntry implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+ method public android.os.Bundle? getArguments();
+ method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+ method public androidx.navigation.NavDestination getDestination();
+ method public String getId();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method public androidx.lifecycle.SavedStateHandle getSavedStateHandle();
+ method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+ method public androidx.lifecycle.ViewModelStore getViewModelStore();
+ property public final android.os.Bundle? arguments;
+ property public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+ property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+ property public final androidx.navigation.NavDestination destination;
+ property public final String id;
+ property public androidx.lifecycle.Lifecycle lifecycle;
+ property public final androidx.lifecycle.SavedStateHandle savedStateHandle;
+ property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+ property public androidx.lifecycle.ViewModelStore viewModelStore;
+ }
+
+ public final class NavDeepLink {
+ method public String? getAction();
+ method public String? getMimeType();
+ method public String? getUriPattern();
+ property public final String? action;
+ property public final String? mimeType;
+ property public final String? uriPattern;
+ }
+
+ public static final class NavDeepLink.Builder {
+ method public androidx.navigation.NavDeepLink build();
+ method public static androidx.navigation.NavDeepLink.Builder fromAction(String action);
+ method public static androidx.navigation.NavDeepLink.Builder fromMimeType(String mimeType);
+ method public static androidx.navigation.NavDeepLink.Builder fromUriPattern(String uriPattern);
+ method public androidx.navigation.NavDeepLink.Builder setAction(String action);
+ method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
+ method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
+ }
+
+ @kotlin.DslMarker public @interface NavDeepLinkDsl {
+ }
+
+ @androidx.navigation.NavDeepLinkDsl public final class NavDeepLinkDslBuilder {
+ ctor public NavDeepLinkDslBuilder();
+ method public String? getAction();
+ method public String? getMimeType();
+ method public String? getUriPattern();
+ method public void setAction(String?);
+ method public void setMimeType(String?);
+ method public void setUriPattern(String?);
+ property public final String? action;
+ property public final String? mimeType;
+ property public final String? uriPattern;
+ }
+
+ public final class NavDeepLinkDslBuilderKt {
+ method public static androidx.navigation.NavDeepLink navDeepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+ }
+
+ public class NavDeepLinkRequest {
+ method public String? getAction();
+ method public String? getMimeType();
+ method public android.net.Uri? getUri();
+ property public String? action;
+ property public String? mimeType;
+ property public android.net.Uri? uri;
+ }
+
+ public static final class NavDeepLinkRequest.Builder {
+ method public androidx.navigation.NavDeepLinkRequest build();
+ method public static androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+ method public static androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+ method public static androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+ method public androidx.navigation.NavDeepLinkRequest.Builder setAction(String action);
+ method public androidx.navigation.NavDeepLinkRequest.Builder setMimeType(String mimeType);
+ method public androidx.navigation.NavDeepLinkRequest.Builder setUri(android.net.Uri uri);
+ field public static final androidx.navigation.NavDeepLinkRequest.Builder.Companion Companion;
+ }
+
+ public static final class NavDeepLinkRequest.Builder.Companion {
+ method public androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+ method public androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+ method public androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+ }
+
+ public class NavDestination {
+ ctor public NavDestination(String navigatorName);
+ ctor public NavDestination(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method public final void addArgument(String argumentName, androidx.navigation.NavArgument argument);
+ method public final void addDeepLink(String uriPattern);
+ method public final void addDeepLink(androidx.navigation.NavDeepLink navDeepLink);
+ method public final String? fillInLabel(android.content.Context context, android.os.Bundle? bundle);
+ method public final androidx.navigation.NavAction? getAction(@IdRes int id);
+ method public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> getArguments();
+ method public static final kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+ method @IdRes public final int getId();
+ method public final CharSequence? getLabel();
+ method public final String getNavigatorName();
+ method public final androidx.navigation.NavGraph? getParent();
+ method public final String? getRoute();
+ method public boolean hasDeepLink(android.net.Uri deepLink);
+ method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+ method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
+ method protected static final <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+ method public final void putAction(@IdRes int actionId, @IdRes int destId);
+ method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
+ method public final void removeAction(@IdRes int actionId);
+ method public final void removeArgument(String argumentName);
+ method public final void setId(@IdRes int);
+ method public final void setLabel(CharSequence?);
+ method public final void setRoute(String?);
+ property public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> arguments;
+ property @IdRes public final int id;
+ property public final CharSequence? label;
+ property public final String navigatorName;
+ property public final androidx.navigation.NavGraph? parent;
+ property public final String? route;
+ field public static final androidx.navigation.NavDestination.Companion Companion;
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface NavDestination.ClassType {
+ method public abstract kotlin.reflect.KClass<?> value();
+ property public abstract kotlin.reflect.KClass<?> value;
+ }
+
+ public static final class NavDestination.Companion {
+ method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+ method protected <C> Class<? extends C> parseClassFromName(android.content.Context context, String name, Class<? extends C> expectedClassType);
+ }
+
+ @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
+ ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+ ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
+ method @Deprecated public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
+ method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
+ method public D build();
+ method public final void deepLink(String uriPattern);
+ method public final void deepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+ method public final int getId();
+ method public final CharSequence? getLabel();
+ method protected final androidx.navigation.Navigator<? extends D> getNavigator();
+ method public final String? getRoute();
+ method public final void setLabel(CharSequence?);
+ property public final int id;
+ property public final CharSequence? label;
+ property protected final androidx.navigation.Navigator<? extends D> navigator;
+ property public final String? route;
+ }
+
+ @kotlin.DslMarker public @interface NavDestinationDsl {
+ }
+
+ public interface NavDirections {
+ method @IdRes public int getActionId();
+ method public android.os.Bundle getArguments();
+ property @IdRes public abstract int actionId;
+ property public abstract android.os.Bundle arguments;
+ }
+
+ public class NavGraph extends androidx.navigation.NavDestination implements java.lang.Iterable<androidx.navigation.NavDestination> kotlin.jvm.internal.markers.KMappedMarker {
+ ctor public NavGraph(androidx.navigation.Navigator<? extends androidx.navigation.NavGraph> navGraphNavigator);
+ method public final void addAll(androidx.navigation.NavGraph other);
+ method public final void addDestination(androidx.navigation.NavDestination node);
+ method public final void addDestinations(java.util.Collection<? extends androidx.navigation.NavDestination> nodes);
+ method public final void addDestinations(androidx.navigation.NavDestination... nodes);
+ method public final void clear();
+ method public final androidx.navigation.NavDestination? findNode(@IdRes int resId);
+ method public final androidx.navigation.NavDestination? findNode(String? route);
+ method public static final androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+ method @Deprecated @IdRes public final int getStartDestination();
+ method @IdRes public final int getStartDestinationId();
+ method public final String? getStartDestinationRoute();
+ method public final java.util.Iterator<androidx.navigation.NavDestination> iterator();
+ method public final void remove(androidx.navigation.NavDestination node);
+ method public final void setStartDestination(int startDestId);
+ method public final void setStartDestination(String startDestRoute);
+ property @IdRes public final int startDestinationId;
+ property public final String? startDestinationRoute;
+ field public static final androidx.navigation.NavGraph.Companion Companion;
+ }
+
+ public static final class NavGraph.Companion {
+ method public androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+ }
+
+ @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
+ ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+ ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
+ method public final void addDestination(androidx.navigation.NavDestination destination);
+ method public androidx.navigation.NavGraph build();
+ method public final <D extends androidx.navigation.NavDestination> void destination(androidx.navigation.NavDestinationBuilder<? extends D> navDestination);
+ method public final androidx.navigation.NavigatorProvider getProvider();
+ method public final operator void unaryPlus(androidx.navigation.NavDestination);
+ property public final androidx.navigation.NavigatorProvider provider;
+ }
+
+ public final class NavGraphBuilderKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavGraphKt {
+ method public static operator boolean contains(androidx.navigation.NavGraph, @IdRes int id);
+ method public static operator boolean contains(androidx.navigation.NavGraph, String route);
+ method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);
+ method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, String route);
+ method public static inline operator void minusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+ method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+ method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavGraph other);
+ }
+
+ @androidx.navigation.Navigator.Name("navigation") public class NavGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.NavGraph> {
+ ctor public NavGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public androidx.navigation.NavGraph createDestination();
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+ }
+
+ public final class NavOptions {
+ method @AnimRes @AnimatorRes public int getEnterAnim();
+ method @AnimRes @AnimatorRes public int getExitAnim();
+ method @AnimRes @AnimatorRes public int getPopEnterAnim();
+ method @AnimRes @AnimatorRes public int getPopExitAnim();
+ method @Deprecated @IdRes public int getPopUpTo();
+ method @IdRes public int getPopUpToId();
+ method public String? getPopUpToRoute();
+ method public boolean isPopUpToInclusive();
+ method public boolean shouldLaunchSingleTop();
+ method public boolean shouldPopUpToSaveState();
+ method public boolean shouldRestoreState();
+ property @AnimRes @AnimatorRes public final int enterAnim;
+ property @AnimRes @AnimatorRes public final int exitAnim;
+ property @AnimRes @AnimatorRes public final int popEnterAnim;
+ property @AnimRes @AnimatorRes public final int popExitAnim;
+ property @IdRes public final int popUpToId;
+ property public final String? popUpToRoute;
+ }
+
+ public static final class NavOptions.Builder {
+ ctor public NavOptions.Builder();
+ method public androidx.navigation.NavOptions build();
+ method public androidx.navigation.NavOptions.Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim);
+ method public androidx.navigation.NavOptions.Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim);
+ method public androidx.navigation.NavOptions.Builder setLaunchSingleTop(boolean singleTop);
+ method public androidx.navigation.NavOptions.Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim);
+ method public androidx.navigation.NavOptions.Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive, optional boolean saveState);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive, optional boolean saveState);
+ method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive);
+ method public androidx.navigation.NavOptions.Builder setRestoreState(boolean restoreState);
+ }
+
+ @androidx.navigation.NavOptionsDsl public final class NavOptionsBuilder {
+ ctor public NavOptionsBuilder();
+ method public void anim(kotlin.jvm.functions.Function1<? super androidx.navigation.AnimBuilder,kotlin.Unit> animBuilder);
+ method public boolean getLaunchSingleTop();
+ method @Deprecated public int getPopUpTo();
+ method public int getPopUpToId();
+ method public String? getPopUpToRoute();
+ method public boolean getRestoreState();
+ method public void popUpTo(@IdRes int id, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+ method public void popUpTo(String route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+ method public void setLaunchSingleTop(boolean);
+ method @Deprecated public void setPopUpTo(int);
+ method public void setRestoreState(boolean);
+ property public final boolean launchSingleTop;
+ property @Deprecated public final int popUpTo;
+ property public final int popUpToId;
+ property public final String? popUpToRoute;
+ property public final boolean restoreState;
+ }
+
+ public final class NavOptionsBuilderKt {
+ method public static androidx.navigation.NavOptions navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+ }
+
+ @kotlin.DslMarker public @interface NavOptionsDsl {
+ }
+
+ public abstract class NavType<T> {
+ ctor public NavType(boolean isNullableAllowed);
+ method public static androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+ method public abstract operator T? get(android.os.Bundle bundle, String key);
+ method public String getName();
+ method public boolean isNullableAllowed();
+ method public abstract T parseValue(String value);
+ method public T parseValue(String value, T previousValue);
+ method public abstract void put(android.os.Bundle bundle, String key, T value);
+ method public String serializeAsValue(T value);
+ property public boolean isNullableAllowed;
+ property public String name;
+ field public static final androidx.navigation.NavType<boolean[]> BoolArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
+ field public static final androidx.navigation.NavType.Companion Companion;
+ field public static final androidx.navigation.NavType<float[]> FloatArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
+ field public static final androidx.navigation.NavType<int[]> IntArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
+ field public static final androidx.navigation.NavType<long[]> LongArrayType;
+ field public static final androidx.navigation.NavType<java.lang.Long> LongType;
+ field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
+ field public static final androidx.navigation.NavType<java.lang.String[]> StringArrayType;
+ field public static final androidx.navigation.NavType<java.lang.String> StringType;
+ }
+
+ public static final class NavType.Companion {
+ method public androidx.navigation.NavType<?> fromArgType(String? type, String? packageName);
+ }
+
+ public static final class NavType.EnumType<D extends java.lang.Enum<?>> extends androidx.navigation.NavType.SerializableType<D> {
+ ctor public NavType.EnumType(Class<D> type);
+ property public String name;
+ }
+
+ public static final class NavType.ParcelableArrayType<D extends android.os.Parcelable> extends androidx.navigation.NavType<D[]> {
+ ctor public NavType.ParcelableArrayType(Class<D> type);
+ method public D![]? get(android.os.Bundle bundle, String key);
+ method public D![] parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D![]? value);
+ property public String name;
+ }
+
+ public static final class NavType.ParcelableType<D> extends androidx.navigation.NavType<D> {
+ ctor public NavType.ParcelableType(Class<D> type);
+ method public D? get(android.os.Bundle bundle, String key);
+ method public D parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D value);
+ property public String name;
+ }
+
+ public static final class NavType.SerializableArrayType<D extends java.io.Serializable> extends androidx.navigation.NavType<D[]> {
+ ctor public NavType.SerializableArrayType(Class<D> type);
+ method public D![]? get(android.os.Bundle bundle, String key);
+ method public D![] parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D![]? value);
+ property public String name;
+ }
+
+ public static class NavType.SerializableType<D extends java.io.Serializable> extends androidx.navigation.NavType<D> {
+ ctor public NavType.SerializableType(Class<D> type);
+ method public D? get(android.os.Bundle bundle, String key);
+ method public D parseValue(String value);
+ method public void put(android.os.Bundle bundle, String key, D value);
+ property public String name;
+ }
+
+ public abstract class Navigator<D extends androidx.navigation.NavDestination> {
+ ctor public Navigator();
+ method public abstract D createDestination();
+ method protected final androidx.navigation.NavigatorState getState();
+ method public final boolean isAttached();
+ method public void navigate(java.util.List<androidx.navigation.NavBackStackEntry> entries, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method public androidx.navigation.NavDestination? navigate(D destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @CallSuper public void onAttach(androidx.navigation.NavigatorState state);
+ method public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+ method public void onRestoreState(android.os.Bundle savedState);
+ method public android.os.Bundle? onSaveState();
+ method public void popBackStack(androidx.navigation.NavBackStackEntry popUpTo, boolean savedState);
+ method public boolean popBackStack();
+ property public final boolean isAttached;
+ property protected final androidx.navigation.NavigatorState state;
+ }
+
+ public static interface Navigator.Extras {
+ }
+
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface Navigator.Name {
+ method public abstract String value();
+ property public abstract String value;
+ }
+
+ public class NavigatorProvider {
+ ctor public NavigatorProvider();
+ method public final androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method @CallSuper public androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method public final <T extends androidx.navigation.Navigator<?>> T getNavigator(Class<T> navigatorClass);
+ method @CallSuper public <T extends androidx.navigation.Navigator<?>> T getNavigator(String name);
+ }
+
+ public final class NavigatorProviderKt {
+ method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, String name);
+ method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);
+ method public static inline operator void plusAssign(androidx.navigation.NavigatorProvider, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ method public static inline operator androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? set(androidx.navigation.NavigatorProvider, String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+ }
+
+ public abstract class NavigatorState {
+ ctor public NavigatorState();
+ method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+ method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+ method @CallSuper public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+ method @CallSuper public void onLaunchSingleTopWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+ method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+ method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+ method @CallSuper public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+ method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
+ method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
+ }
+
+ @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
+ ctor public PopUpToBuilder();
+ method public boolean getInclusive();
+ method public boolean getSaveState();
+ method public void setInclusive(boolean);
+ method public void setSaveState(boolean);
+ property public final boolean inclusive;
+ property public final boolean saveState;
+ }
+
+}
+
diff --git a/navigation/navigation-common/api/restricted_current.ignore b/navigation/navigation-common/api/restricted_current.ignore
deleted file mode 100644
index 8eebd5d..0000000
--- a/navigation/navigation-common/api/restricted_current.ignore
+++ /dev/null
@@ -1,9 +0,0 @@
-// Baseline format: 1.0
-InvalidNullConversion: androidx.navigation.NavType#put(android.os.Bundle, String, T) parameter #2:
- Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.navigation.NavType.put(android.os.Bundle bundle, String key, T value)
-InvalidNullConversion: androidx.navigation.NavType.ParcelableType#put(android.os.Bundle, String, D) parameter #2:
- Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter value in androidx.navigation.NavType.ParcelableType.put(android.os.Bundle bundle, String key, D value)
-
-
-RemovedClass: androidx.navigation.NavArgsLazyKt:
- Removed class androidx.navigation.NavArgsLazyKt
diff --git a/navigation/navigation-compose/api/2.6.0-beta02.txt b/navigation/navigation-compose/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..73c5c5d
--- /dev/null
+++ b/navigation/navigation-compose/api/2.6.0-beta02.txt
@@ -0,0 +1,50 @@
+// Signature format: 4.0
+package androidx.navigation.compose {
+
+ @androidx.navigation.Navigator.Name("composable") public final class ComposeNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.ComposeNavigator.Destination> {
+ ctor public ComposeNavigator();
+ method public androidx.navigation.compose.ComposeNavigator.Destination createDestination();
+ method public kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+ method public void onTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class ComposeNavigator.Destination extends androidx.navigation.NavDestination {
+ ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ }
+
+ public final class DialogHostKt {
+ method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator);
+ }
+
+ @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.DialogNavigator.Destination> {
+ ctor public DialogNavigator();
+ method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class DialogNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+ ctor public DialogNavigator.Destination(androidx.navigation.compose.DialogNavigator navigator, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ }
+
+ public final class NavBackStackEntryProviderKt {
+ method @androidx.compose.runtime.Composable public static void LocalOwnersProvider(androidx.navigation.NavBackStackEntry, androidx.compose.runtime.saveable.SaveableStateHolder saveableStateHolder, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class NavGraphBuilderKt {
+ method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ method public static void dialog(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavHostControllerKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.navigation.NavBackStackEntry> currentBackStackEntryAsState(androidx.navigation.NavController);
+ method @androidx.compose.runtime.Composable public static androidx.navigation.NavHostController rememberNavController(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>... navigators);
+ }
+
+ public final class NavHostKt {
+ method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier);
+ }
+
+}
+
diff --git a/navigation/navigation-compose/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-compose/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..73c5c5d
--- /dev/null
+++ b/navigation/navigation-compose/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,50 @@
+// Signature format: 4.0
+package androidx.navigation.compose {
+
+ @androidx.navigation.Navigator.Name("composable") public final class ComposeNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.ComposeNavigator.Destination> {
+ ctor public ComposeNavigator();
+ method public androidx.navigation.compose.ComposeNavigator.Destination createDestination();
+ method public kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+ method public void onTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class ComposeNavigator.Destination extends androidx.navigation.NavDestination {
+ ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ }
+
+ public final class DialogHostKt {
+ method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator);
+ }
+
+ @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.DialogNavigator.Destination> {
+ ctor public DialogNavigator();
+ method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class DialogNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+ ctor public DialogNavigator.Destination(androidx.navigation.compose.DialogNavigator navigator, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ }
+
+ public final class NavBackStackEntryProviderKt {
+ method @androidx.compose.runtime.Composable public static void LocalOwnersProvider(androidx.navigation.NavBackStackEntry, androidx.compose.runtime.saveable.SaveableStateHolder saveableStateHolder, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class NavGraphBuilderKt {
+ method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ method public static void dialog(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavHostControllerKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.navigation.NavBackStackEntry> currentBackStackEntryAsState(androidx.navigation.NavController);
+ method @androidx.compose.runtime.Composable public static androidx.navigation.NavHostController rememberNavController(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>... navigators);
+ }
+
+ public final class NavHostKt {
+ method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier);
+ }
+
+}
+
diff --git a/navigation/navigation-compose/api/res-2.6.0-beta02.txt b/navigation/navigation-compose/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-compose/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-compose/api/restricted_2.6.0-beta02.txt b/navigation/navigation-compose/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..73c5c5d
--- /dev/null
+++ b/navigation/navigation-compose/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,50 @@
+// Signature format: 4.0
+package androidx.navigation.compose {
+
+ @androidx.navigation.Navigator.Name("composable") public final class ComposeNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.ComposeNavigator.Destination> {
+ ctor public ComposeNavigator();
+ method public androidx.navigation.compose.ComposeNavigator.Destination createDestination();
+ method public kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+ method public void onTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class ComposeNavigator.Destination extends androidx.navigation.NavDestination {
+ ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ }
+
+ public final class DialogHostKt {
+ method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator);
+ }
+
+ @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.DialogNavigator.Destination> {
+ ctor public DialogNavigator();
+ method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class DialogNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+ ctor public DialogNavigator.Destination(androidx.navigation.compose.DialogNavigator navigator, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ }
+
+ public final class NavBackStackEntryProviderKt {
+ method @androidx.compose.runtime.Composable public static void LocalOwnersProvider(androidx.navigation.NavBackStackEntry, androidx.compose.runtime.saveable.SaveableStateHolder saveableStateHolder, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ public final class NavGraphBuilderKt {
+ method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ method public static void dialog(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavHostControllerKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.navigation.NavBackStackEntry> currentBackStackEntryAsState(androidx.navigation.NavController);
+ method @androidx.compose.runtime.Composable public static androidx.navigation.NavHostController rememberNavController(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>... navigators);
+ }
+
+ public final class NavHostKt {
+ method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier);
+ }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-fragment/api/2.6.0-beta02.txt b/navigation/navigation-dynamic-features-fragment/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..fe32d9b
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/2.6.0-beta02.txt
@@ -0,0 +1,69 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures.fragment {
+
+ @androidx.navigation.Navigator.Name("fragment") public final class DynamicFragmentNavigator extends androidx.navigation.fragment.FragmentNavigator {
+ ctor public DynamicFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager manager, int containerId, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination createDestination();
+ }
+
+ public static final class DynamicFragmentNavigator.Destination extends androidx.navigation.fragment.FragmentNavigator.Destination {
+ ctor public DynamicFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ ctor public DynamicFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+ method public String? getModuleName();
+ method public void setModuleName(String?);
+ property public final String? moduleName;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+ ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+ ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
+ method public String? getModuleName();
+ method public void setModuleName(String?);
+ property public final String? moduleName;
+ }
+
+ public final class DynamicFragmentNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ }
+
+ public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+ ctor public DynamicNavHostFragment();
+ method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+ method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+ field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+ }
+
+ public static final class DynamicNavHostFragment.Companion {
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+ }
+
+}
+
+package androidx.navigation.dynamicfeatures.fragment.ui {
+
+ public abstract class AbstractProgressFragment extends androidx.fragment.app.Fragment {
+ ctor public AbstractProgressFragment();
+ ctor public AbstractProgressFragment(int contentLayoutId);
+ method protected abstract void onCancelled();
+ method protected abstract void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+ method protected void onInstalled();
+ method protected abstract void onProgress(@com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus int status, long bytesDownloaded, long bytesTotal);
+ }
+
+ public final class DefaultProgressFragment extends androidx.navigation.dynamicfeatures.fragment.ui.AbstractProgressFragment {
+ ctor public DefaultProgressFragment();
+ method protected void onCancelled();
+ method protected void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+ method protected void onProgress(int status, long bytesDownloaded, long bytesTotal);
+ }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..fe32d9b
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,69 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures.fragment {
+
+ @androidx.navigation.Navigator.Name("fragment") public final class DynamicFragmentNavigator extends androidx.navigation.fragment.FragmentNavigator {
+ ctor public DynamicFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager manager, int containerId, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination createDestination();
+ }
+
+ public static final class DynamicFragmentNavigator.Destination extends androidx.navigation.fragment.FragmentNavigator.Destination {
+ ctor public DynamicFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ ctor public DynamicFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+ method public String? getModuleName();
+ method public void setModuleName(String?);
+ property public final String? moduleName;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+ ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+ ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
+ method public String? getModuleName();
+ method public void setModuleName(String?);
+ property public final String? moduleName;
+ }
+
+ public final class DynamicFragmentNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ }
+
+ public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+ ctor public DynamicNavHostFragment();
+ method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+ method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+ field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+ }
+
+ public static final class DynamicNavHostFragment.Companion {
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+ }
+
+}
+
+package androidx.navigation.dynamicfeatures.fragment.ui {
+
+ public abstract class AbstractProgressFragment extends androidx.fragment.app.Fragment {
+ ctor public AbstractProgressFragment();
+ ctor public AbstractProgressFragment(int contentLayoutId);
+ method protected abstract void onCancelled();
+ method protected abstract void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+ method protected void onInstalled();
+ method protected abstract void onProgress(@com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus int status, long bytesDownloaded, long bytesTotal);
+ }
+
+ public final class DefaultProgressFragment extends androidx.navigation.dynamicfeatures.fragment.ui.AbstractProgressFragment {
+ ctor public DefaultProgressFragment();
+ method protected void onCancelled();
+ method protected void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+ method protected void onProgress(int status, long bytesDownloaded, long bytesTotal);
+ }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-fragment/api/res-2.6.0-beta02.txt b/navigation/navigation-dynamic-features-fragment/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-dynamic-features-fragment/api/restricted_2.6.0-beta02.txt b/navigation/navigation-dynamic-features-fragment/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..fe32d9b
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,69 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures.fragment {
+
+ @androidx.navigation.Navigator.Name("fragment") public final class DynamicFragmentNavigator extends androidx.navigation.fragment.FragmentNavigator {
+ ctor public DynamicFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager manager, int containerId, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination createDestination();
+ }
+
+ public static final class DynamicFragmentNavigator.Destination extends androidx.navigation.fragment.FragmentNavigator.Destination {
+ ctor public DynamicFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ ctor public DynamicFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+ method public String? getModuleName();
+ method public void setModuleName(String?);
+ property public final String? moduleName;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+ ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+ ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
+ method public String? getModuleName();
+ method public void setModuleName(String?);
+ property public final String? moduleName;
+ }
+
+ public final class DynamicFragmentNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ }
+
+ public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+ ctor public DynamicNavHostFragment();
+ method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+ method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+ field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+ }
+
+ public static final class DynamicNavHostFragment.Companion {
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+ }
+
+}
+
+package androidx.navigation.dynamicfeatures.fragment.ui {
+
+ public abstract class AbstractProgressFragment extends androidx.fragment.app.Fragment {
+ ctor public AbstractProgressFragment();
+ ctor public AbstractProgressFragment(int contentLayoutId);
+ method protected abstract void onCancelled();
+ method protected abstract void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+ method protected void onInstalled();
+ method protected abstract void onProgress(@com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus int status, long bytesDownloaded, long bytesTotal);
+ }
+
+ public final class DefaultProgressFragment extends androidx.navigation.dynamicfeatures.fragment.ui.AbstractProgressFragment {
+ ctor public DefaultProgressFragment();
+ method protected void onCancelled();
+ method protected void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+ method protected void onProgress(int status, long bytesDownloaded, long bytesTotal);
+ }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-runtime/api/2.6.0-beta02.txt b/navigation/navigation-dynamic-features-runtime/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e4c37db
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/2.6.0-beta02.txt
@@ -0,0 +1,154 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures {
+
+ @androidx.navigation.Navigator.Name("activity") public final class DynamicActivityNavigator extends androidx.navigation.ActivityNavigator {
+ ctor public DynamicActivityNavigator(android.content.Context context, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination createDestination();
+ }
+
+ public static final class DynamicActivityNavigator.Destination extends androidx.navigation.ActivityNavigator.Destination {
+ ctor public DynamicActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ ctor public DynamicActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+ method public String? getModuleName();
+ method public void setModuleName(String?);
+ property public final String? moduleName;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+ ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+ ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
+ method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
+ method public String? getAction();
+ method public String? getActivityClassName();
+ method public android.net.Uri? getData();
+ method public String? getDataPattern();
+ method public String? getModuleName();
+ method public String? getTargetPackage();
+ method public void setAction(String?);
+ method public void setActivityClassName(String?);
+ method public void setData(android.net.Uri?);
+ method public void setDataPattern(String?);
+ method public void setModuleName(String?);
+ method public void setTargetPackage(String?);
+ property public final String? action;
+ property public final String? activityClassName;
+ property public final android.net.Uri? data;
+ property public final String? dataPattern;
+ property public final String? moduleName;
+ property public final String? targetPackage;
+ }
+
+ public final class DynamicActivityNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ }
+
+ public final class DynamicExtras implements androidx.navigation.Navigator.Extras {
+ ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor, optional androidx.navigation.Navigator.Extras? destinationExtras);
+ ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor);
+ ctor public DynamicExtras();
+ method public androidx.navigation.Navigator.Extras? getDestinationExtras();
+ method public androidx.navigation.dynamicfeatures.DynamicInstallMonitor? getInstallMonitor();
+ property public final androidx.navigation.Navigator.Extras? destinationExtras;
+ property public final androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor;
+ }
+
+ @androidx.navigation.Navigator.Name("navigation") public final class DynamicGraphNavigator extends androidx.navigation.NavGraphNavigator {
+ ctor public DynamicGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.DynamicGraphNavigator.DynamicNavGraph createDestination();
+ method public void installDefaultProgressDestination(kotlin.jvm.functions.Function0<? extends androidx.navigation.NavDestination> progressDestinationSupplier);
+ }
+
+ public static final class DynamicGraphNavigator.DynamicNavGraph extends androidx.navigation.NavGraph {
+ ctor public DynamicGraphNavigator.DynamicNavGraph(androidx.navigation.dynamicfeatures.DynamicGraphNavigator navGraphNavigator, androidx.navigation.NavigatorProvider navigatorProvider);
+ method public String? getModuleName();
+ method public int getProgressDestination();
+ method public void setModuleName(String?);
+ method public void setProgressDestination(int);
+ property public final String? moduleName;
+ property public final int progressDestination;
+ }
+
+ @androidx.navigation.Navigator.Name("include-dynamic") public final class DynamicIncludeGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+ ctor public DynamicIncludeGraphNavigator(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.NavInflater navInflater, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph createDestination();
+ }
+
+ public static final class DynamicIncludeGraphNavigator.DynamicIncludeNavGraph extends androidx.navigation.NavDestination {
+ method public String? getGraphPackage();
+ method public String? getGraphResourceName();
+ method public String? getModuleName();
+ method public void setGraphPackage(String?);
+ method public void setGraphResourceName(String?);
+ method public void setModuleName(String?);
+ property public final String? graphPackage;
+ property public final String? graphResourceName;
+ property public final String? moduleName;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+ ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+ ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
+ method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
+ method public String? getGraphPackage();
+ method public void setGraphPackage(String?);
+ property public final String? graphPackage;
+ }
+
+ public final class DynamicIncludeNavGraphBuilderKt {
+ method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+ method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
+ method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public class DynamicInstallManager {
+ ctor public DynamicInstallManager(android.content.Context context, com.google.android.play.core.splitinstall.SplitInstallManager splitInstallManager);
+ }
+
+ public final class DynamicInstallMonitor {
+ ctor public DynamicInstallMonitor();
+ method public void cancelInstall();
+ method public Exception? getException();
+ method public int getSessionId();
+ method public androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> getStatus();
+ method public boolean isInstallRequired();
+ property public final Exception? exception;
+ property public final boolean isInstallRequired;
+ property public final int sessionId;
+ property public final androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> status;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
+ ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+ ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
+ method public String? getModuleName();
+ method public int getProgressDestination();
+ method public String? getProgressDestinationRoute();
+ method public void setModuleName(String?);
+ method public void setProgressDestination(int);
+ method public void setProgressDestinationRoute(String?);
+ property public final String? moduleName;
+ property public final int progressDestination;
+ property public final String? progressDestinationRoute;
+ }
+
+ public final class DynamicNavGraphBuilderKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavControllerKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavHostKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e4c37db
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,154 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures {
+
+ @androidx.navigation.Navigator.Name("activity") public final class DynamicActivityNavigator extends androidx.navigation.ActivityNavigator {
+ ctor public DynamicActivityNavigator(android.content.Context context, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination createDestination();
+ }
+
+ public static final class DynamicActivityNavigator.Destination extends androidx.navigation.ActivityNavigator.Destination {
+ ctor public DynamicActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ ctor public DynamicActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+ method public String? getModuleName();
+ method public void setModuleName(String?);
+ property public final String? moduleName;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+ ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+ ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
+ method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
+ method public String? getAction();
+ method public String? getActivityClassName();
+ method public android.net.Uri? getData();
+ method public String? getDataPattern();
+ method public String? getModuleName();
+ method public String? getTargetPackage();
+ method public void setAction(String?);
+ method public void setActivityClassName(String?);
+ method public void setData(android.net.Uri?);
+ method public void setDataPattern(String?);
+ method public void setModuleName(String?);
+ method public void setTargetPackage(String?);
+ property public final String? action;
+ property public final String? activityClassName;
+ property public final android.net.Uri? data;
+ property public final String? dataPattern;
+ property public final String? moduleName;
+ property public final String? targetPackage;
+ }
+
+ public final class DynamicActivityNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ }
+
+ public final class DynamicExtras implements androidx.navigation.Navigator.Extras {
+ ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor, optional androidx.navigation.Navigator.Extras? destinationExtras);
+ ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor);
+ ctor public DynamicExtras();
+ method public androidx.navigation.Navigator.Extras? getDestinationExtras();
+ method public androidx.navigation.dynamicfeatures.DynamicInstallMonitor? getInstallMonitor();
+ property public final androidx.navigation.Navigator.Extras? destinationExtras;
+ property public final androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor;
+ }
+
+ @androidx.navigation.Navigator.Name("navigation") public final class DynamicGraphNavigator extends androidx.navigation.NavGraphNavigator {
+ ctor public DynamicGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.DynamicGraphNavigator.DynamicNavGraph createDestination();
+ method public void installDefaultProgressDestination(kotlin.jvm.functions.Function0<? extends androidx.navigation.NavDestination> progressDestinationSupplier);
+ }
+
+ public static final class DynamicGraphNavigator.DynamicNavGraph extends androidx.navigation.NavGraph {
+ ctor public DynamicGraphNavigator.DynamicNavGraph(androidx.navigation.dynamicfeatures.DynamicGraphNavigator navGraphNavigator, androidx.navigation.NavigatorProvider navigatorProvider);
+ method public String? getModuleName();
+ method public int getProgressDestination();
+ method public void setModuleName(String?);
+ method public void setProgressDestination(int);
+ property public final String? moduleName;
+ property public final int progressDestination;
+ }
+
+ @androidx.navigation.Navigator.Name("include-dynamic") public final class DynamicIncludeGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+ ctor public DynamicIncludeGraphNavigator(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.NavInflater navInflater, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph createDestination();
+ }
+
+ public static final class DynamicIncludeGraphNavigator.DynamicIncludeNavGraph extends androidx.navigation.NavDestination {
+ method public String? getGraphPackage();
+ method public String? getGraphResourceName();
+ method public String? getModuleName();
+ method public void setGraphPackage(String?);
+ method public void setGraphResourceName(String?);
+ method public void setModuleName(String?);
+ property public final String? graphPackage;
+ property public final String? graphResourceName;
+ property public final String? moduleName;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+ ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+ ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
+ method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
+ method public String? getGraphPackage();
+ method public void setGraphPackage(String?);
+ property public final String? graphPackage;
+ }
+
+ public final class DynamicIncludeNavGraphBuilderKt {
+ method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+ method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
+ method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public class DynamicInstallManager {
+ ctor public DynamicInstallManager(android.content.Context context, com.google.android.play.core.splitinstall.SplitInstallManager splitInstallManager);
+ }
+
+ public final class DynamicInstallMonitor {
+ ctor public DynamicInstallMonitor();
+ method public void cancelInstall();
+ method public Exception? getException();
+ method public int getSessionId();
+ method public androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> getStatus();
+ method public boolean isInstallRequired();
+ property public final Exception? exception;
+ property public final boolean isInstallRequired;
+ property public final int sessionId;
+ property public final androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> status;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
+ ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+ ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
+ method public String? getModuleName();
+ method public int getProgressDestination();
+ method public String? getProgressDestinationRoute();
+ method public void setModuleName(String?);
+ method public void setProgressDestination(int);
+ method public void setProgressDestinationRoute(String?);
+ property public final String? moduleName;
+ property public final int progressDestination;
+ property public final String? progressDestinationRoute;
+ }
+
+ public final class DynamicNavGraphBuilderKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavControllerKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavHostKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-runtime/api/res-2.6.0-beta02.txt b/navigation/navigation-dynamic-features-runtime/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-dynamic-features-runtime/api/restricted_2.6.0-beta02.txt b/navigation/navigation-dynamic-features-runtime/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e4c37db
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,154 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures {
+
+ @androidx.navigation.Navigator.Name("activity") public final class DynamicActivityNavigator extends androidx.navigation.ActivityNavigator {
+ ctor public DynamicActivityNavigator(android.content.Context context, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination createDestination();
+ }
+
+ public static final class DynamicActivityNavigator.Destination extends androidx.navigation.ActivityNavigator.Destination {
+ ctor public DynamicActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ ctor public DynamicActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+ method public String? getModuleName();
+ method public void setModuleName(String?);
+ property public final String? moduleName;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+ ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+ ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
+ method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
+ method public String? getAction();
+ method public String? getActivityClassName();
+ method public android.net.Uri? getData();
+ method public String? getDataPattern();
+ method public String? getModuleName();
+ method public String? getTargetPackage();
+ method public void setAction(String?);
+ method public void setActivityClassName(String?);
+ method public void setData(android.net.Uri?);
+ method public void setDataPattern(String?);
+ method public void setModuleName(String?);
+ method public void setTargetPackage(String?);
+ property public final String? action;
+ property public final String? activityClassName;
+ property public final android.net.Uri? data;
+ property public final String? dataPattern;
+ property public final String? moduleName;
+ property public final String? targetPackage;
+ }
+
+ public final class DynamicActivityNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ }
+
+ public final class DynamicExtras implements androidx.navigation.Navigator.Extras {
+ ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor, optional androidx.navigation.Navigator.Extras? destinationExtras);
+ ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor);
+ ctor public DynamicExtras();
+ method public androidx.navigation.Navigator.Extras? getDestinationExtras();
+ method public androidx.navigation.dynamicfeatures.DynamicInstallMonitor? getInstallMonitor();
+ property public final androidx.navigation.Navigator.Extras? destinationExtras;
+ property public final androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor;
+ }
+
+ @androidx.navigation.Navigator.Name("navigation") public final class DynamicGraphNavigator extends androidx.navigation.NavGraphNavigator {
+ ctor public DynamicGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.DynamicGraphNavigator.DynamicNavGraph createDestination();
+ method public void installDefaultProgressDestination(kotlin.jvm.functions.Function0<? extends androidx.navigation.NavDestination> progressDestinationSupplier);
+ }
+
+ public static final class DynamicGraphNavigator.DynamicNavGraph extends androidx.navigation.NavGraph {
+ ctor public DynamicGraphNavigator.DynamicNavGraph(androidx.navigation.dynamicfeatures.DynamicGraphNavigator navGraphNavigator, androidx.navigation.NavigatorProvider navigatorProvider);
+ method public String? getModuleName();
+ method public int getProgressDestination();
+ method public void setModuleName(String?);
+ method public void setProgressDestination(int);
+ property public final String? moduleName;
+ property public final int progressDestination;
+ }
+
+ @androidx.navigation.Navigator.Name("include-dynamic") public final class DynamicIncludeGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+ ctor public DynamicIncludeGraphNavigator(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.NavInflater navInflater, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+ method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph createDestination();
+ }
+
+ public static final class DynamicIncludeGraphNavigator.DynamicIncludeNavGraph extends androidx.navigation.NavDestination {
+ method public String? getGraphPackage();
+ method public String? getGraphResourceName();
+ method public String? getModuleName();
+ method public void setGraphPackage(String?);
+ method public void setGraphResourceName(String?);
+ method public void setModuleName(String?);
+ property public final String? graphPackage;
+ property public final String? graphResourceName;
+ property public final String? moduleName;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+ ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+ ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
+ method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
+ method public String? getGraphPackage();
+ method public void setGraphPackage(String?);
+ property public final String? graphPackage;
+ }
+
+ public final class DynamicIncludeNavGraphBuilderKt {
+ method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+ method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
+ method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public class DynamicInstallManager {
+ ctor public DynamicInstallManager(android.content.Context context, com.google.android.play.core.splitinstall.SplitInstallManager splitInstallManager);
+ }
+
+ public final class DynamicInstallMonitor {
+ ctor public DynamicInstallMonitor();
+ method public void cancelInstall();
+ method public Exception? getException();
+ method public int getSessionId();
+ method public androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> getStatus();
+ method public boolean isInstallRequired();
+ property public final Exception? exception;
+ property public final boolean isInstallRequired;
+ property public final int sessionId;
+ property public final androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> status;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
+ ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+ ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
+ method public String? getModuleName();
+ method public int getProgressDestination();
+ method public String? getProgressDestinationRoute();
+ method public void setModuleName(String?);
+ method public void setProgressDestination(int);
+ method public void setProgressDestinationRoute(String?);
+ property public final String? moduleName;
+ property public final int progressDestination;
+ property public final String? progressDestinationRoute;
+ }
+
+ public final class DynamicNavGraphBuilderKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavControllerKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavHostKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+ }
+
+}
+
diff --git a/navigation/navigation-fragment-ktx/api/2.6.0-beta02.txt b/navigation/navigation-fragment-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-fragment-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-fragment-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-fragment-ktx/api/res-2.6.0-beta02.txt b/navigation/navigation-fragment-ktx/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-fragment-ktx/api/restricted_2.6.0-beta02.txt b/navigation/navigation-fragment-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-fragment/api/2.6.0-beta02.txt b/navigation/navigation-fragment/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..97bdfe6
--- /dev/null
+++ b/navigation/navigation-fragment/api/2.6.0-beta02.txt
@@ -0,0 +1,125 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+ public final class NavGraphViewModelLazyKt {
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ }
+
+}
+
+package androidx.navigation.fragment {
+
+ public abstract class AbstractListDetailFragment extends androidx.fragment.app.Fragment {
+ ctor public AbstractListDetailFragment();
+ method public final androidx.navigation.fragment.NavHostFragment getDetailPaneNavHostFragment();
+ method public final androidx.slidingpanelayout.widget.SlidingPaneLayout getSlidingPaneLayout();
+ method public androidx.navigation.fragment.NavHostFragment onCreateDetailPaneNavHostFragment();
+ method public abstract android.view.View onCreateListPaneView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+ method @CallSuper public final android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+ method public void onListPaneViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+ method @CallSuper public final void onViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+ property public final androidx.navigation.fragment.NavHostFragment detailPaneNavHostFragment;
+ property public final androidx.slidingpanelayout.widget.SlidingPaneLayout slidingPaneLayout;
+ }
+
+ @androidx.navigation.Navigator.Name("dialog") public final class DialogFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+ ctor public DialogFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager);
+ method public androidx.navigation.fragment.DialogFragmentNavigator.Destination createDestination();
+ }
+
+ @androidx.navigation.NavDestination.ClassType(DialogFragment::class) public static class DialogFragmentNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+ ctor public DialogFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.DialogFragmentNavigator.Destination> fragmentNavigator);
+ ctor public DialogFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public final String getClassName();
+ method public final androidx.navigation.fragment.DialogFragmentNavigator.Destination setClassName(String className);
+ property public final String className;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+ ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+ ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+ method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
+ }
+
+ public final class DialogFragmentNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+ method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ }
+
+ public final class FragmentKt {
+ method public static androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment);
+ }
+
+ public final class FragmentNavArgsLazyKt {
+ method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(androidx.fragment.app.Fragment);
+ }
+
+ @androidx.navigation.Navigator.Name("fragment") public class FragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+ ctor public FragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, int containerId);
+ method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+ method @Deprecated public androidx.fragment.app.Fragment instantiateFragment(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, String className, android.os.Bundle? args);
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Fragment::class) public static class FragmentNavigator.Destination extends androidx.navigation.NavDestination {
+ ctor public FragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+ ctor public FragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public final String getClassName();
+ method public final androidx.navigation.fragment.FragmentNavigator.Destination setClassName(String className);
+ property public final String className;
+ }
+
+ public static final class FragmentNavigator.Extras implements androidx.navigation.Navigator.Extras {
+ method public java.util.Map<android.view.View,java.lang.String> getSharedElements();
+ property public final java.util.Map<android.view.View,java.lang.String> sharedElements;
+ }
+
+ public static final class FragmentNavigator.Extras.Builder {
+ ctor public FragmentNavigator.Extras.Builder();
+ method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElement(android.view.View sharedElement, String name);
+ method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElements(java.util.Map<android.view.View,java.lang.String> sharedElements);
+ method public androidx.navigation.fragment.FragmentNavigator.Extras build();
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+ ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+ ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+ method public androidx.navigation.fragment.FragmentNavigator.Destination build();
+ }
+
+ public final class FragmentNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ }
+
+ public final class FragmentNavigatorExtrasKt {
+ method public static androidx.navigation.fragment.FragmentNavigator.Extras FragmentNavigatorExtras(kotlin.Pair<? extends android.view.View,java.lang.String>... sharedElements);
+ }
+
+ public class NavHostFragment extends androidx.fragment.app.Fragment implements androidx.navigation.NavHost {
+ ctor public NavHostFragment();
+ method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+ method @Deprecated protected androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> createFragmentNavigator();
+ method public static final androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+ method public final androidx.navigation.NavController getNavController();
+ method @Deprecated @CallSuper protected void onCreateNavController(androidx.navigation.NavController navController);
+ method @CallSuper protected void onCreateNavHostController(androidx.navigation.NavHostController navHostController);
+ property public final androidx.navigation.NavController navController;
+ field public static final androidx.navigation.fragment.NavHostFragment.Companion Companion;
+ }
+
+ public static final class NavHostFragment.Companion {
+ method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+ method public androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+ }
+
+}
+
diff --git a/navigation/navigation-fragment/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-fragment/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..97bdfe6
--- /dev/null
+++ b/navigation/navigation-fragment/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,125 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+ public final class NavGraphViewModelLazyKt {
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ }
+
+}
+
+package androidx.navigation.fragment {
+
+ public abstract class AbstractListDetailFragment extends androidx.fragment.app.Fragment {
+ ctor public AbstractListDetailFragment();
+ method public final androidx.navigation.fragment.NavHostFragment getDetailPaneNavHostFragment();
+ method public final androidx.slidingpanelayout.widget.SlidingPaneLayout getSlidingPaneLayout();
+ method public androidx.navigation.fragment.NavHostFragment onCreateDetailPaneNavHostFragment();
+ method public abstract android.view.View onCreateListPaneView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+ method @CallSuper public final android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+ method public void onListPaneViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+ method @CallSuper public final void onViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+ property public final androidx.navigation.fragment.NavHostFragment detailPaneNavHostFragment;
+ property public final androidx.slidingpanelayout.widget.SlidingPaneLayout slidingPaneLayout;
+ }
+
+ @androidx.navigation.Navigator.Name("dialog") public final class DialogFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+ ctor public DialogFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager);
+ method public androidx.navigation.fragment.DialogFragmentNavigator.Destination createDestination();
+ }
+
+ @androidx.navigation.NavDestination.ClassType(DialogFragment::class) public static class DialogFragmentNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+ ctor public DialogFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.DialogFragmentNavigator.Destination> fragmentNavigator);
+ ctor public DialogFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public final String getClassName();
+ method public final androidx.navigation.fragment.DialogFragmentNavigator.Destination setClassName(String className);
+ property public final String className;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+ ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+ ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+ method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
+ }
+
+ public final class DialogFragmentNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+ method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ }
+
+ public final class FragmentKt {
+ method public static androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment);
+ }
+
+ public final class FragmentNavArgsLazyKt {
+ method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(androidx.fragment.app.Fragment);
+ }
+
+ @androidx.navigation.Navigator.Name("fragment") public class FragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+ ctor public FragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, int containerId);
+ method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+ method @Deprecated public androidx.fragment.app.Fragment instantiateFragment(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, String className, android.os.Bundle? args);
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Fragment::class) public static class FragmentNavigator.Destination extends androidx.navigation.NavDestination {
+ ctor public FragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+ ctor public FragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public final String getClassName();
+ method public final androidx.navigation.fragment.FragmentNavigator.Destination setClassName(String className);
+ property public final String className;
+ }
+
+ public static final class FragmentNavigator.Extras implements androidx.navigation.Navigator.Extras {
+ method public java.util.Map<android.view.View,java.lang.String> getSharedElements();
+ property public final java.util.Map<android.view.View,java.lang.String> sharedElements;
+ }
+
+ public static final class FragmentNavigator.Extras.Builder {
+ ctor public FragmentNavigator.Extras.Builder();
+ method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElement(android.view.View sharedElement, String name);
+ method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElements(java.util.Map<android.view.View,java.lang.String> sharedElements);
+ method public androidx.navigation.fragment.FragmentNavigator.Extras build();
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+ ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+ ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+ method public androidx.navigation.fragment.FragmentNavigator.Destination build();
+ }
+
+ public final class FragmentNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ }
+
+ public final class FragmentNavigatorExtrasKt {
+ method public static androidx.navigation.fragment.FragmentNavigator.Extras FragmentNavigatorExtras(kotlin.Pair<? extends android.view.View,java.lang.String>... sharedElements);
+ }
+
+ public class NavHostFragment extends androidx.fragment.app.Fragment implements androidx.navigation.NavHost {
+ ctor public NavHostFragment();
+ method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+ method @Deprecated protected androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> createFragmentNavigator();
+ method public static final androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+ method public final androidx.navigation.NavController getNavController();
+ method @Deprecated @CallSuper protected void onCreateNavController(androidx.navigation.NavController navController);
+ method @CallSuper protected void onCreateNavHostController(androidx.navigation.NavHostController navHostController);
+ property public final androidx.navigation.NavController navController;
+ field public static final androidx.navigation.fragment.NavHostFragment.Companion Companion;
+ }
+
+ public static final class NavHostFragment.Companion {
+ method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+ method public androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+ }
+
+}
+
diff --git a/navigation/navigation-fragment/api/res-2.6.0-beta02.txt b/navigation/navigation-fragment/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-fragment/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-fragment/api/restricted_2.6.0-beta02.txt b/navigation/navigation-fragment/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..97bdfe6
--- /dev/null
+++ b/navigation/navigation-fragment/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,125 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+ public final class NavGraphViewModelLazyKt {
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<? extends VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+ }
+
+}
+
+package androidx.navigation.fragment {
+
+ public abstract class AbstractListDetailFragment extends androidx.fragment.app.Fragment {
+ ctor public AbstractListDetailFragment();
+ method public final androidx.navigation.fragment.NavHostFragment getDetailPaneNavHostFragment();
+ method public final androidx.slidingpanelayout.widget.SlidingPaneLayout getSlidingPaneLayout();
+ method public androidx.navigation.fragment.NavHostFragment onCreateDetailPaneNavHostFragment();
+ method public abstract android.view.View onCreateListPaneView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+ method @CallSuper public final android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+ method public void onListPaneViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+ method @CallSuper public final void onViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+ property public final androidx.navigation.fragment.NavHostFragment detailPaneNavHostFragment;
+ property public final androidx.slidingpanelayout.widget.SlidingPaneLayout slidingPaneLayout;
+ }
+
+ @androidx.navigation.Navigator.Name("dialog") public final class DialogFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+ ctor public DialogFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager);
+ method public androidx.navigation.fragment.DialogFragmentNavigator.Destination createDestination();
+ }
+
+ @androidx.navigation.NavDestination.ClassType(DialogFragment::class) public static class DialogFragmentNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+ ctor public DialogFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.DialogFragmentNavigator.Destination> fragmentNavigator);
+ ctor public DialogFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public final String getClassName();
+ method public final androidx.navigation.fragment.DialogFragmentNavigator.Destination setClassName(String className);
+ property public final String className;
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+ ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+ ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+ method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
+ }
+
+ public final class DialogFragmentNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+ method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ }
+
+ public final class FragmentKt {
+ method public static androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment);
+ }
+
+ public final class FragmentNavArgsLazyKt {
+ method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(androidx.fragment.app.Fragment);
+ }
+
+ @androidx.navigation.Navigator.Name("fragment") public class FragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+ ctor public FragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, int containerId);
+ method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+ method @Deprecated public androidx.fragment.app.Fragment instantiateFragment(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, String className, android.os.Bundle? args);
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Fragment::class) public static class FragmentNavigator.Destination extends androidx.navigation.NavDestination {
+ ctor public FragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+ ctor public FragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public final String getClassName();
+ method public final androidx.navigation.fragment.FragmentNavigator.Destination setClassName(String className);
+ property public final String className;
+ }
+
+ public static final class FragmentNavigator.Extras implements androidx.navigation.Navigator.Extras {
+ method public java.util.Map<android.view.View,java.lang.String> getSharedElements();
+ property public final java.util.Map<android.view.View,java.lang.String> sharedElements;
+ }
+
+ public static final class FragmentNavigator.Extras.Builder {
+ ctor public FragmentNavigator.Extras.Builder();
+ method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElement(android.view.View sharedElement, String name);
+ method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElements(java.util.Map<android.view.View,java.lang.String> sharedElements);
+ method public androidx.navigation.fragment.FragmentNavigator.Extras build();
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+ ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+ ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+ method public androidx.navigation.fragment.FragmentNavigator.Destination build();
+ }
+
+ public final class FragmentNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id);
+ method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route);
+ method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,? extends kotlin.Unit> builder);
+ }
+
+ public final class FragmentNavigatorExtrasKt {
+ method public static androidx.navigation.fragment.FragmentNavigator.Extras FragmentNavigatorExtras(kotlin.Pair<? extends android.view.View,java.lang.String>... sharedElements);
+ }
+
+ public class NavHostFragment extends androidx.fragment.app.Fragment implements androidx.navigation.NavHost {
+ ctor public NavHostFragment();
+ method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+ method @Deprecated protected androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> createFragmentNavigator();
+ method public static final androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+ method public final androidx.navigation.NavController getNavController();
+ method @Deprecated @CallSuper protected void onCreateNavController(androidx.navigation.NavController navController);
+ method @CallSuper protected void onCreateNavHostController(androidx.navigation.NavHostController navHostController);
+ property public final androidx.navigation.NavController navController;
+ field public static final androidx.navigation.fragment.NavHostFragment.Companion Companion;
+ }
+
+ public static final class NavHostFragment.Companion {
+ method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+ method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+ method public androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+ }
+
+}
+
diff --git a/navigation/navigation-runtime-ktx/api/2.6.0-beta02.txt b/navigation/navigation-runtime-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-runtime-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-runtime-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-runtime-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-runtime-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-runtime-ktx/api/res-2.6.0-beta02.txt b/navigation/navigation-runtime-ktx/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-runtime-ktx/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-runtime-ktx/api/restricted_2.6.0-beta02.txt b/navigation/navigation-runtime-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-runtime-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-runtime/api/2.6.0-beta02.txt b/navigation/navigation-runtime/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..39d9a7f
--- /dev/null
+++ b/navigation/navigation-runtime/api/2.6.0-beta02.txt
@@ -0,0 +1,224 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+ public final class ActivityKt {
+ method public static androidx.navigation.NavController findNavController(android.app.Activity, @IdRes int viewId);
+ }
+
+ public final class ActivityNavArgsLazyKt {
+ method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(android.app.Activity);
+ }
+
+ @androidx.navigation.Navigator.Name("activity") public class ActivityNavigator extends androidx.navigation.Navigator<androidx.navigation.ActivityNavigator.Destination> {
+ ctor public ActivityNavigator(android.content.Context context);
+ method public static final void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+ method public androidx.navigation.ActivityNavigator.Destination createDestination();
+ method public androidx.navigation.NavDestination? navigate(androidx.navigation.ActivityNavigator.Destination destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ field public static final androidx.navigation.ActivityNavigator.Companion Companion;
+ }
+
+ public static final class ActivityNavigator.Companion {
+ method public void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Activity::class) public static class ActivityNavigator.Destination extends androidx.navigation.NavDestination {
+ ctor public ActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+ ctor public ActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public final String? getAction();
+ method public final android.content.ComponentName? getComponent();
+ method public final android.net.Uri? getData();
+ method public final String? getDataPattern();
+ method public final android.content.Intent? getIntent();
+ method public final String? getTargetPackage();
+ method public final androidx.navigation.ActivityNavigator.Destination setAction(String? action);
+ method public final androidx.navigation.ActivityNavigator.Destination setComponentName(android.content.ComponentName? name);
+ method public final androidx.navigation.ActivityNavigator.Destination setData(android.net.Uri? data);
+ method public final androidx.navigation.ActivityNavigator.Destination setDataPattern(String? dataPattern);
+ method public final androidx.navigation.ActivityNavigator.Destination setIntent(android.content.Intent? intent);
+ method public final androidx.navigation.ActivityNavigator.Destination setTargetPackage(String? packageName);
+ property public final String? action;
+ property public final android.content.ComponentName? component;
+ property public final android.net.Uri? data;
+ property public final String? dataPattern;
+ property public final android.content.Intent? intent;
+ property public final String? targetPackage;
+ }
+
+ public static final class ActivityNavigator.Extras implements androidx.navigation.Navigator.Extras {
+ method public androidx.core.app.ActivityOptionsCompat? getActivityOptions();
+ method public int getFlags();
+ property public final androidx.core.app.ActivityOptionsCompat? activityOptions;
+ property public final int flags;
+ }
+
+ public static final class ActivityNavigator.Extras.Builder {
+ ctor public ActivityNavigator.Extras.Builder();
+ method public androidx.navigation.ActivityNavigator.Extras.Builder addFlags(int flags);
+ method public androidx.navigation.ActivityNavigator.Extras build();
+ method public androidx.navigation.ActivityNavigator.Extras.Builder setActivityOptions(androidx.core.app.ActivityOptionsCompat activityOptions);
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+ ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+ ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
+ method public androidx.navigation.ActivityNavigator.Destination build();
+ method public String? getAction();
+ method public kotlin.reflect.KClass<? extends android.app.Activity>? getActivityClass();
+ method public android.net.Uri? getData();
+ method public String? getDataPattern();
+ method public String? getTargetPackage();
+ method public void setAction(String?);
+ method public void setActivityClass(kotlin.reflect.KClass<? extends android.app.Activity>?);
+ method public void setData(android.net.Uri?);
+ method public void setDataPattern(String?);
+ method public void setTargetPackage(String?);
+ property public final String? action;
+ property public final kotlin.reflect.KClass<? extends android.app.Activity>? activityClass;
+ property public final android.net.Uri? data;
+ property public final String? dataPattern;
+ property public final String? targetPackage;
+ }
+
+ public final class ActivityNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ }
+
+ public final class ActivityNavigatorExtrasKt {
+ method public static androidx.navigation.ActivityNavigator.Extras ActivityNavigatorExtras(optional androidx.core.app.ActivityOptionsCompat? activityOptions, optional int flags);
+ }
+
+ public class NavController {
+ ctor public NavController(android.content.Context context);
+ method public void addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+ method @MainThread public final boolean clearBackStack(String route);
+ method @MainThread public final boolean clearBackStack(@IdRes int destinationId);
+ method public androidx.navigation.NavDeepLinkBuilder createDeepLink();
+ method public androidx.navigation.NavBackStackEntry getBackStackEntry(@IdRes int destinationId);
+ method public final androidx.navigation.NavBackStackEntry getBackStackEntry(String route);
+ method public androidx.navigation.NavBackStackEntry? getCurrentBackStackEntry();
+ method public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> getCurrentBackStackEntryFlow();
+ method public androidx.navigation.NavDestination? getCurrentDestination();
+ method @MainThread public androidx.navigation.NavGraph getGraph();
+ method public androidx.navigation.NavInflater getNavInflater();
+ method public androidx.navigation.NavigatorProvider getNavigatorProvider();
+ method public androidx.navigation.NavBackStackEntry? getPreviousBackStackEntry();
+ method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
+ method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+ method @MainThread public void navigate(@IdRes int resId);
+ method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args);
+ method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public void navigate(android.net.Uri deepLink);
+ method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request);
+ method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public void navigate(androidx.navigation.NavDirections directions);
+ method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.Navigator.Extras navigatorExtras);
+ method @MainThread public final void navigate(String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+ method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions);
+ method @MainThread public final void navigate(String route);
+ method @MainThread public boolean navigateUp();
+ method @MainThread public boolean popBackStack();
+ method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive);
+ method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive, boolean saveState);
+ method @MainThread public final boolean popBackStack(String route, boolean inclusive, optional boolean saveState);
+ method @MainThread public final boolean popBackStack(String route, boolean inclusive);
+ method public void removeOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+ method @CallSuper public void restoreState(android.os.Bundle? navState);
+ method @CallSuper public android.os.Bundle? saveState();
+ method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph);
+ method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId);
+ method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId, android.os.Bundle? startDestinationArgs);
+ method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph graph, android.os.Bundle? startDestinationArgs);
+ property public androidx.navigation.NavBackStackEntry? currentBackStackEntry;
+ property public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> currentBackStackEntryFlow;
+ property public androidx.navigation.NavDestination? currentDestination;
+ property @MainThread public androidx.navigation.NavGraph graph;
+ property public androidx.navigation.NavInflater navInflater;
+ property public androidx.navigation.NavigatorProvider navigatorProvider;
+ property public androidx.navigation.NavBackStackEntry? previousBackStackEntry;
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> visibleEntries;
+ field public static final androidx.navigation.NavController.Companion Companion;
+ field public static final String KEY_DEEP_LINK_INTENT = "android-support-nav:controller:deepLinkIntent";
+ }
+
+ public static final class NavController.Companion {
+ }
+
+ public static fun interface NavController.OnDestinationChangedListener {
+ method public void onDestinationChanged(androidx.navigation.NavController controller, androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+ }
+
+ public final class NavControllerKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavDeepLinkBuilder {
+ ctor public NavDeepLinkBuilder(android.content.Context context);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(String route, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(String route);
+ method public android.app.PendingIntent createPendingIntent();
+ method public androidx.core.app.TaskStackBuilder createTaskStackBuilder();
+ method public androidx.navigation.NavDeepLinkBuilder setArguments(android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder setComponentName(Class<? extends android.app.Activity> activityClass);
+ method public androidx.navigation.NavDeepLinkBuilder setComponentName(android.content.ComponentName componentName);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute);
+ method public androidx.navigation.NavDeepLinkBuilder setGraph(@NavigationRes int navGraphId);
+ method public androidx.navigation.NavDeepLinkBuilder setGraph(androidx.navigation.NavGraph navGraph);
+ }
+
+ public interface NavHost {
+ method public androidx.navigation.NavController getNavController();
+ property public abstract androidx.navigation.NavController navController;
+ }
+
+ public class NavHostController extends androidx.navigation.NavController {
+ ctor public NavHostController(android.content.Context context);
+ method public final void enableOnBackPressed(boolean enabled);
+ method public final void setLifecycleOwner(androidx.lifecycle.LifecycleOwner owner);
+ method public final void setOnBackPressedDispatcher(androidx.activity.OnBackPressedDispatcher dispatcher);
+ method public final void setViewModelStore(androidx.lifecycle.ViewModelStore viewModelStore);
+ }
+
+ public final class NavHostKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavInflater {
+ ctor public NavInflater(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider);
+ method public androidx.navigation.NavGraph inflate(@NavigationRes int graphResId);
+ field public static final androidx.navigation.NavInflater.Companion Companion;
+ }
+
+ public static final class NavInflater.Companion {
+ }
+
+ public final class Navigation {
+ method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args);
+ method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId);
+ method public static android.view.View.OnClickListener createNavigateOnClickListener(androidx.navigation.NavDirections directions);
+ method public static androidx.navigation.NavController findNavController(android.app.Activity activity, @IdRes int viewId);
+ method public static androidx.navigation.NavController findNavController(android.view.View view);
+ method public static void setViewNavController(android.view.View view, androidx.navigation.NavController? controller);
+ field public static final androidx.navigation.Navigation INSTANCE;
+ }
+
+ public final class ViewKt {
+ method public static androidx.navigation.NavController findNavController(android.view.View);
+ }
+
+}
+
diff --git a/navigation/navigation-runtime/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-runtime/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..6faf37c
--- /dev/null
+++ b/navigation/navigation-runtime/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,229 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+ public final class ActivityKt {
+ method public static androidx.navigation.NavController findNavController(android.app.Activity, @IdRes int viewId);
+ }
+
+ public final class ActivityNavArgsLazyKt {
+ method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(android.app.Activity);
+ }
+
+ @androidx.navigation.Navigator.Name("activity") public class ActivityNavigator extends androidx.navigation.Navigator<androidx.navigation.ActivityNavigator.Destination> {
+ ctor public ActivityNavigator(android.content.Context context);
+ method public static final void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+ method public androidx.navigation.ActivityNavigator.Destination createDestination();
+ method public androidx.navigation.NavDestination? navigate(androidx.navigation.ActivityNavigator.Destination destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ field public static final androidx.navigation.ActivityNavigator.Companion Companion;
+ }
+
+ public static final class ActivityNavigator.Companion {
+ method public void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Activity::class) public static class ActivityNavigator.Destination extends androidx.navigation.NavDestination {
+ ctor public ActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+ ctor public ActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public final String? getAction();
+ method public final android.content.ComponentName? getComponent();
+ method public final android.net.Uri? getData();
+ method public final String? getDataPattern();
+ method public final android.content.Intent? getIntent();
+ method public final String? getTargetPackage();
+ method public final androidx.navigation.ActivityNavigator.Destination setAction(String? action);
+ method public final androidx.navigation.ActivityNavigator.Destination setComponentName(android.content.ComponentName? name);
+ method public final androidx.navigation.ActivityNavigator.Destination setData(android.net.Uri? data);
+ method public final androidx.navigation.ActivityNavigator.Destination setDataPattern(String? dataPattern);
+ method public final androidx.navigation.ActivityNavigator.Destination setIntent(android.content.Intent? intent);
+ method public final androidx.navigation.ActivityNavigator.Destination setTargetPackage(String? packageName);
+ property public final String? action;
+ property public final android.content.ComponentName? component;
+ property public final android.net.Uri? data;
+ property public final String? dataPattern;
+ property public final android.content.Intent? intent;
+ property public final String? targetPackage;
+ }
+
+ public static final class ActivityNavigator.Extras implements androidx.navigation.Navigator.Extras {
+ method public androidx.core.app.ActivityOptionsCompat? getActivityOptions();
+ method public int getFlags();
+ property public final androidx.core.app.ActivityOptionsCompat? activityOptions;
+ property public final int flags;
+ }
+
+ public static final class ActivityNavigator.Extras.Builder {
+ ctor public ActivityNavigator.Extras.Builder();
+ method public androidx.navigation.ActivityNavigator.Extras.Builder addFlags(int flags);
+ method public androidx.navigation.ActivityNavigator.Extras build();
+ method public androidx.navigation.ActivityNavigator.Extras.Builder setActivityOptions(androidx.core.app.ActivityOptionsCompat activityOptions);
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+ ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+ ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
+ method public androidx.navigation.ActivityNavigator.Destination build();
+ method public String? getAction();
+ method public kotlin.reflect.KClass<? extends android.app.Activity>? getActivityClass();
+ method public android.net.Uri? getData();
+ method public String? getDataPattern();
+ method public String? getTargetPackage();
+ method public void setAction(String?);
+ method public void setActivityClass(kotlin.reflect.KClass<? extends android.app.Activity>?);
+ method public void setData(android.net.Uri?);
+ method public void setDataPattern(String?);
+ method public void setTargetPackage(String?);
+ property public final String? action;
+ property public final kotlin.reflect.KClass<? extends android.app.Activity>? activityClass;
+ property public final android.net.Uri? data;
+ property public final String? dataPattern;
+ property public final String? targetPackage;
+ }
+
+ public final class ActivityNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ }
+
+ public final class ActivityNavigatorExtrasKt {
+ method public static androidx.navigation.ActivityNavigator.Extras ActivityNavigatorExtras(optional androidx.core.app.ActivityOptionsCompat? activityOptions, optional int flags);
+ }
+
+ public class NavController {
+ ctor public NavController(android.content.Context context);
+ method public void addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+ method @MainThread public final boolean clearBackStack(String route);
+ method @MainThread public final boolean clearBackStack(@IdRes int destinationId);
+ method public androidx.navigation.NavDeepLinkBuilder createDeepLink();
+ method @androidx.navigation.NavDeepLinkSaveStateControl public static final void enableDeepLinkSaveState(boolean saveState);
+ method public androidx.navigation.NavBackStackEntry getBackStackEntry(@IdRes int destinationId);
+ method public final androidx.navigation.NavBackStackEntry getBackStackEntry(String route);
+ method public androidx.navigation.NavBackStackEntry? getCurrentBackStackEntry();
+ method public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> getCurrentBackStackEntryFlow();
+ method public androidx.navigation.NavDestination? getCurrentDestination();
+ method @MainThread public androidx.navigation.NavGraph getGraph();
+ method public androidx.navigation.NavInflater getNavInflater();
+ method public androidx.navigation.NavigatorProvider getNavigatorProvider();
+ method public androidx.navigation.NavBackStackEntry? getPreviousBackStackEntry();
+ method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
+ method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+ method @MainThread public void navigate(@IdRes int resId);
+ method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args);
+ method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public void navigate(android.net.Uri deepLink);
+ method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request);
+ method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public void navigate(androidx.navigation.NavDirections directions);
+ method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.Navigator.Extras navigatorExtras);
+ method @MainThread public final void navigate(String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+ method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions);
+ method @MainThread public final void navigate(String route);
+ method @MainThread public boolean navigateUp();
+ method @MainThread public boolean popBackStack();
+ method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive);
+ method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive, boolean saveState);
+ method @MainThread public final boolean popBackStack(String route, boolean inclusive, optional boolean saveState);
+ method @MainThread public final boolean popBackStack(String route, boolean inclusive);
+ method public void removeOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+ method @CallSuper public void restoreState(android.os.Bundle? navState);
+ method @CallSuper public android.os.Bundle? saveState();
+ method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph);
+ method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId);
+ method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId, android.os.Bundle? startDestinationArgs);
+ method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph graph, android.os.Bundle? startDestinationArgs);
+ property public androidx.navigation.NavBackStackEntry? currentBackStackEntry;
+ property public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> currentBackStackEntryFlow;
+ property public androidx.navigation.NavDestination? currentDestination;
+ property @MainThread public androidx.navigation.NavGraph graph;
+ property public androidx.navigation.NavInflater navInflater;
+ property public androidx.navigation.NavigatorProvider navigatorProvider;
+ property public androidx.navigation.NavBackStackEntry? previousBackStackEntry;
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> visibleEntries;
+ field public static final androidx.navigation.NavController.Companion Companion;
+ field public static final String KEY_DEEP_LINK_INTENT = "android-support-nav:controller:deepLinkIntent";
+ }
+
+ public static final class NavController.Companion {
+ method @androidx.navigation.NavDeepLinkSaveStateControl public void enableDeepLinkSaveState(boolean saveState);
+ }
+
+ public static fun interface NavController.OnDestinationChangedListener {
+ method public void onDestinationChanged(androidx.navigation.NavController controller, androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+ }
+
+ public final class NavControllerKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavDeepLinkBuilder {
+ ctor public NavDeepLinkBuilder(android.content.Context context);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(String route, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(String route);
+ method public android.app.PendingIntent createPendingIntent();
+ method public androidx.core.app.TaskStackBuilder createTaskStackBuilder();
+ method public androidx.navigation.NavDeepLinkBuilder setArguments(android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder setComponentName(Class<? extends android.app.Activity> activityClass);
+ method public androidx.navigation.NavDeepLinkBuilder setComponentName(android.content.ComponentName componentName);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute);
+ method public androidx.navigation.NavDeepLinkBuilder setGraph(@NavigationRes int navGraphId);
+ method public androidx.navigation.NavDeepLinkBuilder setGraph(androidx.navigation.NavGraph navGraph);
+ }
+
+ @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface NavDeepLinkSaveStateControl {
+ }
+
+ public interface NavHost {
+ method public androidx.navigation.NavController getNavController();
+ property public abstract androidx.navigation.NavController navController;
+ }
+
+ public class NavHostController extends androidx.navigation.NavController {
+ ctor public NavHostController(android.content.Context context);
+ method public final void enableOnBackPressed(boolean enabled);
+ method public final void setLifecycleOwner(androidx.lifecycle.LifecycleOwner owner);
+ method public final void setOnBackPressedDispatcher(androidx.activity.OnBackPressedDispatcher dispatcher);
+ method public final void setViewModelStore(androidx.lifecycle.ViewModelStore viewModelStore);
+ }
+
+ public final class NavHostKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavInflater {
+ ctor public NavInflater(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider);
+ method public androidx.navigation.NavGraph inflate(@NavigationRes int graphResId);
+ field public static final androidx.navigation.NavInflater.Companion Companion;
+ }
+
+ public static final class NavInflater.Companion {
+ }
+
+ public final class Navigation {
+ method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args);
+ method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId);
+ method public static android.view.View.OnClickListener createNavigateOnClickListener(androidx.navigation.NavDirections directions);
+ method public static androidx.navigation.NavController findNavController(android.app.Activity activity, @IdRes int viewId);
+ method public static androidx.navigation.NavController findNavController(android.view.View view);
+ method public static void setViewNavController(android.view.View view, androidx.navigation.NavController? controller);
+ field public static final androidx.navigation.Navigation INSTANCE;
+ }
+
+ public final class ViewKt {
+ method public static androidx.navigation.NavController findNavController(android.view.View);
+ }
+
+}
+
diff --git a/navigation/navigation-runtime/api/res-2.6.0-beta02.txt b/navigation/navigation-runtime/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-runtime/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-runtime/api/restricted_2.6.0-beta02.txt b/navigation/navigation-runtime/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..39d9a7f
--- /dev/null
+++ b/navigation/navigation-runtime/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,224 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+ public final class ActivityKt {
+ method public static androidx.navigation.NavController findNavController(android.app.Activity, @IdRes int viewId);
+ }
+
+ public final class ActivityNavArgsLazyKt {
+ method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(android.app.Activity);
+ }
+
+ @androidx.navigation.Navigator.Name("activity") public class ActivityNavigator extends androidx.navigation.Navigator<androidx.navigation.ActivityNavigator.Destination> {
+ ctor public ActivityNavigator(android.content.Context context);
+ method public static final void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+ method public androidx.navigation.ActivityNavigator.Destination createDestination();
+ method public androidx.navigation.NavDestination? navigate(androidx.navigation.ActivityNavigator.Destination destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ field public static final androidx.navigation.ActivityNavigator.Companion Companion;
+ }
+
+ public static final class ActivityNavigator.Companion {
+ method public void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+ }
+
+ @androidx.navigation.NavDestination.ClassType(Activity::class) public static class ActivityNavigator.Destination extends androidx.navigation.NavDestination {
+ ctor public ActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+ ctor public ActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+ method public final String? getAction();
+ method public final android.content.ComponentName? getComponent();
+ method public final android.net.Uri? getData();
+ method public final String? getDataPattern();
+ method public final android.content.Intent? getIntent();
+ method public final String? getTargetPackage();
+ method public final androidx.navigation.ActivityNavigator.Destination setAction(String? action);
+ method public final androidx.navigation.ActivityNavigator.Destination setComponentName(android.content.ComponentName? name);
+ method public final androidx.navigation.ActivityNavigator.Destination setData(android.net.Uri? data);
+ method public final androidx.navigation.ActivityNavigator.Destination setDataPattern(String? dataPattern);
+ method public final androidx.navigation.ActivityNavigator.Destination setIntent(android.content.Intent? intent);
+ method public final androidx.navigation.ActivityNavigator.Destination setTargetPackage(String? packageName);
+ property public final String? action;
+ property public final android.content.ComponentName? component;
+ property public final android.net.Uri? data;
+ property public final String? dataPattern;
+ property public final android.content.Intent? intent;
+ property public final String? targetPackage;
+ }
+
+ public static final class ActivityNavigator.Extras implements androidx.navigation.Navigator.Extras {
+ method public androidx.core.app.ActivityOptionsCompat? getActivityOptions();
+ method public int getFlags();
+ property public final androidx.core.app.ActivityOptionsCompat? activityOptions;
+ property public final int flags;
+ }
+
+ public static final class ActivityNavigator.Extras.Builder {
+ ctor public ActivityNavigator.Extras.Builder();
+ method public androidx.navigation.ActivityNavigator.Extras.Builder addFlags(int flags);
+ method public androidx.navigation.ActivityNavigator.Extras build();
+ method public androidx.navigation.ActivityNavigator.Extras.Builder setActivityOptions(androidx.core.app.ActivityOptionsCompat activityOptions);
+ }
+
+ @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+ ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+ ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
+ method public androidx.navigation.ActivityNavigator.Destination build();
+ method public String? getAction();
+ method public kotlin.reflect.KClass<? extends android.app.Activity>? getActivityClass();
+ method public android.net.Uri? getData();
+ method public String? getDataPattern();
+ method public String? getTargetPackage();
+ method public void setAction(String?);
+ method public void setActivityClass(kotlin.reflect.KClass<? extends android.app.Activity>?);
+ method public void setData(android.net.Uri?);
+ method public void setDataPattern(String?);
+ method public void setTargetPackage(String?);
+ property public final String? action;
+ property public final kotlin.reflect.KClass<? extends android.app.Activity>? activityClass;
+ property public final android.net.Uri? data;
+ property public final String? dataPattern;
+ property public final String? targetPackage;
+ }
+
+ public final class ActivityNavigatorDestinationBuilderKt {
+ method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+ }
+
+ public final class ActivityNavigatorExtrasKt {
+ method public static androidx.navigation.ActivityNavigator.Extras ActivityNavigatorExtras(optional androidx.core.app.ActivityOptionsCompat? activityOptions, optional int flags);
+ }
+
+ public class NavController {
+ ctor public NavController(android.content.Context context);
+ method public void addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+ method @MainThread public final boolean clearBackStack(String route);
+ method @MainThread public final boolean clearBackStack(@IdRes int destinationId);
+ method public androidx.navigation.NavDeepLinkBuilder createDeepLink();
+ method public androidx.navigation.NavBackStackEntry getBackStackEntry(@IdRes int destinationId);
+ method public final androidx.navigation.NavBackStackEntry getBackStackEntry(String route);
+ method public androidx.navigation.NavBackStackEntry? getCurrentBackStackEntry();
+ method public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> getCurrentBackStackEntryFlow();
+ method public androidx.navigation.NavDestination? getCurrentDestination();
+ method @MainThread public androidx.navigation.NavGraph getGraph();
+ method public androidx.navigation.NavInflater getNavInflater();
+ method public androidx.navigation.NavigatorProvider getNavigatorProvider();
+ method public androidx.navigation.NavBackStackEntry? getPreviousBackStackEntry();
+ method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
+ method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
+ method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+ method @MainThread public void navigate(@IdRes int resId);
+ method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args);
+ method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public void navigate(android.net.Uri deepLink);
+ method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request);
+ method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public void navigate(androidx.navigation.NavDirections directions);
+ method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.NavOptions? navOptions);
+ method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.Navigator.Extras navigatorExtras);
+ method @MainThread public final void navigate(String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+ method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+ method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions);
+ method @MainThread public final void navigate(String route);
+ method @MainThread public boolean navigateUp();
+ method @MainThread public boolean popBackStack();
+ method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive);
+ method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive, boolean saveState);
+ method @MainThread public final boolean popBackStack(String route, boolean inclusive, optional boolean saveState);
+ method @MainThread public final boolean popBackStack(String route, boolean inclusive);
+ method public void removeOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+ method @CallSuper public void restoreState(android.os.Bundle? navState);
+ method @CallSuper public android.os.Bundle? saveState();
+ method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph);
+ method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId);
+ method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId, android.os.Bundle? startDestinationArgs);
+ method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph graph, android.os.Bundle? startDestinationArgs);
+ property public androidx.navigation.NavBackStackEntry? currentBackStackEntry;
+ property public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> currentBackStackEntryFlow;
+ property public androidx.navigation.NavDestination? currentDestination;
+ property @MainThread public androidx.navigation.NavGraph graph;
+ property public androidx.navigation.NavInflater navInflater;
+ property public androidx.navigation.NavigatorProvider navigatorProvider;
+ property public androidx.navigation.NavBackStackEntry? previousBackStackEntry;
+ property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> visibleEntries;
+ field public static final androidx.navigation.NavController.Companion Companion;
+ field public static final String KEY_DEEP_LINK_INTENT = "android-support-nav:controller:deepLinkIntent";
+ }
+
+ public static final class NavController.Companion {
+ }
+
+ public static fun interface NavController.OnDestinationChangedListener {
+ method public void onDestinationChanged(androidx.navigation.NavController controller, androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+ }
+
+ public final class NavControllerKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavDeepLinkBuilder {
+ ctor public NavDeepLinkBuilder(android.content.Context context);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(String route, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder addDestination(String route);
+ method public android.app.PendingIntent createPendingIntent();
+ method public androidx.core.app.TaskStackBuilder createTaskStackBuilder();
+ method public androidx.navigation.NavDeepLinkBuilder setArguments(android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder setComponentName(Class<? extends android.app.Activity> activityClass);
+ method public androidx.navigation.NavDeepLinkBuilder setComponentName(android.content.ComponentName componentName);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute, optional android.os.Bundle? args);
+ method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute);
+ method public androidx.navigation.NavDeepLinkBuilder setGraph(@NavigationRes int navGraphId);
+ method public androidx.navigation.NavDeepLinkBuilder setGraph(androidx.navigation.NavGraph navGraph);
+ }
+
+ public interface NavHost {
+ method public androidx.navigation.NavController getNavController();
+ property public abstract androidx.navigation.NavController navController;
+ }
+
+ public class NavHostController extends androidx.navigation.NavController {
+ ctor public NavHostController(android.content.Context context);
+ method public final void enableOnBackPressed(boolean enabled);
+ method public final void setLifecycleOwner(androidx.lifecycle.LifecycleOwner owner);
+ method public final void setOnBackPressedDispatcher(androidx.activity.OnBackPressedDispatcher dispatcher);
+ method public final void setViewModelStore(androidx.lifecycle.ViewModelStore viewModelStore);
+ }
+
+ public final class NavHostKt {
+ method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+ }
+
+ public final class NavInflater {
+ ctor public NavInflater(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider);
+ method public androidx.navigation.NavGraph inflate(@NavigationRes int graphResId);
+ field public static final androidx.navigation.NavInflater.Companion Companion;
+ }
+
+ public static final class NavInflater.Companion {
+ }
+
+ public final class Navigation {
+ method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args);
+ method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId);
+ method public static android.view.View.OnClickListener createNavigateOnClickListener(androidx.navigation.NavDirections directions);
+ method public static androidx.navigation.NavController findNavController(android.app.Activity activity, @IdRes int viewId);
+ method public static androidx.navigation.NavController findNavController(android.view.View view);
+ method public static void setViewNavController(android.view.View view, androidx.navigation.NavController? controller);
+ field public static final androidx.navigation.Navigation INSTANCE;
+ }
+
+ public final class ViewKt {
+ method public static androidx.navigation.NavController findNavController(android.view.View);
+ }
+
+}
+
diff --git a/navigation/navigation-testing/api/2.6.0-beta02.txt b/navigation/navigation-testing/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..89f60a4
--- /dev/null
+++ b/navigation/navigation-testing/api/2.6.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.navigation.testing {
+
+ public final class TestNavHostController extends androidx.navigation.NavHostController {
+ ctor public TestNavHostController(android.content.Context context);
+ method public java.util.List<androidx.navigation.NavBackStackEntry> getBackStack();
+ method public void setCurrentDestination(@IdRes int destId, optional android.os.Bundle args);
+ method public void setCurrentDestination(@IdRes int destId);
+ method public void setCurrentDestination(String destRoute, optional android.os.Bundle args);
+ method public void setCurrentDestination(String destRoute);
+ property public final java.util.List<androidx.navigation.NavBackStackEntry> backStack;
+ }
+
+ public final class TestNavigatorState extends androidx.navigation.NavigatorState {
+ ctor public TestNavigatorState(optional android.content.Context? context, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+ ctor public TestNavigatorState(optional android.content.Context? context);
+ ctor public TestNavigatorState();
+ method public androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+ method public androidx.navigation.NavBackStackEntry restoreBackStackEntry(androidx.navigation.NavBackStackEntry previouslySavedEntry);
+ }
+
+}
+
diff --git a/navigation/navigation-testing/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-testing/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..89f60a4
--- /dev/null
+++ b/navigation/navigation-testing/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.navigation.testing {
+
+ public final class TestNavHostController extends androidx.navigation.NavHostController {
+ ctor public TestNavHostController(android.content.Context context);
+ method public java.util.List<androidx.navigation.NavBackStackEntry> getBackStack();
+ method public void setCurrentDestination(@IdRes int destId, optional android.os.Bundle args);
+ method public void setCurrentDestination(@IdRes int destId);
+ method public void setCurrentDestination(String destRoute, optional android.os.Bundle args);
+ method public void setCurrentDestination(String destRoute);
+ property public final java.util.List<androidx.navigation.NavBackStackEntry> backStack;
+ }
+
+ public final class TestNavigatorState extends androidx.navigation.NavigatorState {
+ ctor public TestNavigatorState(optional android.content.Context? context, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+ ctor public TestNavigatorState(optional android.content.Context? context);
+ ctor public TestNavigatorState();
+ method public androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+ method public androidx.navigation.NavBackStackEntry restoreBackStackEntry(androidx.navigation.NavBackStackEntry previouslySavedEntry);
+ }
+
+}
+
diff --git a/navigation/navigation-testing/api/res-2.6.0-beta02.txt b/navigation/navigation-testing/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-testing/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-testing/api/restricted_2.6.0-beta02.txt b/navigation/navigation-testing/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..89f60a4
--- /dev/null
+++ b/navigation/navigation-testing/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.navigation.testing {
+
+ public final class TestNavHostController extends androidx.navigation.NavHostController {
+ ctor public TestNavHostController(android.content.Context context);
+ method public java.util.List<androidx.navigation.NavBackStackEntry> getBackStack();
+ method public void setCurrentDestination(@IdRes int destId, optional android.os.Bundle args);
+ method public void setCurrentDestination(@IdRes int destId);
+ method public void setCurrentDestination(String destRoute, optional android.os.Bundle args);
+ method public void setCurrentDestination(String destRoute);
+ property public final java.util.List<androidx.navigation.NavBackStackEntry> backStack;
+ }
+
+ public final class TestNavigatorState extends androidx.navigation.NavigatorState {
+ ctor public TestNavigatorState(optional android.content.Context? context, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+ ctor public TestNavigatorState(optional android.content.Context? context);
+ ctor public TestNavigatorState();
+ method public androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+ method public androidx.navigation.NavBackStackEntry restoreBackStackEntry(androidx.navigation.NavBackStackEntry previouslySavedEntry);
+ }
+
+}
+
diff --git a/navigation/navigation-ui-ktx/api/2.6.0-beta02.txt b/navigation/navigation-ui-ktx/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-ui-ktx/api/2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-ui-ktx/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-ui-ktx/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-ui-ktx/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-ui-ktx/api/res-2.6.0-beta02.txt b/navigation/navigation-ui-ktx/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/navigation-ui-ktx/api/res-2.6.0-beta02.txt
diff --git a/navigation/navigation-ui-ktx/api/restricted_2.6.0-beta02.txt b/navigation/navigation-ui-ktx/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/navigation/navigation-ui-ktx/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/navigation/navigation-ui/api/2.6.0-beta02.txt b/navigation/navigation-ui/api/2.6.0-beta02.txt
new file mode 100644
index 0000000..27e99a9
--- /dev/null
+++ b/navigation/navigation-ui/api/2.6.0-beta02.txt
@@ -0,0 +1,88 @@
+// Signature format: 4.0
+package androidx.navigation.ui {
+
+ public final class ActivityKt {
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ }
+
+ public final class AppBarConfiguration {
+ method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
+ method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
+ method public androidx.customview.widget.Openable? getOpenableLayout();
+ method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+ method public boolean isTopLevelDestination(androidx.navigation.NavDestination destination);
+ property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+ property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+ property public final androidx.customview.widget.Openable? openableLayout;
+ property public final java.util.Set<java.lang.Integer> topLevelDestinations;
+ }
+
+ public static final class AppBarConfiguration.Builder {
+ ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+ ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+ ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+ ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
+ method public androidx.navigation.ui.AppBarConfiguration build();
+ method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
+ }
+
+ public static fun interface AppBarConfiguration.OnNavigateUpListener {
+ method public boolean onNavigateUp();
+ }
+
+ public final class AppBarConfigurationKt {
+ method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(androidx.navigation.NavGraph navGraph, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+ method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(android.view.Menu topLevelMenu, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+ method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(java.util.Set<java.lang.Integer> topLevelDestinationIds, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+ }
+
+ public final class BottomNavigationViewKt {
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView, androidx.navigation.NavController navController);
+ }
+
+ public final class CollapsingToolbarLayoutKt {
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ }
+
+ public final class MenuItemKt {
+ method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController navController);
+ }
+
+ public final class NavControllerKt {
+ method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable? drawerLayout);
+ method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration appBarConfiguration);
+ }
+
+ public final class NavigationUI {
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController);
+ field public static final androidx.navigation.ui.NavigationUI INSTANCE;
+ }
+
+ public final class NavigationViewKt {
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController navController);
+ }
+
+ public final class ToolbarKt {
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ }
+
+}
+
diff --git a/navigation/navigation-ui/api/public_plus_experimental_2.6.0-beta02.txt b/navigation/navigation-ui/api/public_plus_experimental_2.6.0-beta02.txt
new file mode 100644
index 0000000..7cb97e2
--- /dev/null
+++ b/navigation/navigation-ui/api/public_plus_experimental_2.6.0-beta02.txt
@@ -0,0 +1,94 @@
+// Signature format: 4.0
+package androidx.navigation.ui {
+
+ public final class ActivityKt {
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ }
+
+ public final class AppBarConfiguration {
+ method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
+ method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
+ method public androidx.customview.widget.Openable? getOpenableLayout();
+ method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+ method public boolean isTopLevelDestination(androidx.navigation.NavDestination destination);
+ property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+ property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+ property public final androidx.customview.widget.Openable? openableLayout;
+ property public final java.util.Set<java.lang.Integer> topLevelDestinations;
+ }
+
+ public static final class AppBarConfiguration.Builder {
+ ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+ ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+ ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+ ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
+ method public androidx.navigation.ui.AppBarConfiguration build();
+ method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
+ }
+
+ public static fun interface AppBarConfiguration.OnNavigateUpListener {
+ method public boolean onNavigateUp();
+ }
+
+ public final class AppBarConfigurationKt {
+ method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(androidx.navigation.NavGraph navGraph, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+ method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(android.view.Menu topLevelMenu, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+ method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(java.util.Set<java.lang.Integer> topLevelDestinationIds, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+ }
+
+ public final class BottomNavigationViewKt {
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView, androidx.navigation.NavController navController);
+ }
+
+ public final class CollapsingToolbarLayoutKt {
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ }
+
+ public final class MenuItemKt {
+ method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController navController);
+ }
+
+ public final class NavControllerKt {
+ method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable? drawerLayout);
+ method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration appBarConfiguration);
+ }
+
+ public final class NavigationUI {
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+ method @androidx.navigation.ui.NavigationUiSaveStateControl public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController, boolean saveState);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+ method @androidx.navigation.ui.NavigationUiSaveStateControl public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController, boolean saveState);
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController);
+ method @androidx.navigation.ui.NavigationUiSaveStateControl public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController, boolean saveState);
+ field public static final androidx.navigation.ui.NavigationUI INSTANCE;
+ }
+
+ @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface NavigationUiSaveStateControl {
+ }
+
+ public final class NavigationViewKt {
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController navController);
+ }
+
+ public final class ToolbarKt {
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ }
+
+}
+
diff --git a/navigation/navigation-ui/api/res-2.6.0-beta02.txt b/navigation/navigation-ui/api/res-2.6.0-beta02.txt
new file mode 100644
index 0000000..e65fdbe
--- /dev/null
+++ b/navigation/navigation-ui/api/res-2.6.0-beta02.txt
@@ -0,0 +1,8 @@
+anim nav_default_enter_anim
+anim nav_default_exit_anim
+anim nav_default_pop_enter_anim
+anim nav_default_pop_exit_anim
+animator nav_default_enter_anim
+animator nav_default_exit_anim
+animator nav_default_pop_enter_anim
+animator nav_default_pop_exit_anim
diff --git a/navigation/navigation-ui/api/restricted_2.6.0-beta02.txt b/navigation/navigation-ui/api/restricted_2.6.0-beta02.txt
new file mode 100644
index 0000000..27e99a9
--- /dev/null
+++ b/navigation/navigation-ui/api/restricted_2.6.0-beta02.txt
@@ -0,0 +1,88 @@
+// Signature format: 4.0
+package androidx.navigation.ui {
+
+ public final class ActivityKt {
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ }
+
+ public final class AppBarConfiguration {
+ method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
+ method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
+ method public androidx.customview.widget.Openable? getOpenableLayout();
+ method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+ method public boolean isTopLevelDestination(androidx.navigation.NavDestination destination);
+ property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+ property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+ property public final androidx.customview.widget.Openable? openableLayout;
+ property public final java.util.Set<java.lang.Integer> topLevelDestinations;
+ }
+
+ public static final class AppBarConfiguration.Builder {
+ ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+ ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+ ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+ ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
+ method public androidx.navigation.ui.AppBarConfiguration build();
+ method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
+ }
+
+ public static fun interface AppBarConfiguration.OnNavigateUpListener {
+ method public boolean onNavigateUp();
+ }
+
+ public final class AppBarConfigurationKt {
+ method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(androidx.navigation.NavGraph navGraph, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+ method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(android.view.Menu topLevelMenu, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+ method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(java.util.Set<java.lang.Integer> topLevelDestinationIds, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+ }
+
+ public final class BottomNavigationViewKt {
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView, androidx.navigation.NavController navController);
+ }
+
+ public final class CollapsingToolbarLayoutKt {
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ }
+
+ public final class MenuItemKt {
+ method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController navController);
+ }
+
+ public final class NavControllerKt {
+ method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable? drawerLayout);
+ method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration appBarConfiguration);
+ }
+
+ public final class NavigationUI {
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController);
+ field public static final androidx.navigation.ui.NavigationUI INSTANCE;
+ }
+
+ public final class NavigationViewKt {
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController navController);
+ }
+
+ public final class ToolbarKt {
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ }
+
+}
+
diff --git a/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java b/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java
index 8d57a15..95635c5 100644
--- a/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java
+++ b/preference/preference/src/main/java/androidx/preference/PreferenceViewHolder.java
@@ -25,7 +25,7 @@
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
@@ -61,7 +61,7 @@
}
/** @hide */
- @RestrictTo(RestrictTo.Scope.TESTS)
+ @VisibleForTesting
@NonNull
public static PreferenceViewHolder createInstanceForTests(@NonNull View itemView) {
return new PreferenceViewHolder(itemView);
diff --git a/privacysandbox/ads/ads-adservices-java/api/1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/1.0.0-beta04.txt
new file mode 100644
index 0000000..26eea8b
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/1.0.0-beta04.txt
@@ -0,0 +1,92 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+ public abstract class AdIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AdIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+ public abstract class AdSelectionManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+ }
+
+ public static final class AdSelectionManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+ public abstract class AppSetIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AppSetIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+ public abstract class CustomAudienceManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+ }
+
+ public static final class CustomAudienceManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+ public abstract class MeasurementManagerFutures {
+ method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+ method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+ }
+
+ public static final class MeasurementManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+ public abstract class TopicsManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+ }
+
+ public static final class TopicsManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices-java/api/public_plus_experimental_1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/public_plus_experimental_1.0.0-beta04.txt
new file mode 100644
index 0000000..26eea8b
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/public_plus_experimental_1.0.0-beta04.txt
@@ -0,0 +1,92 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+ public abstract class AdIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AdIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+ public abstract class AdSelectionManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+ }
+
+ public static final class AdSelectionManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+ public abstract class AppSetIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AppSetIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+ public abstract class CustomAudienceManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+ }
+
+ public static final class CustomAudienceManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+ public abstract class MeasurementManagerFutures {
+ method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+ method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+ }
+
+ public static final class MeasurementManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+ public abstract class TopicsManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+ }
+
+ public static final class TopicsManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices-java/api/res-1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/res-1.0.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/res-1.0.0-beta04.txt
diff --git a/privacysandbox/ads/ads-adservices-java/api/restricted_1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices-java/api/restricted_1.0.0-beta04.txt
new file mode 100644
index 0000000..26eea8b
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/restricted_1.0.0-beta04.txt
@@ -0,0 +1,92 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+ public abstract class AdIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AdIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+ public abstract class AdSelectionManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+ }
+
+ public static final class AdSelectionManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+ public abstract class AppSetIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AppSetIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+ public abstract class CustomAudienceManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+ }
+
+ public static final class CustomAudienceManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+ public abstract class MeasurementManagerFutures {
+ method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+ method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+ }
+
+ public static final class MeasurementManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+ public abstract class TopicsManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+ }
+
+ public static final class TopicsManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/1.0.0-beta04.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/1.0.0-beta04.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+ public final class AdId {
+ method public String getAdId();
+ method public boolean isLimitAdTrackingEnabled();
+ property public final String adId;
+ property public final boolean isLimitAdTrackingEnabled;
+ }
+
+ public abstract class AdIdManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+ method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+ }
+
+ public static final class AdIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+ public final class AdSelectionConfig {
+ ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+ method public android.net.Uri getDecisionLogicUri();
+ method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+ method public android.net.Uri getTrustedScoringSignalsUri();
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+ property public final android.net.Uri decisionLogicUri;
+ property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+ property public final android.net.Uri trustedScoringSignalsUri;
+ }
+
+ public abstract class AdSelectionManager {
+ method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+ }
+
+ public static final class AdSelectionManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ }
+
+ public final class AdSelectionOutcome {
+ ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+ method public long getAdSelectionId();
+ method public android.net.Uri getRenderUri();
+ property public final long adSelectionId;
+ property public final android.net.Uri renderUri;
+ }
+
+ public final class ReportImpressionRequest {
+ ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+ method public long getAdSelectionId();
+ property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+ property public final long adSelectionId;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+ public final class AppSetId {
+ ctor public AppSetId(String id, int scope);
+ method public String getId();
+ method public int getScope();
+ property public final String id;
+ property public final int scope;
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+ field public static final int SCOPE_APP = 1; // 0x1
+ field public static final int SCOPE_DEVELOPER = 2; // 0x2
+ }
+
+ public static final class AppSetId.Companion {
+ }
+
+ public abstract class AppSetIdManager {
+ method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+ method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+ }
+
+ public static final class AppSetIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+ public final class AdData {
+ ctor public AdData(android.net.Uri renderUri, String metadata);
+ method public String getMetadata();
+ method public android.net.Uri getRenderUri();
+ property public final String metadata;
+ property public final android.net.Uri renderUri;
+ }
+
+ public final class AdSelectionSignals {
+ ctor public AdSelectionSignals(String signals);
+ method public String getSignals();
+ property public final String signals;
+ }
+
+ public final class AdTechIdentifier {
+ ctor public AdTechIdentifier(String identifier);
+ method public String getIdentifier();
+ property public final String identifier;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+ public final class CustomAudience {
+ ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+ method public java.time.Instant? getActivationTime();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+ method public android.net.Uri getBiddingLogicUri();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public android.net.Uri getDailyUpdateUri();
+ method public java.time.Instant? getExpirationTime();
+ method public String getName();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+ property public final java.time.Instant? activationTime;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+ property public final android.net.Uri biddingLogicUri;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final android.net.Uri dailyUpdateUri;
+ property public final java.time.Instant? expirationTime;
+ property public final String name;
+ property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+ }
+
+ public static final class CustomAudience.Builder {
+ ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+ }
+
+ public abstract class CustomAudienceManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+ }
+
+ public static final class CustomAudienceManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ }
+
+ public final class JoinCustomAudienceRequest {
+ ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+ property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+ }
+
+ public final class LeaveCustomAudienceRequest {
+ ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public String getName();
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final String name;
+ }
+
+ public final class TrustedBiddingData {
+ ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+ method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+ method public android.net.Uri getTrustedBiddingUri();
+ property public final java.util.List<java.lang.String> trustedBiddingKeys;
+ property public final android.net.Uri trustedBiddingUri;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+ ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+ method public int getDeletionMode();
+ method public java.util.List<android.net.Uri> getDomainUris();
+ method public java.time.Instant getEnd();
+ method public int getMatchBehavior();
+ method public java.util.List<android.net.Uri> getOriginUris();
+ method public java.time.Instant getStart();
+ property public final int deletionMode;
+ property public final java.util.List<android.net.Uri> domainUris;
+ property public final java.time.Instant end;
+ property public final int matchBehavior;
+ property public final java.util.List<android.net.Uri> originUris;
+ property public final java.time.Instant start;
+ field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+ field public static final int DELETION_MODE_ALL = 0; // 0x0
+ field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+ field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+ field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+ ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+ }
+
+ public static final class DeletionRequest.Companion {
+ }
+
+ public abstract class MeasurementManager {
+ ctor public MeasurementManager();
+ method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+ method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+ field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+ field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+ }
+
+ public static final class MeasurementManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+ ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+ ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+ method public android.net.Uri? getAppDestination();
+ method public android.view.InputEvent? getInputEvent();
+ method public android.net.Uri getTopOriginUri();
+ method public android.net.Uri? getVerifiedDestination();
+ method public android.net.Uri? getWebDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+ property public final android.net.Uri? appDestination;
+ property public final android.view.InputEvent? inputEvent;
+ property public final android.net.Uri topOriginUri;
+ property public final android.net.Uri? verifiedDestination;
+ property public final android.net.Uri? webDestination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+ }
+
+ public static final class WebSourceRegistrationRequest.Builder {
+ ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+ ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+ ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+ method public android.net.Uri getDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+ property public final android.net.Uri destination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+ public final class GetTopicsRequest {
+ ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+ method public String getAdsSdkName();
+ method public boolean getShouldRecordObservation();
+ property public final String adsSdkName;
+ property public final boolean shouldRecordObservation;
+ }
+
+ public static final class GetTopicsRequest.Builder {
+ ctor public GetTopicsRequest.Builder();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+ }
+
+ public final class GetTopicsResponse {
+ ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+ }
+
+ public final class Topic {
+ ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+ method public long getModelVersion();
+ method public long getTaxonomyVersion();
+ method public int getTopicId();
+ property public final long modelVersion;
+ property public final long taxonomyVersion;
+ property public final int topicId;
+ }
+
+ public abstract class TopicsManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+ method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+ }
+
+ public static final class TopicsManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta04.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta04.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+ public final class AdId {
+ method public String getAdId();
+ method public boolean isLimitAdTrackingEnabled();
+ property public final String adId;
+ property public final boolean isLimitAdTrackingEnabled;
+ }
+
+ public abstract class AdIdManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+ method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+ }
+
+ public static final class AdIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+ public final class AdSelectionConfig {
+ ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+ method public android.net.Uri getDecisionLogicUri();
+ method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+ method public android.net.Uri getTrustedScoringSignalsUri();
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+ property public final android.net.Uri decisionLogicUri;
+ property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+ property public final android.net.Uri trustedScoringSignalsUri;
+ }
+
+ public abstract class AdSelectionManager {
+ method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+ }
+
+ public static final class AdSelectionManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ }
+
+ public final class AdSelectionOutcome {
+ ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+ method public long getAdSelectionId();
+ method public android.net.Uri getRenderUri();
+ property public final long adSelectionId;
+ property public final android.net.Uri renderUri;
+ }
+
+ public final class ReportImpressionRequest {
+ ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+ method public long getAdSelectionId();
+ property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+ property public final long adSelectionId;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+ public final class AppSetId {
+ ctor public AppSetId(String id, int scope);
+ method public String getId();
+ method public int getScope();
+ property public final String id;
+ property public final int scope;
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+ field public static final int SCOPE_APP = 1; // 0x1
+ field public static final int SCOPE_DEVELOPER = 2; // 0x2
+ }
+
+ public static final class AppSetId.Companion {
+ }
+
+ public abstract class AppSetIdManager {
+ method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+ method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+ }
+
+ public static final class AppSetIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+ public final class AdData {
+ ctor public AdData(android.net.Uri renderUri, String metadata);
+ method public String getMetadata();
+ method public android.net.Uri getRenderUri();
+ property public final String metadata;
+ property public final android.net.Uri renderUri;
+ }
+
+ public final class AdSelectionSignals {
+ ctor public AdSelectionSignals(String signals);
+ method public String getSignals();
+ property public final String signals;
+ }
+
+ public final class AdTechIdentifier {
+ ctor public AdTechIdentifier(String identifier);
+ method public String getIdentifier();
+ property public final String identifier;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+ public final class CustomAudience {
+ ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+ method public java.time.Instant? getActivationTime();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+ method public android.net.Uri getBiddingLogicUri();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public android.net.Uri getDailyUpdateUri();
+ method public java.time.Instant? getExpirationTime();
+ method public String getName();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+ property public final java.time.Instant? activationTime;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+ property public final android.net.Uri biddingLogicUri;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final android.net.Uri dailyUpdateUri;
+ property public final java.time.Instant? expirationTime;
+ property public final String name;
+ property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+ }
+
+ public static final class CustomAudience.Builder {
+ ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+ }
+
+ public abstract class CustomAudienceManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+ }
+
+ public static final class CustomAudienceManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ }
+
+ public final class JoinCustomAudienceRequest {
+ ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+ property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+ }
+
+ public final class LeaveCustomAudienceRequest {
+ ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public String getName();
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final String name;
+ }
+
+ public final class TrustedBiddingData {
+ ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+ method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+ method public android.net.Uri getTrustedBiddingUri();
+ property public final java.util.List<java.lang.String> trustedBiddingKeys;
+ property public final android.net.Uri trustedBiddingUri;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+ ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+ method public int getDeletionMode();
+ method public java.util.List<android.net.Uri> getDomainUris();
+ method public java.time.Instant getEnd();
+ method public int getMatchBehavior();
+ method public java.util.List<android.net.Uri> getOriginUris();
+ method public java.time.Instant getStart();
+ property public final int deletionMode;
+ property public final java.util.List<android.net.Uri> domainUris;
+ property public final java.time.Instant end;
+ property public final int matchBehavior;
+ property public final java.util.List<android.net.Uri> originUris;
+ property public final java.time.Instant start;
+ field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+ field public static final int DELETION_MODE_ALL = 0; // 0x0
+ field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+ field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+ field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+ ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+ }
+
+ public static final class DeletionRequest.Companion {
+ }
+
+ public abstract class MeasurementManager {
+ ctor public MeasurementManager();
+ method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+ method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+ field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+ field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+ }
+
+ public static final class MeasurementManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+ ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+ ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+ method public android.net.Uri? getAppDestination();
+ method public android.view.InputEvent? getInputEvent();
+ method public android.net.Uri getTopOriginUri();
+ method public android.net.Uri? getVerifiedDestination();
+ method public android.net.Uri? getWebDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+ property public final android.net.Uri? appDestination;
+ property public final android.view.InputEvent? inputEvent;
+ property public final android.net.Uri topOriginUri;
+ property public final android.net.Uri? verifiedDestination;
+ property public final android.net.Uri? webDestination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+ }
+
+ public static final class WebSourceRegistrationRequest.Builder {
+ ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+ ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+ ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+ method public android.net.Uri getDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+ property public final android.net.Uri destination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+ public final class GetTopicsRequest {
+ ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+ method public String getAdsSdkName();
+ method public boolean getShouldRecordObservation();
+ property public final String adsSdkName;
+ property public final boolean shouldRecordObservation;
+ }
+
+ public static final class GetTopicsRequest.Builder {
+ ctor public GetTopicsRequest.Builder();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+ }
+
+ public final class GetTopicsResponse {
+ ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+ }
+
+ public final class Topic {
+ ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+ method public long getModelVersion();
+ method public long getTaxonomyVersion();
+ method public int getTopicId();
+ property public final long modelVersion;
+ property public final long taxonomyVersion;
+ property public final int topicId;
+ }
+
+ public abstract class TopicsManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+ method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+ }
+
+ public static final class TopicsManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/res-1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/res-1.0.0-beta04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/res-1.0.0-beta04.txt
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_1.0.0-beta04.txt b/privacysandbox/ads/ads-adservices/api/restricted_1.0.0-beta04.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/restricted_1.0.0-beta04.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+ public final class AdId {
+ method public String getAdId();
+ method public boolean isLimitAdTrackingEnabled();
+ property public final String adId;
+ property public final boolean isLimitAdTrackingEnabled;
+ }
+
+ public abstract class AdIdManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+ method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+ }
+
+ public static final class AdIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+ public final class AdSelectionConfig {
+ ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+ method public android.net.Uri getDecisionLogicUri();
+ method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+ method public android.net.Uri getTrustedScoringSignalsUri();
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+ property public final android.net.Uri decisionLogicUri;
+ property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+ property public final android.net.Uri trustedScoringSignalsUri;
+ }
+
+ public abstract class AdSelectionManager {
+ method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+ }
+
+ public static final class AdSelectionManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ }
+
+ public final class AdSelectionOutcome {
+ ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+ method public long getAdSelectionId();
+ method public android.net.Uri getRenderUri();
+ property public final long adSelectionId;
+ property public final android.net.Uri renderUri;
+ }
+
+ public final class ReportImpressionRequest {
+ ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+ method public long getAdSelectionId();
+ property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+ property public final long adSelectionId;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+ public final class AppSetId {
+ ctor public AppSetId(String id, int scope);
+ method public String getId();
+ method public int getScope();
+ property public final String id;
+ property public final int scope;
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+ field public static final int SCOPE_APP = 1; // 0x1
+ field public static final int SCOPE_DEVELOPER = 2; // 0x2
+ }
+
+ public static final class AppSetId.Companion {
+ }
+
+ public abstract class AppSetIdManager {
+ method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+ method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+ }
+
+ public static final class AppSetIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+ public final class AdData {
+ ctor public AdData(android.net.Uri renderUri, String metadata);
+ method public String getMetadata();
+ method public android.net.Uri getRenderUri();
+ property public final String metadata;
+ property public final android.net.Uri renderUri;
+ }
+
+ public final class AdSelectionSignals {
+ ctor public AdSelectionSignals(String signals);
+ method public String getSignals();
+ property public final String signals;
+ }
+
+ public final class AdTechIdentifier {
+ ctor public AdTechIdentifier(String identifier);
+ method public String getIdentifier();
+ property public final String identifier;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+ public final class CustomAudience {
+ ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+ method public java.time.Instant? getActivationTime();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+ method public android.net.Uri getBiddingLogicUri();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public android.net.Uri getDailyUpdateUri();
+ method public java.time.Instant? getExpirationTime();
+ method public String getName();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+ property public final java.time.Instant? activationTime;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+ property public final android.net.Uri biddingLogicUri;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final android.net.Uri dailyUpdateUri;
+ property public final java.time.Instant? expirationTime;
+ property public final String name;
+ property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+ }
+
+ public static final class CustomAudience.Builder {
+ ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+ }
+
+ public abstract class CustomAudienceManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+ }
+
+ public static final class CustomAudienceManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ }
+
+ public final class JoinCustomAudienceRequest {
+ ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+ property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+ }
+
+ public final class LeaveCustomAudienceRequest {
+ ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public String getName();
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final String name;
+ }
+
+ public final class TrustedBiddingData {
+ ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+ method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+ method public android.net.Uri getTrustedBiddingUri();
+ property public final java.util.List<java.lang.String> trustedBiddingKeys;
+ property public final android.net.Uri trustedBiddingUri;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+ ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+ method public int getDeletionMode();
+ method public java.util.List<android.net.Uri> getDomainUris();
+ method public java.time.Instant getEnd();
+ method public int getMatchBehavior();
+ method public java.util.List<android.net.Uri> getOriginUris();
+ method public java.time.Instant getStart();
+ property public final int deletionMode;
+ property public final java.util.List<android.net.Uri> domainUris;
+ property public final java.time.Instant end;
+ property public final int matchBehavior;
+ property public final java.util.List<android.net.Uri> originUris;
+ property public final java.time.Instant start;
+ field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+ field public static final int DELETION_MODE_ALL = 0; // 0x0
+ field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+ field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+ field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+ ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+ }
+
+ public static final class DeletionRequest.Companion {
+ }
+
+ public abstract class MeasurementManager {
+ ctor public MeasurementManager();
+ method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+ method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+ field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+ field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+ }
+
+ public static final class MeasurementManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+ ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+ ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+ method public android.net.Uri? getAppDestination();
+ method public android.view.InputEvent? getInputEvent();
+ method public android.net.Uri getTopOriginUri();
+ method public android.net.Uri? getVerifiedDestination();
+ method public android.net.Uri? getWebDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+ property public final android.net.Uri? appDestination;
+ property public final android.view.InputEvent? inputEvent;
+ property public final android.net.Uri topOriginUri;
+ property public final android.net.Uri? verifiedDestination;
+ property public final android.net.Uri? webDestination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+ }
+
+ public static final class WebSourceRegistrationRequest.Builder {
+ ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+ ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+ ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+ method public android.net.Uri getDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+ property public final android.net.Uri destination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+ public final class GetTopicsRequest {
+ ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+ method public String getAdsSdkName();
+ method public boolean getShouldRecordObservation();
+ property public final String adsSdkName;
+ property public final boolean shouldRecordObservation;
+ }
+
+ public static final class GetTopicsRequest.Builder {
+ ctor public GetTopicsRequest.Builder();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+ }
+
+ public final class GetTopicsResponse {
+ ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+ }
+
+ public final class Topic {
+ ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+ method public long getModelVersion();
+ method public long getTaxonomyVersion();
+ method public int getTopicId();
+ property public final long modelVersion;
+ property public final long taxonomyVersion;
+ property public final int topicId;
+ }
+
+ public abstract class TopicsManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+ method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+ }
+
+ public static final class TopicsManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt
index 0f39bc3..c46fb02 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/SdkCodeGenerator.kt
@@ -55,6 +55,7 @@
private val sandboxApiVersion: SandboxApiVersion
) {
private val binderCodeConverter = ServerBinderCodeConverter(api)
+ private val target = GenerationTarget.SERVER
fun generate() {
if (api.services.isEmpty()) {
@@ -103,15 +104,15 @@
private fun generateStubDelegates() {
val stubDelegateGenerator = StubDelegatesGenerator(basePackageName(), binderCodeConverter)
- api.services.map { stubDelegateGenerator.generate(it, GenerationTarget.SERVER) }
+ api.services.map { stubDelegateGenerator.generate(it, target) }
.forEach(::write)
- api.interfaces.map { stubDelegateGenerator.generate(it, GenerationTarget.SERVER) }
+ api.interfaces.map { stubDelegateGenerator.generate(it, target) }
.forEach(::write)
}
private fun generateValueConverters() {
val valueConverterFileGenerator =
- ValueConverterFileGenerator(binderCodeConverter, GenerationTarget.SERVER)
+ ValueConverterFileGenerator(binderCodeConverter, target)
api.values.map(valueConverterFileGenerator::generate).forEach(::write)
api.interfaces.filter { it.inheritsSandboxedUiAdapter }.map {
CoreLibInfoAndBinderWrapperConverterGenerator.generate(it).also(::write)
@@ -120,7 +121,7 @@
private fun generateCallbackProxies() {
val clientProxyGenerator = ClientProxyTypeGenerator(basePackageName(), binderCodeConverter)
- api.callbacks.map { clientProxyGenerator.generate(it, GenerationTarget.SERVER) }
+ api.callbacks.map { clientProxyGenerator.generate(it, target) }
.forEach(::write)
}
@@ -146,7 +147,7 @@
private fun generateSuspendFunctionUtilities() {
if (!api.hasSuspendFunctions()) return
TransportCancellationGenerator(basePackageName()).generate().also(::write)
- ThrowableParcelConverterFileGenerator(basePackageName()).generate(convertToParcel = true)
+ ThrowableParcelConverterFileGenerator(basePackageName(), target).generate()
.also(::write)
}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt
index 9cd62a7..0eccb02 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/PrivacySandboxKspCompilerTest.kt
@@ -62,7 +62,7 @@
assertThat(ToolMetadata.parseFrom(resourceMap[expectedMetadataRelativePath]))
.isEqualTo(
ToolMetadata.newBuilder()
- .setCodeGenerationVersion(2)
+ .setCodeGenerationVersion(3)
.build()
)
}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index e168416..87c0cf2 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -1,5 +1,7 @@
package com.mysdk
+import kotlin.coroutines.cancellation.CancellationException
+
public object PrivacySandboxThrowableParcelConverter {
public fun toThrowableParcel(throwable: Throwable): PrivacySandboxThrowableParcel {
val parcel = PrivacySandboxThrowableParcel()
@@ -20,6 +22,7 @@
throwable.suppressedExceptions.map {
toThrowableParcel(it)
}.toTypedArray()
+ parcel.isCancellationException = throwable is CancellationException
return parcel
}
}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index e168416..87c0cf2 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -1,5 +1,7 @@
package com.mysdk
+import kotlin.coroutines.cancellation.CancellationException
+
public object PrivacySandboxThrowableParcelConverter {
public fun toThrowableParcel(throwable: Throwable): PrivacySandboxThrowableParcel {
val parcel = PrivacySandboxThrowableParcel()
@@ -20,6 +22,7 @@
throwable.suppressedExceptions.map {
toThrowableParcel(it)
}.toTypedArray()
+ parcel.isCancellationException = throwable is CancellationException
return parcel
}
}
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index e168416..87c0cf2 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -1,5 +1,7 @@
package com.mysdk
+import kotlin.coroutines.cancellation.CancellationException
+
public object PrivacySandboxThrowableParcelConverter {
public fun toThrowableParcel(throwable: Throwable): PrivacySandboxThrowableParcel {
val parcel = PrivacySandboxThrowableParcel()
@@ -20,6 +22,7 @@
throwable.suppressedExceptions.map {
toThrowableParcel(it)
}.toTypedArray()
+ parcel.isCancellationException = throwable is CancellationException
return parcel
}
}
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 0ec0a73..6ae6736 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
@@ -25,6 +25,7 @@
import androidx.privacysandbox.tools.core.generator.ClientProxyTypeGenerator
import androidx.privacysandbox.tools.core.generator.GenerationTarget
import androidx.privacysandbox.tools.core.generator.PrivacySandboxExceptionFileGenerator
+import androidx.privacysandbox.tools.core.generator.PrivacySandboxCancellationExceptionFileGenerator
import androidx.privacysandbox.tools.core.generator.ServiceFactoryFileGenerator
import androidx.privacysandbox.tools.core.generator.StubDelegatesGenerator
import androidx.privacysandbox.tools.core.generator.ThrowableParcelConverterFileGenerator
@@ -228,9 +229,9 @@
output: File
) {
if (!api.hasSuspendFunctions()) return
- ThrowableParcelConverterFileGenerator(basePackageName).generate(
- convertFromParcel = true
- ).writeTo(output)
+ ThrowableParcelConverterFileGenerator(basePackageName, GenerationTarget.CLIENT)
+ .generate().writeTo(output)
PrivacySandboxExceptionFileGenerator(basePackageName).generate().writeTo(output)
+ PrivacySandboxCancellationExceptionFileGenerator(basePackageName).generate().writeTo(output)
}
}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxCancellationException.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxCancellationException.kt
new file mode 100644
index 0000000..1d4b760
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxCancellationException.kt
@@ -0,0 +1,8 @@
+package com.sdk
+
+import java.util.concurrent.CancellationException
+
+public class PrivacySandboxCancellationException(
+ public override val message: String?,
+ public override val cause: Throwable?,
+) : CancellationException()
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt
index 73d230c..a7c0dd1 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt
@@ -5,14 +5,16 @@
public object PrivacySandboxThrowableParcelConverter {
public fun fromThrowableParcel(throwableParcel: PrivacySandboxThrowableParcel): Throwable {
val exceptionClass = throwableParcel.exceptionClass
- val errorMessage = throwableParcel.errorMessage
val stackTrace = throwableParcel.stackTrace
- val exception = PrivacySandboxException(
- "[$exceptionClass] $errorMessage",
- throwableParcel.cause?.firstOrNull()?.let {
- fromThrowableParcel(it)
- }
- )
+ val errorMessage = "[$exceptionClass] ${throwableParcel.errorMessage}"
+ val cause = throwableParcel.cause?.firstOrNull()?.let {
+ fromThrowableParcel(it)
+ }
+ val exception = if (throwableParcel.isCancellationException) {
+ PrivacySandboxCancellationException(errorMessage, cause)
+ } else {
+ PrivacySandboxException(errorMessage, cause)
+ }
for (suppressed in throwableParcel.suppressedExceptions) {
exception.addSuppressed(fromThrowableParcel(suppressed))
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxCancellationException.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxCancellationException.kt
new file mode 100644
index 0000000..84cd486
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxCancellationException.kt
@@ -0,0 +1,8 @@
+package com.mysdk
+
+import java.util.concurrent.CancellationException
+
+public class PrivacySandboxCancellationException(
+ public override val message: String?,
+ public override val cause: Throwable?,
+) : CancellationException()
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index 7797726..dddab12 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -5,14 +5,16 @@
public object PrivacySandboxThrowableParcelConverter {
public fun fromThrowableParcel(throwableParcel: PrivacySandboxThrowableParcel): Throwable {
val exceptionClass = throwableParcel.exceptionClass
- val errorMessage = throwableParcel.errorMessage
val stackTrace = throwableParcel.stackTrace
- val exception = PrivacySandboxException(
- "[$exceptionClass] $errorMessage",
- throwableParcel.cause?.firstOrNull()?.let {
- fromThrowableParcel(it)
- }
- )
+ val errorMessage = "[$exceptionClass] ${throwableParcel.errorMessage}"
+ val cause = throwableParcel.cause?.firstOrNull()?.let {
+ fromThrowableParcel(it)
+ }
+ val exception = if (throwableParcel.isCancellationException) {
+ PrivacySandboxCancellationException(errorMessage, cause)
+ } else {
+ PrivacySandboxException(errorMessage, cause)
+ }
for (suppressed in throwableParcel.suppressedExceptions) {
exception.addSuppressed(fromThrowableParcel(suppressed))
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxCancellationException.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxCancellationException.kt
new file mode 100644
index 0000000..6d764fc
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxCancellationException.kt
@@ -0,0 +1,8 @@
+package com.sdkwithvalues
+
+import java.util.concurrent.CancellationException
+
+public class PrivacySandboxCancellationException(
+ public override val message: String?,
+ public override val cause: Throwable?,
+) : CancellationException()
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt
index d86cb2b..7efbf10 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt
@@ -5,14 +5,16 @@
public object PrivacySandboxThrowableParcelConverter {
public fun fromThrowableParcel(throwableParcel: PrivacySandboxThrowableParcel): Throwable {
val exceptionClass = throwableParcel.exceptionClass
- val errorMessage = throwableParcel.errorMessage
val stackTrace = throwableParcel.stackTrace
- val exception = PrivacySandboxException(
- "[$exceptionClass] $errorMessage",
- throwableParcel.cause?.firstOrNull()?.let {
- fromThrowableParcel(it)
- }
- )
+ val errorMessage = "[$exceptionClass] ${throwableParcel.errorMessage}"
+ val cause = throwableParcel.cause?.firstOrNull()?.let {
+ fromThrowableParcel(it)
+ }
+ val exception = if (throwableParcel.isCancellationException) {
+ PrivacySandboxCancellationException(errorMessage, cause)
+ } else {
+ PrivacySandboxException(errorMessage, cause)
+ }
for (suppressed in throwableParcel.suppressedExceptions) {
exception.addSuppressed(fromThrowableParcel(suppressed))
}
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/Metadata.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/Metadata.kt
index fded100..75f5057 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/Metadata.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/Metadata.kt
@@ -24,7 +24,7 @@
/** Tool metadata message. It's serialized and stored in every SDK API descriptor. */
val toolMetadata: ToolMetadata =
ToolMetadata.newBuilder()
- .setCodeGenerationVersion(2)
+ .setCodeGenerationVersion(3)
.build()
/** Relative path to metadata file in SDK API descriptor jar. */
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 110714a..ec307d5 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
@@ -217,6 +217,7 @@
"suppressedExceptions",
AidlTypeSpec(throwableParcelType(), isList = true, kind = AidlTypeKind.PARCELABLE)
)
+ addProperty("isCancellationException", primitive("boolean"))
}
}
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/KotlinPoetSpecs.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/KotlinPoetSpecs.kt
index 39efaca..2a68daf 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/KotlinPoetSpecs.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/KotlinPoetSpecs.kt
@@ -150,6 +150,8 @@
// Kotlin coroutines
val resumeWithExceptionMethod =
MemberName("kotlin.coroutines", "resumeWithException", isExtension = true)
+ val cancellationExceptionClass =
+ ClassName("kotlin.coroutines.cancellation", "CancellationException")
// KotlinX coroutines
val coroutineScopeClass = ClassName("kotlinx.coroutines", "CoroutineScope")
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxCancellationExceptionFileGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxCancellationExceptionFileGenerator.kt
new file mode 100644
index 0000000..fe8888f
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxCancellationExceptionFileGenerator.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package androidx.privacysandbox.tools.core.generator
+
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.PropertySpec
+import com.squareup.kotlinpoet.TypeSpec
+import com.squareup.kotlinpoet.asTypeName
+import kotlin.coroutines.cancellation.CancellationException
+
+class PrivacySandboxCancellationExceptionFileGenerator(private val basePackageName: String) {
+
+ private val privacySandboxCancellationExceptionName = "PrivacySandboxCancellationException"
+
+ fun generate(): FileSpec {
+ val classSpec = TypeSpec.classBuilder(privacySandboxCancellationExceptionName).build {
+ superclass(CancellationException::class)
+ addModifiers(KModifier.PUBLIC)
+ primaryConstructor(
+ listOf(
+ PropertySpec.builder(
+ "message",
+ String::class.asTypeName().copy(nullable = true),
+ ).addModifiers(KModifier.OVERRIDE).build(),
+ PropertySpec.builder(
+ "cause",
+ Throwable::class.asTypeName().copy(nullable = true),
+ ).addModifiers(KModifier.OVERRIDE).build()
+ )
+ )
+ }
+
+ return FileSpec.builder(
+ basePackageName,
+ privacySandboxCancellationExceptionName
+ ).build {
+ addCommonSettings()
+ addType(classSpec)
+ }
+ }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt
index b45aca8..647ca84 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt
@@ -18,6 +18,7 @@
import androidx.privacysandbox.tools.core.generator.AidlGenerator.Companion.parcelableStackFrameName
import androidx.privacysandbox.tools.core.generator.AidlGenerator.Companion.throwableParcelName
+import androidx.privacysandbox.tools.core.generator.SpecNames.cancellationExceptionClass
import androidx.privacysandbox.tools.core.generator.SpecNames.stackTraceElementClass
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FileSpec
@@ -25,7 +26,10 @@
import com.squareup.kotlinpoet.MemberName
import com.squareup.kotlinpoet.TypeSpec
-class ThrowableParcelConverterFileGenerator(private val basePackageName: String) {
+class ThrowableParcelConverterFileGenerator(
+ private val basePackageName: String,
+ private val target: GenerationTarget,
+) {
companion object {
const val converterName = "${throwableParcelName}Converter"
fun toThrowableParcelNameSpec(packageName: String) = MemberName(ClassName(
@@ -43,19 +47,21 @@
private val toThrowableParcelNameSpec = toThrowableParcelNameSpec(basePackageName)
private val fromThrowableParcelNameSpec = fromThrowableParcelNameSpec(basePackageName)
- fun generate(convertToParcel: Boolean = false, convertFromParcel: Boolean = false) =
+ fun generate() =
FileSpec.builder(
basePackageName,
converterName
).build {
addCommonSettings()
- addType(generateConverter(convertToParcel, convertFromParcel))
+ addType(generateConverter())
}
- private fun generateConverter(convertToParcel: Boolean, convertFromParcel: Boolean) =
+ private fun generateConverter() =
TypeSpec.objectBuilder(ClassName(basePackageName, converterName)).build {
- if (convertToParcel) addFunction(generateToThrowableParcel())
- if (convertFromParcel) addFunction(generateFromThrowableParcel())
+ when (target) {
+ GenerationTarget.CLIENT -> addFunction(generateFromThrowableParcel())
+ GenerationTarget.SERVER -> addFunction(generateToThrowableParcel())
+ }
}
private fun generateToThrowableParcel() =
@@ -83,10 +89,12 @@
throwable.suppressedExceptions.map {
${toThrowableParcelNameSpec.simpleName}(it)
}.toTypedArray()
+ parcel.isCancellationException = throwable is %T
return parcel
""".trimIndent(),
throwableParcelNameSpec,
parcelableStackFrameNameSpec,
+ cancellationExceptionClass,
)
}
}
@@ -99,14 +107,16 @@
add(
"""
val exceptionClass = throwableParcel.exceptionClass
- val errorMessage = throwableParcel.errorMessage
val stackTrace = throwableParcel.stackTrace
- val exception = PrivacySandboxException(
- "[${'$'}exceptionClass] ${'$'}errorMessage",
- throwableParcel.cause?.firstOrNull()?.let {
- ${fromThrowableParcelNameSpec.simpleName}(it)
- }
- )
+ val errorMessage = "[${'$'}exceptionClass] ${'$'}{throwableParcel.errorMessage}"
+ val cause = throwableParcel.cause?.firstOrNull()?.let {
+ ${fromThrowableParcelNameSpec.simpleName}(it)
+ }
+ val exception = if (throwableParcel.isCancellationException) {
+ PrivacySandboxCancellationException(errorMessage, cause)
+ } else {
+ PrivacySandboxException(errorMessage, cause)
+ }
for (suppressed in throwableParcel.suppressedExceptions) {
exception.addSuppressed(${fromThrowableParcelNameSpec.simpleName}(suppressed))
}
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
index cff4771..edfc441 100644
--- a/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
@@ -8,4 +8,5 @@
PrivacySandboxThrowableParcel[] suppressedExceptions;
String errorMessage;
String exceptionClass;
+ boolean isCancellationException;
}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
index cff4771..edfc441 100644
--- a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
@@ -8,4 +8,5 @@
PrivacySandboxThrowableParcel[] suppressedExceptions;
String errorMessage;
String exceptionClass;
+ boolean isCancellationException;
}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
index cff4771..edfc441 100644
--- a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
@@ -8,4 +8,5 @@
PrivacySandboxThrowableParcel[] suppressedExceptions;
String errorMessage;
String exceptionClass;
+ boolean isCancellationException;
}
\ No newline at end of file
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
index 441d576..4e15e86 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
@@ -27,8 +27,11 @@
import android.util.Log
import android.view.ViewGroup
import android.widget.LinearLayout
+import android.widget.TextView
import androidx.annotation.RequiresExtension
import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
+import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionState
+import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionStateChangedListener
import androidx.privacysandbox.ui.client.view.SandboxedSdkView
import androidx.privacysandbox.ui.integration.testaidl.ISdkApi
@@ -75,11 +78,13 @@
val sdkApi = ISdkApi.Stub.asInterface(mSandboxedSdk.getInterface())
mSandboxedSdkView1 = findViewById<SandboxedSdkView>(R.id.rendered_view)
+ mSandboxedSdkView1.addStateChangedListener(StateChangeListener(mSandboxedSdkView1))
mSandboxedSdkView1.setAdapter(SandboxedUiAdapterFactory.createFromCoreLibInfo(
sdkApi.loadAd(/*isWebView=*/ true)
))
mSandboxedSdkView2 = SandboxedSdkView(this@MainActivity)
+ mSandboxedSdkView2.addStateChangedListener(StateChangeListener(mSandboxedSdkView2))
mSandboxedSdkView2.layoutParams = ViewGroup.LayoutParams(200, 200)
runOnUiThread(Runnable {
findViewById<LinearLayout>(R.id.ad_layout).addView(mSandboxedSdkView2)
@@ -95,6 +100,25 @@
}
}
+ private inner class StateChangeListener(val view: SandboxedSdkView) :
+ SandboxedSdkUiSessionStateChangedListener {
+ override fun onStateChanged(state: SandboxedSdkUiSessionState) {
+ Log.i(TAG, "UI session state changed to: " + state.toString())
+ if (state is SandboxedSdkUiSessionState.Error) {
+ // If the session fails to open, display the error.
+ val parent = view.parent as ViewGroup
+ val index = parent.indexOfChild(view)
+ val textView = TextView(this@MainActivity)
+ textView.setText(state.throwable.message)
+
+ runOnUiThread(Runnable {
+ parent.removeView(view)
+ parent.addView(textView, index)
+ })
+ }
+ }
+ }
+
companion object {
private const val TAG = "TestSandboxClient"
diff --git a/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt b/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt
index e616de7..2045f7c 100644
--- a/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt
+++ b/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt
@@ -24,6 +24,7 @@
import android.graphics.Paint
import android.net.Uri
import android.os.Bundle
+import android.provider.Settings
import android.util.Log
import android.view.View
import android.view.ViewGroup
@@ -44,6 +45,11 @@
return BannerAd(isWebView).toCoreLibInfo(mContext!!)
}
+ private fun isAirplaneModeOn(): Boolean {
+ return Settings.Global.getInt(
+ mContext?.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) != 0
+ }
+
private inner class BannerAd(private val isWebView: Boolean) : SandboxedUiAdapter {
override fun openSession(
context: Context,
@@ -56,6 +62,13 @@
Log.d(TAG, "Session requested")
lateinit var adView: View
if (isWebView) {
+ // To test error cases.
+ if (isAirplaneModeOn()) {
+ clientExecutor.execute {
+ client.onSessionError(Throwable("Cannot load WebView in airplane mode."))
+ }
+ return
+ }
val webView = WebView(context)
webView.loadUrl(AD_URL)
webView.layoutParams = ViewGroup.LayoutParams(
@@ -120,6 +133,6 @@
companion object {
private const val TAG = "TestSandboxSdk"
- private const val AD_URL = "http://www.google.com/"
+ private const val AD_URL = "https://www.google.com/"
}
}
diff --git a/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationTestWithProfileInstallerInitializer.kt b/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationTestWithProfileInstallerInitializer.kt
index 6cbc48f..20cff54 100644
--- a/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationTestWithProfileInstallerInitializer.kt
+++ b/profileinstaller/integration-tests/profile-verification/src/androidTest/java/androidx/profileinstaller/integration/profileverification/ProfileVerificationTestWithProfileInstallerInitializer.kt
@@ -17,7 +17,6 @@
package androidx.profileinstaller.integration.profileverification
import androidx.profileinstaller.ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE
-import androidx.profileinstaller.ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
import androidx.profileinstaller.ProfileVerifier.CompilationStatus.RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
import androidx.profileinstaller.ProfileVersion
import androidx.test.filters.LargeTest
@@ -169,14 +168,7 @@
install(apkName = APK_WITH_INITIALIZER_V3, withProfile = true)
start(ACTIVITY_NAME)
evaluateUI {
-
- // Taimen Api 28 and Cuttlefish Api 29 behave differently.
- if ((isApi29 && isCuttlefish) || (isApi28 && !isCuttlefish)) {
- profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING)
- } else {
- profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE)
- }
-
+ profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE)
hasReferenceProfile(true)
hasCurrentProfile(true)
}
@@ -231,13 +223,7 @@
install(apkName = APK_WITH_INITIALIZER_V3, withProfile = true)
start(ACTIVITY_NAME)
evaluateUI {
-
- // Taimen Api 28 and Cuttlefish Api 29 behave differently.
- if ((isApi29 && isCuttlefish) || (isApi28 && !isCuttlefish)) {
- profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING)
- } else {
- profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE)
- }
+ profileInstalled(RESULT_CODE_COMPILED_WITH_PROFILE)
hasReferenceProfile(true)
hasCurrentProfile(true)
}
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
index 2669001..8b47bcd 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
@@ -394,6 +394,7 @@
case Build.VERSION_CODES.S:
case Build.VERSION_CODES.S_V2:
case Build.VERSION_CODES.TIRAMISU:
+ case 34:
return ProfileVersion.V015_S;
default:
@@ -429,6 +430,7 @@
case Build.VERSION_CODES.S:
case Build.VERSION_CODES.S_V2:
case Build.VERSION_CODES.TIRAMISU:
+ case 34:
return true;
default:
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVersion.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVersion.java
index 650f8f9..fe44d97 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVersion.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVersion.java
@@ -33,7 +33,7 @@
static final byte[] METADATA_V001_N = new byte[]{'0', '0', '1', '\0'};
static final byte[] METADATA_V002 = new byte[]{'0', '0', '2', '\0'};
public static final int MIN_SUPPORTED_SDK = Build.VERSION_CODES.N;
- public static final int MAX_SUPPORTED_SDK = Build.VERSION_CODES.TIRAMISU;
+ public static final int MAX_SUPPORTED_SDK = 34;
static String dexKeySeparator(byte[] version) {
if (Arrays.equals(version, V001_N)) {
diff --git a/settings.gradle b/settings.gradle
index 1a50a33..ec6901c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -202,11 +202,6 @@
}
private String getRequestedProjectSubsetName() {
- def prop = providers.gradleProperty("androidx.projects")
- if (prop.isPresent()) {
- return prop.get().toUpperCase()
- }
-
def envProp = providers.environmentVariable("ANDROIDX_PROJECTS")
if (envProp.isPresent()) {
return envProp.get().toUpperCase()
@@ -214,8 +209,15 @@
return null
}
+private String getRequestedProjectPrefix() {
+ def envProp = providers.environmentVariable("PROJECT_PREFIX")
+ if (envProp.isPresent()) {
+ return envProp.get()
+ }
+ return null
+}
+
boolean isAllProjects() {
- String projectSubsetName = getRequestedProjectSubsetName()
return requestedProjectSubsetName == null || requestedProjectSubsetName == "ALL"
}
@@ -313,10 +315,12 @@
@Field Map<String, File> allProjects = new HashMap<String, File>()
// A set of projects that the user asked to filter to.
@Field Set<String> filteredProjects = new HashSet<String>()
+filteredProjects.add(":lint-checks")
@Field Pattern projectReferencePattern = Pattern.compile(
"(project|projectOrArtifact)\\((path: )?[\"'](?<name>\\S*)[\"'](, configuration: .*)?\\)"
)
+@Field Pattern multilineProjectReference = Pattern.compile("project\\(\$")
@Field Pattern inspection = Pattern.compile("packageInspector\\(project, \"(.*)\"\\)")
@Field Pattern composePlugin = Pattern.compile("id\\(\"AndroidXComposePlugin\"\\)")
@Field Pattern paparazziPlugin = Pattern.compile("id\\(\"AndroidXPaparazziPlugin\"\\)")
@@ -331,8 +335,10 @@
// the project name in the IDE
// the Maven artifactId
//
-def includeProject(name, filePath, List<BuildType> filter = []) {
- if (shouldIncludeForFilter(filter)) filteredProjects.add(name)
+def includeProject(String name, filePath, List<BuildType> filter = []) {
+ if (getRequestedProjectPrefix() != null) {
+ if (name.startsWith(getRequestedProjectPrefix())) filteredProjects.add(name)
+ } else if (shouldIncludeForFilter(filter)) filteredProjects.add(name)
def file
if (filePath != null) {
if (filePath instanceof String) {
@@ -349,12 +355,19 @@
allProjects[name] = file
File buildGradle = new File(file, "build.gradle")
if (buildGradle.exists()) {
+ def buildGradleProperty = settings.services.get(ObjectFactory).fileProperty().fileValue(buildGradle)
+ def contents = settings.providers.fileContents(buildGradleProperty).getAsText().get()
Set<String> links = new HashSet<String>()
- for (line in buildGradle.readLines()) {
+ for (line in contents.lines()) {
Matcher m = projectReferencePattern.matcher(line)
if (m.find()) {
links.add(m.group("name"))
}
+ if (multilineProjectReference.matcher(line).find()) {
+ throw new IllegalStateException(
+ "Multi-line project() references are not supported. Please fix $file.absolutePath"
+ )
+ }
Matcher matcherInspection = inspection.matcher(line)
if (matcherInspection) {
links.add(matcherInspection.group(1))
@@ -1171,6 +1184,10 @@
// Workaround for b/203825166
includeBuild("placeholder")
+// ---------------------------------------------------------------------
+// --- there should be no includeProject additions after this line -----
+// ---------------------------------------------------------------------
+
// For a given project path add the transitive project references to the include set.
void addReferences(String projectPath, Set<String> included) {
if (projectPath in included) return
@@ -1179,11 +1196,33 @@
addReferences(reference, included)
}
}
-Set<String> projectsToInclude = new HashSet<>()
-for (filteredProject in filteredProjects) {
- addReferences(filteredProject, projectsToInclude)
+
+void includeRequestedProjectsAndDependencies() {
+ // Adding by including `:foo:bar` Gradle will automatically load `:foo`, so we need to
+ // add those implicit parent project references.
+ for (projectPath in projectReferences.keySet()) {
+ Set<String> newReferences = new HashSet<>()
+ for (reference in projectReferences[projectPath]) {
+ String[] segments = reference.substring(1).split(":")
+ String subpath = ""
+ for (int i = 0; i < segments.length; i++) {
+ subpath += ":" + segments[i]
+ if (allProjects.containsKey(subpath)) {
+ newReferences.add(subpath)
+ }
+ }
+ }
+ projectReferences[projectPath].addAll(newReferences)
+ }
+
+ Set<String> projectsToInclude = new HashSet<>()
+ for (filteredProject in filteredProjects) {
+ addReferences(filteredProject, projectsToInclude)
+ }
+ for (entry in projectsToInclude) {
+ settings.include(entry)
+ project(entry).projectDir = allProjects[entry]
+ }
}
-for (entry in projectsToInclude) {
- settings.include(entry)
- project(entry).projectDir = allProjects[entry]
-}
+
+includeRequestedProjectsAndDependencies()
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlExtensionImpl.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlExtensionImpl.kt
new file mode 100644
index 0000000..fa45f3c
--- /dev/null
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlExtensionImpl.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.stableaidl
+
+import androidx.stableaidl.api.Action
+import androidx.stableaidl.api.StableAidlExtension
+import androidx.stableaidl.tasks.StableAidlCheckApi
+import androidx.stableaidl.tasks.UpdateStableAidlApiTask
+import com.android.build.api.variant.SourceDirectories
+import com.android.build.gradle.internal.tasks.factory.dependsOn
+import java.io.File
+import org.gradle.api.Task
+import org.gradle.api.tasks.TaskProvider
+
+/**
+ * Internal implementation of [StableAidlExtension] that wraps task providers.
+ */
+open class StableAidlExtensionImpl : StableAidlExtension {
+ override val checkAction: Action = object : Action {
+ override fun <T : Task> before(task: TaskProvider<T>) {
+ task.dependsOn(checkTaskProvider)
+ }
+ }
+
+ override val updateAction: Action = object : Action {
+ override fun <T : Task> before(task: TaskProvider<T>) {
+ task.dependsOn(updateTaskProvider)
+ }
+ }
+
+ override var taskGroup: String? = null
+ set(taskGroup) {
+ allTasks.forEach { (_, tasks) ->
+ tasks.forEach { task ->
+ task.configure {
+ it.group = taskGroup
+ }
+ }
+ }
+ field = taskGroup
+ }
+
+ override fun addStaticImportDirs(vararg dirs: File) {
+ importSourceDirs.forEach { importSourceDir ->
+ dirs.forEach { dir ->
+ importSourceDir.addStaticSourceDirectory(dir.absolutePath)
+ }
+ }
+ }
+
+ internal lateinit var updateTaskProvider: TaskProvider<UpdateStableAidlApiTask>
+ internal lateinit var checkTaskProvider: TaskProvider<StableAidlCheckApi>
+
+ internal val importSourceDirs = mutableListOf<SourceDirectories.Flat>()
+ internal val allTasks = mutableMapOf<String, Set<TaskProvider<*>>>()
+}
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlPlugin.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlPlugin.kt
index 91c809f..95a513e 100644
--- a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlPlugin.kt
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlPlugin.kt
@@ -16,6 +16,7 @@
package androidx.stableaidl
+import androidx.stableaidl.api.StableAidlExtension
import com.android.build.api.dsl.SdkComponents
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.DslExtension
@@ -29,6 +30,8 @@
import org.gradle.api.file.RegularFile
import org.gradle.api.provider.Provider
+private const val DEFAULT_VARIANT_NAME = "release"
+private const val EXTENSION_NAME = "stableaidl"
private const val PLUGIN_DIRNAME = "stable_aidl"
private const val GENERATED_PATH = "generated/source/$PLUGIN_DIRNAME"
private const val INTERMEDIATES_PATH = "intermediates/${PLUGIN_DIRNAME}_parcelable"
@@ -40,6 +43,11 @@
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
?: throw GradleException("Stable AIDL plugin requires Android Gradle Plugin")
+ val extension = project.extensions.create(
+ EXTENSION_NAME,
+ StableAidlExtensionImpl::class.java
+ )
+
// Obtain the AIDL executable and framework AIDL file paths using private APIs. See
// b/268237729 for public API request, after which we can obtain them from SdkComponents.
val base = project.extensions.getByType(BaseExtension::class.java)
@@ -130,7 +138,7 @@
lastReleasedApiDir,
generateAidlApiTask
)
- registerCheckAidlApi(
+ val checkAidlApiTask = registerCheckAidlApi(
project,
variant,
aidlExecutable,
@@ -141,12 +149,29 @@
generateAidlApiTask,
checkAidlApiReleaseTask
)
- registerUpdateAidlApi(
+ val updateAidlApiTask = registerUpdateAidlApi(
project,
variant,
lastCheckedInApiDir,
generateAidlApiTask
)
+
+ if (variant.name == DEFAULT_VARIANT_NAME) {
+ extension.updateTaskProvider = updateAidlApiTask
+ extension.checkTaskProvider = checkAidlApiTask
+ }
+
+ extension.importSourceDirs.add(
+ variant.sources.getByName(SOURCE_TYPE_STABLE_AIDL_IMPORTS)
+ )
+
+ extension.allTasks[variant.name] = setOf(
+ compileAidlApiTask,
+ generateAidlApiTask,
+ checkAidlApiReleaseTask,
+ checkAidlApiTask,
+ updateAidlApiTask
+ )
}
}
}
@@ -209,3 +234,19 @@
}?.artifacts?.artifactFiles
return listOfNotNull(aidlFiles, stableAidlFiles)
}
+
+/**
+ * When the Stable AIDL plugin is applies to the project, runs the specified [lambda] with access to
+ * the plugin's public APIs via [StableAidlExtension].
+ *
+ * If the project does not have the Stable AIDL plugin applied, this is a no-op.
+ */
+fun Project.withStableAidlPlugin(lambda: (StableAidlExtension) -> Unit) {
+ project.plugins.withId("androidx.stableaidl") { plugin ->
+ (plugin as? StableAidlPlugin)?.let {
+ project.extensions.findByType(StableAidlExtension::class.java)?.let { ext ->
+ lambda(ext)
+ } ?: throw GradleException("Failed to locate extension for StableAidlPlugin")
+ } ?: throw GradleException("Plugin with ID \"androidx.stableaidl\" is not StableAidlPlugin")
+ }
+}
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/api/StableAidlExtension.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/api/StableAidlExtension.kt
new file mode 100644
index 0000000..8c083ab
--- /dev/null
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/api/StableAidlExtension.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.stableaidl.api
+
+import java.io.File
+import org.gradle.api.Incubating
+import org.gradle.api.Task
+import org.gradle.api.tasks.TaskProvider
+
+/**
+ * Extension that allows access to the StableAidl plugin's public APIs.
+ */
+interface StableAidlExtension {
+ /**
+ * An action representing the task that checks the current Stable AIDL API surface for
+ * compatibility against the previously-frozen API surface.
+ */
+ @get:Incubating
+ val checkAction: Action
+
+ /**
+ * An action representing the task that updates the frozen Stable AIDL API surface.
+ */
+ @get:Incubating
+ val updateAction: Action
+
+ /**
+ * The task group to use for Stable AIDL tasks, or `null` to hide them.
+ */
+ @get:Incubating
+ var taskGroup: String?
+
+ /**
+ * Adds static import directories to be passed to Stable AIDL.
+ *
+ * Static imports may be used in `import` statements, but are not exported to dependencies.
+ */
+ fun addStaticImportDirs(vararg dirs: File)
+}
+
+interface Action {
+ /**
+ * Runs the action before the specified [task].
+ */
+ fun <T : Task> before(task: TaskProvider<T>)
+}
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/tasks/StableAidlPackageApi.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/tasks/StableAidlPackageApi.kt
index 218b300..370dd6d 100644
--- a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/tasks/StableAidlPackageApi.kt
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/tasks/StableAidlPackageApi.kt
@@ -31,10 +31,12 @@
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction
+import org.gradle.work.DisableCachingByDefault
/**
* Transforms an AAR by adding parcelable headers.
*/
+@DisableCachingByDefault(because = "Primarily filesystem operations")
abstract class StableAidlPackageApi : DefaultTask() {
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
diff --git a/test/screenshot/screenshot/build.gradle b/test/screenshot/screenshot/build.gradle
index aef8ffc..7147d25 100644
--- a/test/screenshot/screenshot/build.gradle
+++ b/test/screenshot/screenshot/build.gradle
@@ -30,10 +30,7 @@
)
dependencies {
- bundleInside(project(
- path: ":test:screenshot:screenshot-proto",
- configuration: "export"
- ))
+ bundleInside(project(path: ":test:screenshot:screenshot-proto", configuration: "export"))
implementation("androidx.annotation:annotation:1.0.0")
implementation("androidx.core:core:1.5.0-rc02")
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java
index c0384d2..c91c791 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java
@@ -102,7 +102,7 @@
// Short click with a time duration as a parameter (`click(long duration)`).
UiObject2 button4 = mDevice.findObject(By.res(TEST_APP, "button4"));
assertEquals("text4", button4.getText());
- button4.click((long) (ViewConfiguration.getLongPressTimeout() / 1.5));
+ button4.click(50L);
button4.wait(Until.textEquals("text4_clicked"), TIMEOUT_MS);
assertEquals("text4_clicked", button4.getText());
@@ -121,8 +121,7 @@
// Short click with two parameters (`click(Point point, long duration)`).
UiObject2 button6 = mDevice.findObject(By.res(TEST_APP, "button6"));
assertEquals("text6", button6.getText());
- button6.click(getPointInsideBounds(button6),
- (long) (ViewConfiguration.getLongPressTimeout() / 1.5));
+ button6.click(getPointInsideBounds(button6), 50L);
button6.wait(Until.textEquals("text6_clicked"), TIMEOUT_MS);
assertEquals("text6_clicked", button6.getText());
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/By.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/By.java
index 9c10f1f..d480917 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/By.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/By.java
@@ -328,8 +328,8 @@
* @see BySelector#hasAncestor(BySelector, int)
*/
public static @NonNull BySelector hasAncestor(@NonNull BySelector ancestorSelector,
- @IntRange(from = 1) int maxHeight) {
- return new BySelector().hasAncestor(ancestorSelector, maxHeight);
+ @IntRange(from = 1) int maxAncestorDistance) {
+ return new BySelector().hasAncestor(ancestorSelector, maxAncestorDistance);
}
/**
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/ByMatcher.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/ByMatcher.java
index f653171..9ad57c2 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/ByMatcher.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/ByMatcher.java
@@ -59,14 +59,14 @@
// Inverts parent-child relationships until a root selector is found.
private BySelector invertSelector(BySelector selector) {
- if (selector.mParentSelector == null) {
+ if (selector.mAncestorSelector == null) {
return selector;
}
- BySelector parent = new BySelector(selector.mParentSelector);
- selector.mParentSelector = null;
- selector.mMaxDepth = selector.mParentHeight;
- selector.mParentHeight = null;
- return invertSelector(parent.hasDescendant(selector));
+ BySelector ancestor = new BySelector(selector.mAncestorSelector);
+ selector.mAncestorSelector = null;
+ selector.mMaxDepth = selector.mMaxAncestorDistance;
+ selector.mMaxAncestorDistance = null;
+ return invertSelector(ancestor.hasDescendant(selector));
}
/**
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/BySelector.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/BySelector.java
index c6997db..63a0a49 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/BySelector.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/BySelector.java
@@ -51,9 +51,9 @@
Integer mMinDepth;
Integer mMaxDepth;
- // Parent selector
- BySelector mParentSelector;
- Integer mParentHeight;
+ // Ancestor selector
+ BySelector mAncestorSelector;
+ Integer mMaxAncestorDistance;
// Child selectors
final List<BySelector> mChildSelectors = new LinkedList<>();
@@ -87,9 +87,9 @@
mMinDepth = original.mMinDepth;
mMaxDepth = original.mMaxDepth;
- mParentSelector = original.mParentSelector == null ? null :
- new BySelector(original.mParentSelector);
- mParentHeight = original.mParentHeight;
+ mAncestorSelector = original.mAncestorSelector == null ? null :
+ new BySelector(original.mAncestorSelector);
+ mMaxAncestorDistance = original.mMaxAncestorDistance;
for (BySelector childSelector : original.mChildSelectors) {
mChildSelectors.add(new BySelector(childSelector));
@@ -595,10 +595,10 @@
*/
public @NonNull BySelector hasAncestor(@NonNull BySelector ancestorSelector) {
checkNotNull(ancestorSelector, "ancestorSelector cannot be null");
- if (mParentSelector != null) {
+ if (mAncestorSelector != null) {
throw new IllegalStateException("Parent/ancestor selector is already defined");
}
- mParentSelector = ancestorSelector;
+ mAncestorSelector = ancestorSelector;
return this;
}
@@ -607,14 +607,16 @@
* it has an ancestor element which matches the {@code ancestorSelector} and all other
* criteria for this selector are met.
*
- * @param ancestorSelector The selector used to find a matching ancestor element.
- * @param maxHeight The maximum height above the element to search for the ancestor.
+ * @param ancestorSelector The selector used to find a matching ancestor element.
+ * @param maxAncestorDistance The maximum distance between the element and its relevant
+ * ancestor in the view hierarchy, e.g. 1 only matches the parent
+ * element, 2 matches parent or grandparent.
* @return A reference to this {@link BySelector}.
*/
public @NonNull BySelector hasAncestor(@NonNull BySelector ancestorSelector,
- @IntRange(from = 1) int maxHeight) {
+ @IntRange(from = 1) int maxAncestorDistance) {
hasAncestor(ancestorSelector);
- mParentHeight = maxHeight;
+ mMaxAncestorDistance = maxAncestorDistance;
return this;
}
@@ -626,7 +628,7 @@
*
* @param childSelector The selector used to find a matching child element.
* @return A reference to this {@link BySelector}.
- * @throws IllegalArgumentException if the selector has a parent selector
+ * @throws IllegalArgumentException if the selector has a parent/ancestor selector
*/
public @NonNull BySelector hasChild(@NonNull BySelector childSelector) {
checkNotNull(childSelector, "childSelector cannot be null");
@@ -641,11 +643,11 @@
*
* @param descendantSelector The selector used to find a matching descendant element.
* @return A reference to this {@link BySelector}.
- * @throws IllegalArgumentException if the selector has a parent selector
+ * @throws IllegalArgumentException if the selector has a parent/ancestor selector
*/
public @NonNull BySelector hasDescendant(@NonNull BySelector descendantSelector) {
checkNotNull(descendantSelector, "descendantSelector cannot be null");
- if (descendantSelector.mParentSelector != null) {
+ if (descendantSelector.mAncestorSelector != null) {
// Search root is ambiguous with nested parent selectors.
throw new IllegalArgumentException(
"Nested parent/ancestor selectors are not supported");
@@ -663,7 +665,7 @@
* @param descendantSelector The selector used to find a matching descendant element.
* @param maxDepth The maximum depth under the element to search the descendant.
* @return A reference to this {@link BySelector}.
- * @throws IllegalArgumentException if the selector has a parent selector
+ * @throws IllegalArgumentException if the selector has a parent/ancestor selector
*/
public @NonNull BySelector hasDescendant(@NonNull BySelector descendantSelector, int maxDepth) {
hasDescendant(descendantSelector);
@@ -721,9 +723,9 @@
if (mSelected != null) {
builder.append("SELECTED='").append(mSelected).append("', ");
}
- if (mParentSelector != null) {
- builder.append("PARENT='")
- .append(mParentSelector.toString().substring(11))
+ if (mAncestorSelector != null) {
+ builder.append("ANCESTOR='")
+ .append(mAncestorSelector.toString().substring(11))
.append("', ");
}
for (BySelector childSelector : mChildSelectors) {
@@ -741,4 +743,3 @@
return value;
}
}
-
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java
index 8c8a203..1c165d3 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java
@@ -146,8 +146,8 @@
// Loop
MotionEvent event;
- for (long elapsedTime = 0; !pending.isEmpty() || !active.isEmpty();
- elapsedTime = SystemClock.uptimeMillis() - startTime) {
+ long elapsedTime = 0;
+ for (; !pending.isEmpty() || !active.isEmpty(); elapsedTime += injectionDelay) {
// Touch up any completed pointers
while (!active.isEmpty()
@@ -168,7 +168,7 @@
action = MotionEvent.ACTION_POINTER_UP
+ (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
}
- event = getMotionEvent(startTime, startTime + elapsedTime, action, properties,
+ event = getMotionEvent(startTime, SystemClock.uptimeMillis(), action, properties,
coordinates, gesture.displayId());
getDevice().getUiAutomation().injectInputEvent(event, false);
@@ -183,8 +183,9 @@
}
if (!active.isEmpty()) {
- event = getMotionEvent(startTime, startTime + elapsedTime, MotionEvent.ACTION_MOVE,
- properties, coordinates, active.peek().displayId());
+ event = getMotionEvent(startTime, SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_MOVE, properties, coordinates,
+ active.peek().displayId());
getDevice().getUiAutomation().injectInputEvent(event, false);
}
@@ -205,7 +206,7 @@
action = MotionEvent.ACTION_POINTER_DOWN
+ ((properties.size() - 1) << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
}
- event = getMotionEvent(startTime, startTime + elapsedTime, action, properties,
+ event = getMotionEvent(startTime, SystemClock.uptimeMillis(), action, properties,
coordinates, gesture.displayId());
getDevice().getUiAutomation().injectInputEvent(event, false);
@@ -215,6 +216,12 @@
SystemClock.sleep(injectionDelay);
}
+
+ long upTime = SystemClock.uptimeMillis() - startTime;
+ if (upTime >= 2 * elapsedTime) {
+ Log.w(TAG, String.format("Gestures took longer than expected (%dms >> %dms), device "
+ + "might be in a busy state.", upTime, elapsedTime));
+ }
}
/** Helper function to obtain a MotionEvent. */
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 10c61b6..7c56a46 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
@@ -18,8 +18,7 @@
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.State
+import androidx.compose.runtime.IntState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.LayoutModifier
@@ -37,11 +36,11 @@
internal class TvLazyListItemScopeImpl : TvLazyListItemScope {
- private var maxWidthState: MutableState<Int> = mutableStateOf(Int.MAX_VALUE)
- private var maxHeightState: MutableState<Int> = mutableStateOf(Int.MAX_VALUE)
+ private val maxWidthState = mutableStateOf(Int.MAX_VALUE)
+ private val maxHeightState = mutableStateOf(Int.MAX_VALUE)
fun setMaxSize(width: Int, height: Int) {
- maxWidthState.value = width
- maxHeightState.value = height
+ maxWidthState.intValue = width
+ maxHeightState.intValue = height
}
override fun Modifier.fillParentMaxSize(fraction: Float) = then(
@@ -90,21 +89,21 @@
private class ParentSizeModifier(
val fraction: Float,
inspectorInfo: InspectorInfo.() -> Unit,
- val widthState: State<Int>? = null,
- val heightState: State<Int>? = null,
+ val widthState: IntState? = null,
+ val heightState: IntState? = null,
) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
- val width = if (widthState != null && widthState.value != Constraints.Infinity) {
- (widthState.value * fraction).roundToInt()
+ val width = if (widthState != null && widthState.intValue != Constraints.Infinity) {
+ (widthState.intValue * fraction).roundToInt()
} else {
Constraints.Infinity
}
- val height = if (heightState != null && heightState.value != Constraints.Infinity) {
- (heightState.value * fraction).roundToInt()
+ val height = if (heightState != null && heightState.intValue != Constraints.Infinity) {
+ (heightState.intValue * fraction).roundToInt()
} else {
Constraints.Infinity
}
diff --git a/versionedparcelable/versionedparcelable/api/aidlRelease/current/androidx/versionedparcelable/ParcelImpl.aidl b/versionedparcelable/versionedparcelable/api/aidlRelease/current/androidx/versionedparcelable/ParcelImpl.aidl
index 5b1fea6..8d22e67 100644
--- a/versionedparcelable/versionedparcelable/api/aidlRelease/current/androidx/versionedparcelable/ParcelImpl.aidl
+++ b/versionedparcelable/versionedparcelable/api/aidlRelease/current/androidx/versionedparcelable/ParcelImpl.aidl
@@ -1,3 +1,18 @@
+/**
+ * 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.
+ */
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
diff --git a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnSnapFlingBehavior.kt b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnSnapFlingBehavior.kt
index 0d57a82..31de3b4 100644
--- a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnSnapFlingBehavior.kt
+++ b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/lazy/ScalingLazyColumnSnapFlingBehavior.kt
@@ -124,6 +124,8 @@
scrollBy(value - lastValue)
lastValue = value
}
+ // We consider all velocity consumed
+ return 0.0f
}
return animationState.velocity
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt
index 05c634e..37e074b 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt
@@ -502,9 +502,9 @@
expectedPlaceholderStage: PlaceholderStage = PlaceholderStage.ShowPlaceholder
) {
val animationLoopStart =
- (frameMillis.value.div(PLACEHOLDER_SHIMMER_GAP_BETWEEN_ANIMATION_LOOPS_MS) + 1) *
+ (frameMillis.longValue.div(PLACEHOLDER_SHIMMER_GAP_BETWEEN_ANIMATION_LOOPS_MS) + 1) *
PLACEHOLDER_SHIMMER_GAP_BETWEEN_ANIMATION_LOOPS_MS
- frameMillis.value = animationLoopStart
+ frameMillis.longValue = animationLoopStart
rule.waitForIdle()
assertThat(placeholderStage).isEqualTo(expectedPlaceholderStage)
}
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 0fd8e94..53ed85a 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
@@ -115,7 +115,7 @@
coroutineScope {
while (isActive) {
withInfiniteAnimationFrameMillis {
- frameMillis.value = it
+ frameMillis.longValue = it
}
}
}
@@ -135,7 +135,7 @@
* [PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS]
*/
internal val placeholderWipeOffProgression: Float by derivedStateOf {
- val absoluteProgression = ((frameMillis.value - startOfWipeOffAnimation).coerceAtMost(
+ val absoluteProgression = ((frameMillis.longValue - startOfWipeOffAnimation).coerceAtMost(
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS).toFloat() /
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS).coerceAtMost(1f)
val easedProgression = wipeOffInterpolator.transform(absoluteProgression)
@@ -156,7 +156,7 @@
*/
@ExperimentalWearMaterialApi
internal val placeholderWipeOffAlpha: Float by derivedStateOf {
- val absoluteProgression = ((frameMillis.value - startOfWipeOffAnimation).coerceAtMost(
+ val absoluteProgression = ((frameMillis.longValue - startOfWipeOffAnimation).coerceAtMost(
PLACEHOLDER_WIPE_OFF_PROGRESSION_ALPHA_DURATION_MS).toFloat() /
PLACEHOLDER_WIPE_OFF_PROGRESSION_ALPHA_DURATION_MS).coerceAtMost(1f)
@@ -174,8 +174,8 @@
@ExperimentalWearMaterialApi
public val placeholderProgression: Float by derivedStateOf {
val absoluteProgression =
- (frameMillis.value.mod(PLACEHOLDER_SHIMMER_GAP_BETWEEN_ANIMATION_LOOPS_MS).coerceAtMost(
- PLACEHOLDER_SHIMMER_DURATION_MS).toFloat() /
+ (frameMillis.longValue.mod(PLACEHOLDER_SHIMMER_GAP_BETWEEN_ANIMATION_LOOPS_MS)
+ .coerceAtMost(PLACEHOLDER_SHIMMER_DURATION_MS).toFloat() /
PLACEHOLDER_SHIMMER_DURATION_MS)
val easedProgression = progressionInterpolator.transform(absoluteProgression)
lerp(-maxScreenDimension * 0.5f, maxScreenDimension * 1.5f, easedProgression)
@@ -189,8 +189,8 @@
@ExperimentalWearMaterialApi
internal val placeholderShimmerAlpha: Float by derivedStateOf {
val absoluteProgression =
- (frameMillis.value.mod(PLACEHOLDER_SHIMMER_GAP_BETWEEN_ANIMATION_LOOPS_MS).coerceAtMost(
- PLACEHOLDER_SHIMMER_DURATION_MS).toFloat() /
+ (frameMillis.longValue.mod(PLACEHOLDER_SHIMMER_GAP_BETWEEN_ANIMATION_LOOPS_MS)
+ .coerceAtMost(PLACEHOLDER_SHIMMER_DURATION_MS).toFloat() /
PLACEHOLDER_SHIMMER_DURATION_MS)
if (absoluteProgression <= 0.5f) {
@@ -236,13 +236,13 @@
if (field != PlaceholderStage.ShowContent) {
// WipeOff
if (startOfWipeOffAnimation != 0L) {
- if ((frameMillis.value - startOfWipeOffAnimation) >=
+ if ((frameMillis.longValue - startOfWipeOffAnimation) >=
PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS) {
field = PlaceholderStage.ShowContent
}
// Placeholder
} else if (isContentReady.value()) {
- startOfWipeOffAnimation = frameMillis.value
+ startOfWipeOffAnimation = frameMillis.longValue
field = PlaceholderStage.WipeOff
}
}
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 207cb92..e030378 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
@@ -145,8 +145,8 @@
requireNotNull(offset) {
"The initial value must have an associated anchor."
}
- offsetState.value = offset
- absoluteOffset.value = offset
+ offsetState.floatValue = offset
+ absoluteOffset.floatValue = offset
initialOffset = offset
}
}
@@ -207,24 +207,24 @@
internal var resistance: ResistanceConfig? by mutableStateOf(null)
internal val draggableState = DraggableState {
- val newAbsolute = absoluteOffset.value + it
+ val newAbsolute = absoluteOffset.floatValue + it
val clamped = newAbsolute.coerceIn(minBound, maxBound)
val overflow = newAbsolute - clamped
val resistanceDelta = resistance?.computeResistance(overflow) ?: 0f
- offsetState.value = clamped + resistanceDelta
- overflowState.value = overflow
- absoluteOffset.value = newAbsolute
+ offsetState.floatValue = clamped + resistanceDelta
+ overflowState.floatValue = overflow
+ absoluteOffset.floatValue = newAbsolute
}
private suspend fun snapInternalToOffset(target: Float) {
draggableState.drag {
- dragBy(target - absoluteOffset.value)
+ dragBy(target - absoluteOffset.floatValue)
}
}
private suspend fun animateInternalToOffset(target: Float, spec: AnimationSpec<Float>) {
draggableState.drag {
- var prevValue = absoluteOffset.value
+ var prevValue = absoluteOffset.floatValue
animationTarget.value = target
isAnimationRunning = true
try {
@@ -342,7 +342,7 @@
}
animateInternalToOffset(targetOffset, anim)
} finally {
- val endOffset = absoluteOffset.value
+ val endOffset = absoluteOffset.floatValue
val endValue = anchors
// fighting rounding error once again, anchor should be as close as 0.5 pixels
.filterKeys { anchorOffset -> abs(anchorOffset - endOffset) < 0.5f }
@@ -401,9 +401,9 @@
* @return the amount of [delta] consumed
*/
internal fun performDrag(delta: Float): Float {
- val potentiallyConsumed = absoluteOffset.value + delta
+ val potentiallyConsumed = absoluteOffset.floatValue + delta
val clamped = potentiallyConsumed.coerceIn(minBound, maxBound)
- val deltaToConsume = clamped - absoluteOffset.value
+ val deltaToConsume = clamped - absoluteOffset.floatValue
if (abs(deltaToConsume) > 0) {
draggableState.dispatchRawDelta(deltaToConsume)
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/current.txt b/wear/protolayout/protolayout-expression-pipeline/api/current.txt
index a4ee560..712985a 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/current.txt
@@ -16,12 +16,9 @@
method public static androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest forDynamicString(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString, android.icu.util.ULocale, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.String!>);
}
- public class DynamicTypeEvaluator implements java.lang.AutoCloseable {
+ public class DynamicTypeEvaluator {
ctor public DynamicTypeEvaluator(androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config);
method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest) throws androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.EvaluationException;
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void close();
- method @UiThread public void disablePlatformDataSources();
- method @UiThread public void enablePlatformDataSources();
}
public static final class DynamicTypeEvaluator.Config {
@@ -30,7 +27,6 @@
method public androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway? getSensorGateway();
method public androidx.wear.protolayout.expression.pipeline.StateStore? getStateStore();
method public androidx.wear.protolayout.expression.pipeline.TimeGateway? getTimeGateway();
- method public boolean isPlatformDataSourcesInitiallyEnabled();
}
public static final class DynamicTypeEvaluator.Config.Builder {
@@ -38,7 +34,6 @@
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config build();
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setAnimationQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setDynamicTypesQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
- method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setPlatformDataSourcesInitiallyEnabled(boolean);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setSensorGateway(androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setStateStore(androidx.wear.protolayout.expression.pipeline.StateStore);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setTimeGateway(androidx.wear.protolayout.expression.pipeline.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 a4ee560..712985a 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
@@ -16,12 +16,9 @@
method public static androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest forDynamicString(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString, android.icu.util.ULocale, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.String!>);
}
- public class DynamicTypeEvaluator implements java.lang.AutoCloseable {
+ public class DynamicTypeEvaluator {
ctor public DynamicTypeEvaluator(androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config);
method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest) throws androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.EvaluationException;
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void close();
- method @UiThread public void disablePlatformDataSources();
- method @UiThread public void enablePlatformDataSources();
}
public static final class DynamicTypeEvaluator.Config {
@@ -30,7 +27,6 @@
method public androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway? getSensorGateway();
method public androidx.wear.protolayout.expression.pipeline.StateStore? getStateStore();
method public androidx.wear.protolayout.expression.pipeline.TimeGateway? getTimeGateway();
- method public boolean isPlatformDataSourcesInitiallyEnabled();
}
public static final class DynamicTypeEvaluator.Config.Builder {
@@ -38,7 +34,6 @@
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config build();
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setAnimationQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setDynamicTypesQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
- method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setPlatformDataSourcesInitiallyEnabled(boolean);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setSensorGateway(androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setStateStore(androidx.wear.protolayout.expression.pipeline.StateStore);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setTimeGateway(androidx.wear.protolayout.expression.pipeline.TimeGateway);
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt b/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
index 72f6bf6..f5ae829 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
@@ -16,12 +16,9 @@
method public static androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest forDynamicString(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString, android.icu.util.ULocale, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.String!>);
}
- public class DynamicTypeEvaluator implements java.lang.AutoCloseable {
+ public class DynamicTypeEvaluator {
ctor public DynamicTypeEvaluator(androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config);
method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest) throws androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.EvaluationException;
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void close();
- method @UiThread public void disablePlatformDataSources();
- method @UiThread public void enablePlatformDataSources();
}
public static final class DynamicTypeEvaluator.Config {
@@ -30,7 +27,6 @@
method public androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway? getSensorGateway();
method public androidx.wear.protolayout.expression.pipeline.StateStore? getStateStore();
method public androidx.wear.protolayout.expression.pipeline.TimeGateway? getTimeGateway();
- method public boolean isPlatformDataSourcesInitiallyEnabled();
}
public static final class DynamicTypeEvaluator.Config.Builder {
@@ -38,7 +34,6 @@
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config build();
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setAnimationQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setDynamicTypesQuotaManager(androidx.wear.protolayout.expression.pipeline.QuotaManager);
- method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setPlatformDataSourcesInitiallyEnabled(boolean);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setSensorGateway(androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setStateStore(androidx.wear.protolayout.expression.pipeline.StateStore);
method public androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator.Config.Builder setTimeGateway(androidx.wear.protolayout.expression.pipeline.TimeGateway);
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 3ece394..068ce4b 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
@@ -21,13 +21,11 @@
import android.icu.util.ULocale;
import android.os.Handler;
import android.os.Looper;
-import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
-import androidx.annotation.UiThread;
import androidx.wear.protolayout.expression.DynamicBuilders;
import androidx.wear.protolayout.expression.pipeline.BoolNodes.ComparisonFloatNode;
import androidx.wear.protolayout.expression.pipeline.BoolNodes.ComparisonInt32Node;
@@ -102,7 +100,7 @@
* <p>It's the callers responsibility to destroy those dynamic types after use, with {@link
* BoundDynamicType#close()}.
*/
-public class DynamicTypeEvaluator implements AutoCloseable {
+public class DynamicTypeEvaluator {
private static final String TAG = "DynamicTypeEvaluator";
private static final QuotaManager NO_OP_QUOTA_MANAGER =
new FixedQuotaManagerImpl(Integer.MAX_VALUE);
@@ -131,17 +129,14 @@
@NonNull private static final StateStore EMPTY_STATE_STORE = new StateStore(emptyMap());
- @NonNull private final Config mConfig;
@NonNull private final StateStore mStateStore;
@NonNull private final QuotaManager mAnimationQuotaManager;
@NonNull private final QuotaManager mDynamicTypesQuotaManager;
- @NonNull private final TimeGateway mTimeGateway;
- @Nullable private final EpochTimePlatformDataSource mTimeDataSource;
+ @NonNull private final EpochTimePlatformDataSource mTimeDataSource;
@Nullable private final SensorGatewayPlatformDataSource mSensorGatewayDataSource;
/** Configuration for creating {@link DynamicTypeEvaluator}. */
public static final class Config {
- private final boolean mPlatformDataSourcesInitiallyEnabled;
@Nullable private final StateStore mStateStore;
@Nullable private final QuotaManager mAnimationQuotaManager;
@Nullable private final TimeGateway mTimeGateway;
@@ -149,13 +144,11 @@
@Nullable private final QuotaManager mDynamicTypesQuotaManager;
Config(
- boolean platformDataSourcesInitiallyEnabled,
@Nullable StateStore stateStore,
@Nullable QuotaManager animationQuotaManager,
@Nullable QuotaManager dynamicTypesQuotaManager,
@Nullable TimeGateway timeGateway,
@Nullable SensorGateway sensorGateway) {
- this.mPlatformDataSourcesInitiallyEnabled = platformDataSourcesInitiallyEnabled;
this.mStateStore = stateStore;
this.mAnimationQuotaManager = animationQuotaManager;
this.mTimeGateway = timeGateway;
@@ -165,7 +158,6 @@
/** Builds a {@link DynamicTypeEvaluator.Config}. */
public static final class Builder {
- private boolean mPlatformDataSourcesInitiallyEnabled = false;
@Nullable private StateStore mStateStore = null;
@Nullable private QuotaManager mAnimationQuotaManager = null;
@Nullable private QuotaManager mDynamicTypesQuotaManager;
@@ -174,19 +166,6 @@
@Nullable private SensorGateway mSensorGateway = null;
/**
- * Sets whether sending updates from sensor and time sources should be allowed
- * initially. After that, enabling updates from sensor and time sources can be done via
- * {@link #enablePlatformDataSources()} or {@link #disablePlatformDataSources()}.
- *
- * <p>Defaults to {@code false}.
- */
- @NonNull
- public Builder setPlatformDataSourcesInitiallyEnabled(boolean value) {
- mPlatformDataSourcesInitiallyEnabled = value;
- return this;
- }
-
- /**
* Sets the state store that will be used for dereferencing the state keys in the
* dynamic types.
*
@@ -251,7 +230,6 @@
@NonNull
public Config build() {
return new Config(
- mPlatformDataSourcesInitiallyEnabled,
mStateStore,
mAnimationQuotaManager,
mDynamicTypesQuotaManager,
@@ -261,15 +239,6 @@
}
/**
- * Gets whether sending updates from sensor and time sources should be allowed initially.
- * After that, enabling updates from sensor and time sources can be done via {@link
- * #enablePlatformDataSources()} or {@link #disablePlatformDataSources()}.
- */
- public boolean isPlatformDataSourcesInitiallyEnabled() {
- return mPlatformDataSourcesInitiallyEnabled;
- }
-
- /**
* Gets the state store that will be used for dereferencing the state keys in the dynamic
* types, or {@code null} which is equivalent to an empty state store (state bindings will
* trigger {@link DynamicTypeValueReceiver#onInvalidated()}).
@@ -320,7 +289,6 @@
/** Constructs a {@link DynamicTypeEvaluator}. */
public DynamicTypeEvaluator(@NonNull Config config) {
- this.mConfig = config;
this.mStateStore =
config.getStateStore() != null ? config.getStateStore() : EMPTY_STATE_STORE;
this.mAnimationQuotaManager =
@@ -333,21 +301,13 @@
: NO_OP_QUOTA_MANAGER;
Handler uiHandler = new Handler(Looper.getMainLooper());
MainThreadExecutor uiExecutor = new MainThreadExecutor(uiHandler);
- this.mTimeGateway =
- config.getTimeGateway() != null
- ? config.getTimeGateway()
- : new TimeGatewayImpl(uiHandler);
- this.mTimeDataSource = new EpochTimePlatformDataSource(uiExecutor, mTimeGateway);
- if (config.isPlatformDataSourcesInitiallyEnabled()
- && this.mTimeGateway instanceof TimeGatewayImpl) {
- ((TimeGatewayImpl) this.mTimeGateway).enableUpdates();
+ TimeGateway timeGateway = config.getTimeGateway();
+ if (timeGateway == null) {
+ timeGateway = new TimeGatewayImpl(uiHandler);
+ ((TimeGatewayImpl) timeGateway).enableUpdates();
}
+ this.mTimeDataSource = new EpochTimePlatformDataSource(uiExecutor, timeGateway);
if (config.getSensorGateway() != null) {
- if (config.isPlatformDataSourcesInitiallyEnabled()) {
- config.getSensorGateway().enableUpdates();
- } else {
- config.getSensorGateway().disableUpdates();
- }
this.mSensorGatewayDataSource =
new SensorGatewayPlatformDataSource(uiExecutor, config.getSensorGateway());
} else {
@@ -1102,46 +1062,6 @@
resultBuilder.add(node);
}
- /** Enables sending updates on sensor and time. */
- @UiThread
- public void enablePlatformDataSources() {
- if (this.mTimeGateway instanceof TimeGatewayImpl) {
- ((TimeGatewayImpl) mTimeGateway).enableUpdates();
- }
- if (mConfig.getSensorGateway() != null) {
- mConfig.getSensorGateway().enableUpdates();
- }
- }
-
- /** Disables sending updates on sensor and time. */
- @UiThread
- public void disablePlatformDataSources() {
- if (this.mTimeGateway instanceof TimeGatewayImpl) {
- ((TimeGatewayImpl) mTimeGateway).disableUpdates();
- }
- if (mConfig.getSensorGateway() != null) {
- mConfig.getSensorGateway().disableUpdates();
- }
- }
-
- /**
- * Closes resources owned by this {@link DynamicTypeEvaluator}.
- *
- * <p>This will not close provided resources, like the {@link TimeGateway} or {@link
- * SensorGateway}.
- */
- @RestrictTo(Scope.LIBRARY_GROUP)
- @Override
- public void close() {
- if (mTimeGateway instanceof TimeGatewayImpl) {
- try {
- ((TimeGatewayImpl) mTimeGateway).close();
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error while cleaning up time gateway", ex);
- }
- }
- }
-
/**
* Wraps {@link DynamicTypeValueReceiver} and executes its methods on the given {@link
* Executor}.
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FixedQuotaManagerImpl.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FixedQuotaManagerImpl.java
index 6a3f1d3..dbe00c9 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FixedQuotaManagerImpl.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FixedQuotaManagerImpl.java
@@ -60,15 +60,13 @@
}
/** Returns true if all quota has been released. */
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- @RestrictTo(Scope.TESTS)
+ @VisibleForTesting
public boolean isAllQuotaReleased() {
return mQuotaCounter == 0;
}
/** Returns the remaining quota. */
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- @RestrictTo(Scope.TESTS)
+ @VisibleForTesting
public int getRemainingQuota() {
return mQuotaCap - mQuotaCounter;
}
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
index e30b7c5..67b1243 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
@@ -92,7 +92,6 @@
StateStore stateStore = new StateStore(new HashMap<>());
return new DynamicTypeEvaluator(
new DynamicTypeEvaluator.Config.Builder()
- .setPlatformDataSourcesInitiallyEnabled(true)
.setStateStore(stateStore)
.setAnimationQuotaManager(animationQuota)
.setDynamicTypesQuotaManager(dynamicTypesQuota)
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 86a002e..7632f30 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
@@ -326,7 +326,6 @@
DynamicTypeEvaluator evaluator =
new DynamicTypeEvaluator(
new DynamicTypeEvaluator.Config.Builder()
- .setPlatformDataSourcesInitiallyEnabled(true)
.setStateStore(stateStore)
.setAnimationQuotaManager(new FixedQuotaManagerImpl(MAX_VALUE))
.build());
@@ -491,6 +490,7 @@
}
@Override
+ @NonNull
public String toString() {
return mName + " = " + mExpectedValue;
}
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
index ba47d07..be3cff9 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
@@ -24,6 +24,8 @@
import android.annotation.SuppressLint;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.icu.util.ULocale;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -46,6 +48,7 @@
import androidx.wear.protolayout.expression.pipeline.FixedQuotaManagerImpl;
import androidx.wear.protolayout.expression.pipeline.QuotaManager;
import androidx.wear.protolayout.expression.pipeline.StateStore;
+import androidx.wear.protolayout.expression.pipeline.TimeGatewayImpl;
import androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway;
import androidx.wear.protolayout.expression.proto.DynamicProto.DynamicBool;
import androidx.wear.protolayout.expression.proto.DynamicProto.DynamicColor;
@@ -92,16 +95,15 @@
boolean mFullyVisible;
@NonNull final QuotaManager mAnimationQuotaManager;
@NonNull private final DynamicTypeEvaluator mEvaluator;
+ @NonNull private final TimeGatewayImpl mTimeGateway;
/** Creates a {@link ProtoLayoutDynamicDataPipeline} without animation support. */
@RestrictTo(Scope.LIBRARY_GROUP)
public ProtoLayoutDynamicDataPipeline(
- boolean canUpdateGateways,
@Nullable SensorGateway sensorGateway,
@NonNull StateStore stateStore) {
// Build pipeline with quota that doesn't allow any animations.
this(
- canUpdateGateways,
sensorGateway,
stateStore,
/* enableAnimations= */ false,
@@ -115,13 +117,11 @@
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public ProtoLayoutDynamicDataPipeline(
- boolean canUpdateGateways,
@Nullable SensorGateway sensorGateway,
@NonNull StateStore stateStore,
@NonNull QuotaManager animationQuotaManager,
@NonNull QuotaManager dynamicNodesQuotaManager) {
this(
- canUpdateGateways,
sensorGateway,
stateStore,
/* enableAnimations= */ true,
@@ -131,7 +131,6 @@
/** Creates a {@link ProtoLayoutDynamicDataPipeline}. */
private ProtoLayoutDynamicDataPipeline(
- boolean canUpdateGateways,
@Nullable SensorGateway sensorGateway,
@NonNull StateStore stateStore,
boolean enableAnimations,
@@ -139,9 +138,10 @@
@NonNull QuotaManager dynamicNodeQuotaManager) {
this.mEnableAnimations = enableAnimations;
this.mAnimationQuotaManager = animationQuotaManager;
+ this.mTimeGateway = new TimeGatewayImpl(new Handler(Looper.getMainLooper()));
DynamicTypeEvaluator.Config.Builder evaluatorConfigBuilder =
new DynamicTypeEvaluator.Config.Builder()
- .setPlatformDataSourcesInitiallyEnabled(canUpdateGateways)
+ .setTimeGateway(mTimeGateway)
.setStateStore(stateStore);
evaluatorConfigBuilder.setDynamicTypesQuotaManager(dynamicNodeQuotaManager);
if (sensorGateway != null) {
@@ -150,12 +150,12 @@
if (enableAnimations) {
evaluatorConfigBuilder.setAnimationQuotaManager(animationQuotaManager);
}
- this.mEvaluator = new DynamicTypeEvaluator(evaluatorConfigBuilder.build());
+ DynamicTypeEvaluator.Config evaluatorConfig = evaluatorConfigBuilder.build();
+ this.mEvaluator = new DynamicTypeEvaluator(evaluatorConfig);
}
/** Returns the number of active dynamic types in this pipeline. */
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- @RestrictTo(Scope.TESTS)
+ @VisibleForTesting
public int size() {
return mPositionIdTree.getAllNodes().stream().mapToInt(NodeInfo::size).sum();
}
@@ -186,7 +186,6 @@
*/
@VisibleForTesting
@NonNull
- @RestrictTo(Scope.TESTS)
public PipelineMaker newPipelineMaker() {
return newPipelineMaker(
(enterTransition, view) -> new AnimationSet(/* shareInterpolator= */ false),
@@ -201,10 +200,12 @@
@SuppressWarnings("RestrictTo")
@RestrictTo(Scope.LIBRARY_GROUP)
public void setUpdatesEnabled(boolean canUpdate) {
+ // SensorGateway is not owned by ProtoLayoutDynamicDataPipeline, so the callers who create
+ // it are responsible for updates.
if (canUpdate) {
- mEvaluator.enablePlatformDataSources();
+ mTimeGateway.enableUpdates();
} else {
- mEvaluator.disablePlatformDataSources();
+ mTimeGateway.disableUpdates();
}
}
@@ -212,7 +213,8 @@
@RestrictTo(Scope.LIBRARY_GROUP)
@SuppressWarnings("RestrictTo")
public void close() {
- mEvaluator.close();
+ mPositionIdTree.clear();
+ mTimeGateway.close();
}
/**
@@ -1062,8 +1064,7 @@
}
/** Returns how many animations are running. */
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- @RestrictTo(Scope.TESTS)
+ @VisibleForTesting
public int getRunningAnimationsCount() {
return mPositionIdTree.getAllNodes().stream()
.mapToInt(NodeInfo::getRunningAnimationCount)
@@ -1077,8 +1078,7 @@
}
/** Returns How many dynamic data nodes exist in the pipeline. */
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- @RestrictTo(Scope.TESTS)
+ @VisibleForTesting
public int getDynamicExpressionsNodesCount() {
return mPositionIdTree.getAllNodes().stream()
.mapToInt(NodeInfo::getExpressionNodesCount)
@@ -1086,8 +1086,7 @@
}
/** Returns whether all quota has been released. */
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- @RestrictTo(Scope.TESTS)
+ @VisibleForTesting
public boolean isAllQuotaReleased() {
return mAnimationQuotaManager instanceof FixedQuotaManagerImpl
&& ((FixedQuotaManagerImpl) mAnimationQuotaManager).isAllQuotaReleased();
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java
index 19fafca..b496675 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java
@@ -646,17 +646,15 @@
StateStore stateStore = config.getStateStore();
if (stateStore != null) {
- boolean updatesEnabled = config.getUpdatesEnabled();
mDataPipeline =
config.getAnimationEnabled()
? new ProtoLayoutDynamicDataPipeline(
- updatesEnabled,
- config.getSensorGateway(),
+ config.getSensorGateway(),
stateStore,
new FixedQuotaManagerImpl(config.getRunningAnimationsLimit()),
new FixedQuotaManagerImpl(DYNAMIC_NODES_MAX_COUNT))
: new ProtoLayoutDynamicDataPipeline(
- updatesEnabled, config.getSensorGateway(), stateStore);
+ config.getSensorGateway(), stateStore);
mDataPipeline.setFullyVisible(config.getIsViewFullyVisible());
} else {
mDataPipeline = 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 c93a489..f8c0a3d 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
@@ -377,7 +377,6 @@
.build();
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -444,7 +443,6 @@
DynamicInt32 dynamicInt = fixedDynamicInt32(1);
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -465,7 +463,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -496,7 +493,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -525,7 +521,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -582,7 +577,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -621,7 +615,6 @@
List<String> expected = Arrays.asList(NODE_1_1, NODE_1_1_1);
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -643,7 +636,6 @@
public void resolvedAnimatedImage_canStorePlayAndResetOnVisible() {
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -677,7 +669,6 @@
public void resolvedAnimatedImage_canStoreAndPlayOnVisibleOnce() {
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -709,7 +700,6 @@
public void resolvedAnimatedImage_canStorePlayAndResetOnLoad() {
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -738,7 +728,6 @@
String boolStateKey = "KEY";
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -776,7 +765,6 @@
String boolStateKey = "KEY";
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -797,7 +785,6 @@
String boolStateKey = "KEY";
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -820,7 +807,6 @@
FixedQuotaManagerImpl quotaManager = new FixedQuotaManagerImpl(MAX_VALUE);
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
quotaManager,
@@ -854,7 +840,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -892,7 +877,6 @@
FixedQuotaManagerImpl quotaManager = new FixedQuotaManagerImpl(/* quotaCap= */ 0);
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -920,7 +904,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -958,7 +941,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1008,7 +990,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1040,7 +1021,6 @@
FixedQuotaManagerImpl quotaManager = new FixedQuotaManagerImpl(/* quotaCap= */ 0);
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
quotaManager,
@@ -1125,7 +1105,6 @@
public void resolvedSeekableAnimatedImage_canStoreAndRegisterWithAnimatableFixedFloat() {
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1158,7 +1137,6 @@
public void resolvedSeekableAnimatedImage_canStoreAndRegisterWithAnimatableDynamicFloat() {
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1204,7 +1182,6 @@
public void resolvedSeekableAnimatedImage_getSeekableAnimationTotalDurationMillis() {
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1228,7 +1205,6 @@
public void whenInvisible_pausesAvds() {
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1264,7 +1240,6 @@
public void visibilityChange_avdsStatusChange() {
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
@@ -1377,7 +1352,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
quotaManager,
@@ -1431,7 +1405,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
quotaManager,
@@ -1488,7 +1461,6 @@
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
quotaManager,
@@ -1546,7 +1518,6 @@
FixedQuotaManagerImpl quotaManager = new FixedQuotaManagerImpl(1);
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
quotaManager,
@@ -1798,7 +1769,6 @@
Trigger.newBuilder().setOnLoadTrigger(OnLoadTrigger.getDefaultInstance()).build();
ProtoLayoutDynamicDataPipeline pipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore,
quotaManager,
@@ -1843,13 +1813,11 @@
ProtoLayoutDynamicDataPipeline pipeline =
enableAnimations
? new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
- /* sensorGateway= */ null,
+ /* sensorGateway= */ null,
mStateStore,
new FixedQuotaManagerImpl(MAX_VALUE),
new FixedQuotaManagerImpl(MAX_VALUE))
: new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
/* sensorGateway= */ null,
mStateStore);
shadowOf(getMainLooper()).idle();
@@ -1874,8 +1842,7 @@
AddToListCallback<Float> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true, /* sensorGateway= */ null, mStateStore);
+ new ProtoLayoutDynamicDataPipeline( /* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
@@ -1893,8 +1860,7 @@
AddToListCallback<Integer> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true, /* sensorGateway= */ null, mStateStore);
+ new ProtoLayoutDynamicDataPipeline( /* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
@@ -1912,8 +1878,7 @@
AddToListCallback<Float> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true, /* sensorGateway= */ null, mStateStore);
+ new ProtoLayoutDynamicDataPipeline( /* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
@@ -1931,8 +1896,7 @@
AddToListCallback<Float> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true, /* sensorGateway= */ null, mStateStore);
+ new ProtoLayoutDynamicDataPipeline( /* sensorGateway= */ null, mStateStore);
shadowOf(getMainLooper()).idle();
pipeline.setFullyVisible(true);
@@ -1950,8 +1914,7 @@
AddToListCallback<Integer> receiver =
new AddToListCallback<>(results, /* invalidList= */ null);
ProtoLayoutDynamicDataPipeline pipeline =
- new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true, /* 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 4a49fcd..9e263bf 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
@@ -3378,8 +3378,7 @@
FixedQuotaManagerImpl quotaManager) {
mDataPipeline =
new ProtoLayoutDynamicDataPipeline(
- /* canUpdateGateways= */ true,
- null,
+ /* sensorGateway= */ null,
mStateStore,
quotaManager,
new FixedQuotaManagerImpl(MAX_VALUE));
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 26dc1e9..daae481 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
@@ -35,7 +35,6 @@
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
import androidx.wear.watchface.complications.data.ComplicationData
-import androidx.wear.watchface.complications.data.ComplicationDataExpressionEvaluator.Companion.hasExpression
import androidx.wear.watchface.complications.data.ComplicationType
import androidx.wear.watchface.complications.data.ComplicationType.Companion.fromWireType
import androidx.wear.watchface.complications.data.GoalProgressComplicationData
@@ -616,7 +615,7 @@
require(complicationData.validTimeRange == TimeRange.ALWAYS) {
"Preview data should have time range set to ALWAYS."
}
- require(!hasExpression(complicationData.asWireComplicationData())) {
+ require(!complicationData.asWireComplicationData().hasExpression()) {
"Preview data must not have expressions."
}
}
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
index 5463ef96..5423cf2 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
@@ -150,7 +150,8 @@
+ "validTimeRange=TimeRange("
+ "startDateTimeMillis=-1000000000-01-01T00:00:00Z,"
+ " endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z),"
- + " dataSource=null, persistencePolicy=0, displayPolicy=0),"
+ + " dataSource=null, persistencePolicy=0, displayPolicy=0, "
+ + "fallback=null),"
+ " timelineEntries=["
+ "TimelineEntry(validity=TimeInterval(start=1970-01-02T03:46:40Z,"
+ " end=1970-01-03T07:33:20Z),"
@@ -164,7 +165,8 @@
+ "validTimeRange=TimeRange("
+ "startDateTimeMillis=-1000000000-01-01T00:00:00Z,"
+ " endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z),"
- + " dataSource=null, persistencePolicy=0, displayPolicy=0))])");
+ + " dataSource=null, persistencePolicy=0, displayPolicy=0, "
+ + "fallback=null))])");
}
@Test
diff --git a/wear/watchface/watchface-complications-data/api/current.ignore b/wear/watchface/watchface-complications-data/api/current.ignore
index 123515f..aa5a43b 100644
--- a/wear/watchface/watchface-complications-data/api/current.ignore
+++ b/wear/watchface/watchface-complications-data/api/current.ignore
@@ -1,4 +1,20 @@
// Baseline format: 1.0
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.LongTextComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder to BuilderT
+
+
RemovedClass: androidx.wear.watchface.complications.data.DataKt:
Removed class androidx.wear.watchface.complications.data.DataKt
RemovedClass: androidx.wear.watchface.complications.data.ImageKt:
diff --git a/wear/watchface/watchface-complications-data/api/current.txt b/wear/watchface/watchface-complications-data/api/current.txt
index f36afa5..2992e08 100644
--- a/wear/watchface/watchface-complications-data/api/current.txt
+++ b/wear/watchface/watchface-complications-data/api/current.txt
@@ -113,10 +113,10 @@
ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -141,10 +141,10 @@
public static final class LongTextComplicationData.Builder {
ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -176,9 +176,9 @@
public static final class MonochromaticImageComplicationData.Builder {
ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
}
@@ -208,10 +208,10 @@
public static final class NoPermissionComplicationData.Builder {
ctor public NoPermissionComplicationData.Builder();
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -234,9 +234,9 @@
public static final class PhotoImageComplicationData.Builder {
ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
}
@@ -285,10 +285,10 @@
ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -315,10 +315,10 @@
public static final class ShortTextComplicationData.Builder {
ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -352,9 +352,9 @@
public static final class SmallImageComplicationData.Builder {
ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
}
@@ -459,11 +459,11 @@
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class WeightedElementsComplicationData.Builder {
ctor public WeightedElementsComplicationData.Builder(java.util.List<androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Element> elements, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
diff --git a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
index 520f8dc..e6efd13 100644
--- a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
@@ -116,10 +116,10 @@
ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -144,10 +144,10 @@
public static final class LongTextComplicationData.Builder {
ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -179,9 +179,9 @@
public static final class MonochromaticImageComplicationData.Builder {
ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
}
@@ -211,10 +211,10 @@
public static final class NoPermissionComplicationData.Builder {
ctor public NoPermissionComplicationData.Builder();
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -237,9 +237,9 @@
public static final class PhotoImageComplicationData.Builder {
ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
}
@@ -288,10 +288,10 @@
ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -318,10 +318,10 @@
public static final class ShortTextComplicationData.Builder {
ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -355,9 +355,9 @@
public static final class SmallImageComplicationData.Builder {
ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
}
@@ -462,11 +462,11 @@
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class WeightedElementsComplicationData.Builder {
ctor public WeightedElementsComplicationData.Builder(java.util.List<androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Element> elements, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.ignore b/wear/watchface/watchface-complications-data/api/restricted_current.ignore
index d3ed8b1..fa0dab0 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.ignore
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.ignore
@@ -1,4 +1,20 @@
// Baseline format: 1.0
+ChangedType: androidx.wear.watchface.complications.data.LongTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.LongTextComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.LongTextComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder to BuilderT
+ChangedType: androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder#setDataSource(android.content.ComponentName):
+ Method androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder.setDataSource has changed return type from androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder to BuilderT
+
+
RemovedClass: androidx.wear.watchface.complications.data.DataKt:
Removed class androidx.wear.watchface.complications.data.DataKt
RemovedClass: androidx.wear.watchface.complications.data.DefaultComplicationDataSourcePolicyWireFormat:
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.txt b/wear/watchface/watchface-complications-data/api/restricted_current.txt
index a60802c..7a434b5 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.txt
@@ -113,10 +113,10 @@
ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -141,10 +141,10 @@
public static final class LongTextComplicationData.Builder {
ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -176,9 +176,9 @@
public static final class MonochromaticImageComplicationData.Builder {
ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
}
@@ -208,10 +208,10 @@
public static final class NoPermissionComplicationData.Builder {
ctor public NoPermissionComplicationData.Builder();
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -234,9 +234,9 @@
public static final class PhotoImageComplicationData.Builder {
ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
}
@@ -286,10 +286,10 @@
ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -316,10 +316,10 @@
public static final class ShortTextComplicationData.Builder {
ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -353,9 +353,9 @@
public static final class SmallImageComplicationData.Builder {
ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
}
@@ -462,11 +462,11 @@
@RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class WeightedElementsComplicationData.Builder {
ctor public WeightedElementsComplicationData.Builder(java.util.List<androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Element> elements, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData build();
- method public final T setDataSource(android.content.ComponentName? dataSource);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setDisplayPolicy(int displayPolicy);
+ method public final BuilderT setDataSource(android.content.ComponentName? dataSource);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setDisplayPolicy(int displayPolicy);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
- method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final T setPersistencePolicy(int persistencePolicy);
+ method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final BuilderT setPersistencePolicy(int persistencePolicy);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
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 b122f85..4bdd46d 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
@@ -1115,6 +1115,15 @@
val endDateTimeMillis: Long
get() = fields.getLong(FIELD_END_TIME, Long.MAX_VALUE)
+ /** Returns `true` if the complication contains an expression that needs to be evaluated. */
+ fun hasExpression(): Boolean =
+ (hasRangedValueExpression() && rangedValueExpression != null) ||
+ (hasLongText() && longText?.expression != null) ||
+ (hasLongTitle() && longTitle?.expression != null) ||
+ (hasShortText() && shortText?.expression != null) ||
+ (hasShortTitle() && shortTitle?.expression != null) ||
+ (hasContentDescription() && contentDescription?.expression != null)
+
/**
* Returns true if the complication data contains at least one text field with a value that may
* change based on the current time.
@@ -2230,6 +2239,9 @@
FIELD_END_TIME,
FIELD_TIMELINE_ENTRIES,
FIELD_TIMELINE_ENTRY_TYPE,
+ // Placeholder or fallback.
+ FIELD_PLACEHOLDER_FIELDS,
+ FIELD_PLACEHOLDER_TYPE,
)
// Used for validation. OPTIONAL_FIELDS[i] is a list containing all the fields which are
@@ -2313,8 +2325,6 @@
FIELD_LONG_TEXT,
FIELD_MAX_VALUE,
FIELD_MIN_VALUE,
- FIELD_PLACEHOLDER_FIELDS,
- FIELD_PLACEHOLDER_TYPE,
FIELD_SMALL_IMAGE,
FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
FIELD_SHORT_TITLE,
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 d6589c5..89fbc08 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
@@ -216,7 +216,6 @@
evaluator =
DynamicTypeEvaluator(
DynamicTypeEvaluator.Config.Builder()
- .setPlatformDataSourcesInitiallyEnabled(true)
.apply { stateStore?.let { setStateStore(it) } }
.apply { timeGateway?.let { setTimeGateway(it) } }
.apply { sensorGateway?.let { setSensorGateway(it) } }
@@ -243,7 +242,6 @@
for (receiver in pendingReceivers + invalidReceivers + completeReceivers) {
receiver.close()
}
- if (this@State::evaluator.isInitialized) evaluator.close()
}
}
@@ -304,16 +302,6 @@
companion object {
val INVALID_DATA: WireComplicationData = NoDataComplicationData().asWireComplicationData()
-
- fun hasExpression(data: WireComplicationData): Boolean =
- data.run {
- (hasRangedValueExpression() && rangedValueExpression != null) ||
- (hasLongText() && longText?.expression != null) ||
- (hasLongTitle() && longTitle?.expression != null) ||
- (hasShortText() && shortText?.expression != null) ||
- (hasShortTitle() && shortTitle?.expression != null) ||
- (hasContentDescription() && contentDescription?.expression != null)
- }
}
}
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 42fe7ce..e34d53c 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
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:OptIn(ComplicationExperimental::class)
-
package androidx.wear.watchface.complications.data
import android.app.PendingIntent
@@ -115,7 +113,8 @@
public val validTimeRange: TimeRange = TimeRange.ALWAYS,
public val dataSource: ComponentName?,
@ComplicationPersistencePolicy public val persistencePolicy: Int,
- @ComplicationDisplayPolicy public val displayPolicy: Int
+ @ComplicationDisplayPolicy public val displayPolicy: Int,
+ private val fallback: ComplicationData?,
) {
/**
* [tapAction] which is a [PendingIntent] unfortunately can't be serialized. This property is
@@ -132,7 +131,6 @@
* Converts this value to [WireComplicationData] object used for serialization.
*
* This is only needed internally to convert to the underlying communication protocol.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun asWireComplicationData(): WireComplicationData {
@@ -153,6 +151,13 @@
builder.setDataSource(dataSource)
builder.setPersistencePolicy(persistencePolicy)
builder.setDisplayPolicy(displayPolicy)
+ if (fallback == null) {
+ builder.setPlaceholder(null)
+ } else {
+ val placeholderBuilder = fallback.createWireComplicationDataBuilder()
+ fallback.fillWireComplicationDataBuilder(placeholderBuilder)
+ builder.setPlaceholder(placeholderBuilder.build())
+ }
}
/**
@@ -186,24 +191,14 @@
override fun hashCode(): Int = asWireComplicationData().hashCode()
- /**
- * Builder for properties in common for most Complication Types.
- *
- */
+ /** Builder for properties in common for most Complication Types. */
@RestrictTo(RestrictTo.Scope.LIBRARY)
- public abstract class BaseBuilder<T : BaseBuilder<T, ReturnT>, ReturnT> {
+ public sealed class BaseBuilder<BuilderT : BaseBuilder<BuilderT, BuiltT>, BuiltT> {
internal var cachedWireComplicationData: WireComplicationData? = null
internal var dataSource: ComponentName? = null
internal var persistencePolicy = ComplicationPersistencePolicies.CACHING_ALLOWED
internal var displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY
-
- @Suppress("NewApi")
- internal fun setCommon(data: WireComplicationData) = apply {
- setCachedWireComplicationData(data)
- setDataSource(data.dataSource)
- setPersistencePolicy(data.persistencePolicy)
- setDisplayPolicy(data.displayPolicy)
- }
+ internal var fallback: BuiltT? = null
/**
* Sets the [ComponentName] of the ComplicationDataSourceService that provided this
@@ -213,37 +208,48 @@
* set this value on its behalf.
*/
@Suppress("UNCHECKED_CAST", "SetterReturnsThis")
- public fun setDataSource(dataSource: ComponentName?): T {
+ public fun setDataSource(dataSource: ComponentName?): BuilderT {
this.dataSource = dataSource
- return this as T
+ return this as BuilderT
}
@Suppress("UNCHECKED_CAST", "SetterReturnsThis")
internal fun setCachedWireComplicationData(
cachedWireComplicationData: WireComplicationData?
- ): T {
+ ): BuilderT {
this.cachedWireComplicationData = cachedWireComplicationData
- return this as T
+ return this as BuilderT
}
/** Sets the complication's [ComplicationPersistencePolicy]. */
@Suppress("UNCHECKED_CAST", "SetterReturnsThis")
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public fun setPersistencePolicy(@ComplicationPersistencePolicy persistencePolicy: Int): T {
+ public fun setPersistencePolicy(
+ @ComplicationPersistencePolicy persistencePolicy: Int
+ ): BuilderT {
this.persistencePolicy = persistencePolicy
- return this as T
+ return this as BuilderT
}
/** Sets the complication's [ComplicationDisplayPolicy]. */
@Suppress("UNCHECKED_CAST", "SetterReturnsThis")
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public fun setDisplayPolicy(@ComplicationDisplayPolicy displayPolicy: Int): T {
+ public fun setDisplayPolicy(@ComplicationDisplayPolicy displayPolicy: Int): BuilderT {
this.displayPolicy = displayPolicy
- return this as T
+ return this as BuilderT
+ }
+
+ /** Sets the complication's fallback, use in case any expression has been invalidated. */
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public fun setFallback(fallback: BuiltT?): BuilderT {
+ this.fallback = fallback
+ return this as BuilderT
}
/** Builds the ComplicationData */
- abstract fun build(): ReturnT
+ abstract fun build(): BuiltT
}
}
@@ -277,7 +283,8 @@
dataSource = null,
persistencePolicy = placeholder?.persistencePolicy
?: ComplicationPersistencePolicies.CACHING_ALLOWED,
- displayPolicy = placeholder?.displayPolicy ?: ComplicationDisplayPolicies.ALWAYS_DISPLAY
+ displayPolicy = placeholder?.displayPolicy ?: ComplicationDisplayPolicies.ALWAYS_DISPLAY,
+ fallback = placeholder,
) {
/** Constructs a NoDataComplicationData without a [placeholder]. */
@@ -303,17 +310,6 @@
else -> null
}
- override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {
- super.fillWireComplicationDataBuilder(builder)
- if (placeholder == null) {
- builder.setPlaceholder(null)
- } else {
- val placeholderBuilder = placeholder.createWireComplicationDataBuilder()
- placeholder.fillWireComplicationDataBuilder(placeholderBuilder)
- builder.setPlaceholder(placeholderBuilder.build())
- }
- }
-
override fun toString(): String {
return "NoDataComplicationData(" +
"placeholder=$placeholder, " +
@@ -341,7 +337,8 @@
cachedWireComplicationData = null,
dataSource = null,
persistencePolicy = ComplicationPersistencePolicies.CACHING_ALLOWED,
- displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY
+ displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY,
+ fallback = null,
) {
// Always empty.
override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {}
@@ -370,7 +367,8 @@
cachedWireComplicationData = null,
dataSource = null,
persistencePolicy = ComplicationPersistencePolicies.CACHING_ALLOWED,
- displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY
+ displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY,
+ fallback = null,
) {
// Always empty.
override fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {}
@@ -400,9 +398,8 @@
* recommended to choose the [smallImage]. It's best practice for a ComplicationDataSource to
* specify both a [monochromaticImage] and a [smallImage]
*
- * A data source that wants to serve a ShortTextComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a ShortTextComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
* ```
* <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
* android:value="SHORT_TEXT"/>
@@ -434,6 +431,7 @@
* be rendered as a light grey box.
* @property contentDescription The content description field for accessibility. Please do not
* include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
*/
public class ShortTextComplicationData
internal constructor(
@@ -447,7 +445,9 @@
cachedWireComplicationData: WireComplicationData?,
dataSource: ComponentName?,
@ComplicationPersistencePolicy persistencePolicy: Int,
- @ComplicationDisplayPolicy displayPolicy: Int
+ @ComplicationDisplayPolicy displayPolicy: Int,
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public val fallback: ShortTextComplicationData?,
) :
ComplicationData(
TYPE,
@@ -456,7 +456,8 @@
validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
dataSource = dataSource,
persistencePolicy = persistencePolicy,
- displayPolicy = displayPolicy
+ displayPolicy = displayPolicy,
+ fallback = fallback,
) {
/**
* Builder for [ShortTextComplicationData].
@@ -465,9 +466,9 @@
*
* @param text The main localized [ComplicationText]. This must be less than 7 characters long
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications do
- * not have textual representation this attribute can be used for providing such. Please do not
- * include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some complications
+ * do not have textual representation this attribute can be used for providing such. Please do
+ * not include the word 'complication' in the description.
*/
public class Builder(
private val text: ComplicationText,
@@ -516,7 +517,8 @@
cachedWireComplicationData,
dataSource,
persistencePolicy,
- displayPolicy
+ displayPolicy,
+ fallback,
)
}
@@ -543,7 +545,8 @@
"contentDescription=$contentDescription, " +
"tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
"tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
- "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+ "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+ "fallback=$fallback)"
}
override fun hasPlaceholderFields() =
@@ -590,9 +593,8 @@
* recommended to choose the [smallImage]. It's best practice for a ComplicationDataSource to
* specify both a [monochromaticImage] and a [smallImage].
*
- * A data source that wants to serve a LongTextComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a LongTextComplicationData must include the following meta data
+ * in its manifest (NB the value is a comma separated list):
* ```
* <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
* android:value="LONG_TEXT"/>
@@ -614,6 +616,7 @@
* be rendered as a light grey box.
* @property contentDescription The content description field for accessibility. Please do not
* include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
*/
public class LongTextComplicationData
internal constructor(
@@ -627,7 +630,9 @@
cachedWireComplicationData: WireComplicationData?,
dataSource: ComponentName?,
@ComplicationPersistencePolicy persistencePolicy: Int,
- @ComplicationDisplayPolicy displayPolicy: Int
+ @ComplicationDisplayPolicy displayPolicy: Int,
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public val fallback: LongTextComplicationData?,
) :
ComplicationData(
TYPE,
@@ -636,7 +641,8 @@
validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
dataSource = dataSource,
persistencePolicy = persistencePolicy,
- displayPolicy = displayPolicy
+ displayPolicy = displayPolicy,
+ fallback = fallback,
) {
/**
* Builder for [LongTextComplicationData].
@@ -646,9 +652,9 @@
* @param text Localized main [ComplicationText] to display within the complication. There isn't
* an explicit character limit but text may be truncated if too long
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications do
- * not have textual representation this attribute can be used for providing such. Please do not
- * include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some complications
+ * do not have textual representation this attribute can be used for providing such. Please do
+ * not include the word 'complication' in the description.
*/
public class Builder(
private val text: ComplicationText,
@@ -697,7 +703,8 @@
cachedWireComplicationData,
dataSource,
persistencePolicy,
- displayPolicy
+ displayPolicy,
+ fallback,
)
}
@@ -724,7 +731,8 @@
"contentDescription=$contentDescription), " +
"tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
"tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
- "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+ "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+ "fallback=$fallback)"
}
override fun hasPlaceholderFields() =
@@ -820,9 +828,8 @@
* recommended to choose the [smallImage]. It's best practice for a ComplicationDataSource to
* specify both a [monochromaticImage] and a [smallImage].
*
- * A data source that wants to serve a RangedValueComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a RangedValueComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
* ```
* <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
* android:value="GOAL_PROGRESS"/>
@@ -865,6 +872,7 @@
* @property valueType The semantic meaning of [value]. The complication renderer may choose to
* visually differentiate between the different types, for example rendering a dot on a line/arc
* to indicate the value for a [TYPE_RATING].
+ * @property fallback Used in case any expression has been invalidated.
*/
public class RangedValueComplicationData
internal constructor(
@@ -884,7 +892,10 @@
public val colorRamp: ColorRamp?,
@RangedValueType public val valueType: Int,
@ComplicationPersistencePolicy persistencePolicy: Int,
- @ComplicationDisplayPolicy displayPolicy: Int
+ @ComplicationDisplayPolicy displayPolicy: Int,
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public val fallback: RangedValueComplicationData?,
) :
ComplicationData(
TYPE,
@@ -893,15 +904,15 @@
validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
dataSource = dataSource,
persistencePolicy = persistencePolicy,
- displayPolicy = displayPolicy
+ displayPolicy = displayPolicy,
+ fallback = fallback,
) {
/**
* 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.
- *
- * @hide
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public val valueExpression: DynamicFloat? = valueExpression
@RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -933,9 +944,9 @@
* @param max The maximum value. This must be less than [Float.MAX_VALUE]. For
* [TYPE_PERCENTAGE] this must be 0f.
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications
- * do not have textual representation this attribute can be used for providing such. Please
- * do not include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some
+ * complications do not have textual representation this attribute can be used for
+ * providing such. Please do not include the word 'complication' in the description.
*/
public constructor(
value: Float,
@@ -954,10 +965,11 @@
* @param max The maximum value. This must be less than [Float.MAX_VALUE]. For
* [TYPE_PERCENTAGE] this must be 0f.
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications
- * do not have textual representation this attribute can be used for providing such. Please
- * do not include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some
+ * complications do not have textual representation this attribute can be used for
+ * providing such. Please do not include the word 'complication' in the description.
*/
+ // TODO(b/269414040): Unhide complication expression APIs.
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public constructor(
valueExpression: DynamicFloat,
@@ -1058,7 +1070,8 @@
colorRamp,
valueType,
persistencePolicy,
- displayPolicy
+ displayPolicy,
+ fallback,
)
}
}
@@ -1109,7 +1122,7 @@
"tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
"tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
"colorRamp=$colorRamp, persistencePolicy=$persistencePolicy, " +
- "displayPolicy=$displayPolicy)"
+ "displayPolicy=$displayPolicy, fallback=$fallback)"
}
override fun hasPlaceholderFields() =
@@ -1186,9 +1199,8 @@
* [RangedValueComplicationData.TYPE_RATING] into
* [RangedValueComplicationData.Builder.setValueType].
*
- * A data source that wants to serve a SmallImageComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a SmallImageComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
* ```
* <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
* android:value="GOAL_PROGRESS"/>
@@ -1227,6 +1239,7 @@
* include the word 'complication' in the description.
* @property colorRamp Optional hint to render the progress bar representing [value] with the
* specified [ColorRamp].
+ * @property fallback Used in case any expression has been invalidated.
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public class GoalProgressComplicationData
@@ -1245,7 +1258,10 @@
dataSource: ComponentName?,
public val colorRamp: ColorRamp?,
@ComplicationPersistencePolicy persistencePolicy: Int,
- @ComplicationDisplayPolicy displayPolicy: Int
+ @ComplicationDisplayPolicy displayPolicy: Int,
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public val fallback: GoalProgressComplicationData?,
) :
ComplicationData(
TYPE,
@@ -1254,15 +1270,15 @@
validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
dataSource = dataSource,
persistencePolicy = persistencePolicy,
- displayPolicy = displayPolicy
+ displayPolicy = displayPolicy,
+ fallback = fallback,
) {
/**
* 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.
- *
- * @hide
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public val valueExpression: DynamicFloat? = valueExpression
/**
@@ -1287,9 +1303,9 @@
* @param value The value of the goal complication which should be >= 0.
* @param targetValue The target value. This must be less than [Float.MAX_VALUE].
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications
- * do not have textual representation this attribute can be used for providing such. Please
- * do not include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some
+ * complications do not have textual representation this attribute can be used for
+ * providing such. Please do not include the word 'complication' in the description.
*/
public constructor(
value: Float,
@@ -1304,10 +1320,11 @@
* evaluated into a value dynamically, and should be >= 0.
* @param targetValue The target value. This must be less than [Float.MAX_VALUE].
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications
- * do not have textual representation this attribute can be used for providing such. Please
- * do not include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some
+ * complications do not have textual representation this attribute can be used for
+ * providing such. Please do not include the word 'complication' in the description.
*/
+ // TODO(b/269414040): Unhide complication expression APIs.
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public constructor(
valueExpression: DynamicFloat,
@@ -1392,6 +1409,7 @@
colorRamp,
persistencePolicy,
displayPolicy,
+ fallback = fallback,
)
}
}
@@ -1440,7 +1458,7 @@
"tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
"tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
"colorRamp=$colorRamp, persistencePolicy=$persistencePolicy, " +
- "displayPolicy=$displayPolicy)"
+ "displayPolicy=$displayPolicy, fallback=$fallback)"
}
override fun hasPlaceholderFields() =
@@ -1493,9 +1511,8 @@
* recommended to choose the [smallImage]. It's best practice for a ComplicationDataSource to
* specify both a [monochromaticImage] and a [smallImage].
*
- * A data source that wants to serve a SmallImageComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a SmallImageComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
* ```
* <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
* android:value="WEIGHTED_ELEMENTS"/>
@@ -1536,6 +1553,7 @@
* grey box.
* @property contentDescription The content description field for accessibility. Please do not
* include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public class WeightedElementsComplicationData
@@ -1552,7 +1570,10 @@
cachedWireComplicationData: WireComplicationData?,
dataSource: ComponentName?,
@ComplicationPersistencePolicy persistencePolicy: Int,
- @ComplicationDisplayPolicy displayPolicy: Int
+ @ComplicationDisplayPolicy displayPolicy: Int,
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public val fallback: WeightedElementsComplicationData?,
) :
ComplicationData(
TYPE,
@@ -1561,7 +1582,8 @@
validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
dataSource = dataSource,
persistencePolicy = persistencePolicy,
- displayPolicy = displayPolicy
+ displayPolicy = displayPolicy,
+ fallback = fallback,
) {
/**
* Describes a single value within a [WeightedElementsComplicationData].
@@ -1617,9 +1639,9 @@
* to an experience where the color key becomes obvious. The maximum valid size of this list
* is provided by [getMaxElements].
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications
- * do not have textual representation this attribute can be used for providing such. Please
- * do not include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some complications
+ * do not have textual representation this attribute can be used for providing such. Please do
+ * not include the word 'complication' in the description.
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public class Builder(
@@ -1707,7 +1729,8 @@
cachedWireComplicationData,
dataSource,
persistencePolicy,
- displayPolicy
+ displayPolicy,
+ fallback,
)
}
}
@@ -1755,7 +1778,8 @@
"text=$text, contentDescription=$contentDescription), " +
"tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
"tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
- "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+ "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+ "fallback=$fallback)"
}
override fun hasPlaceholderFields() =
@@ -1795,7 +1819,6 @@
*
* A data source that wants to serve a MonochromaticImageComplicationData must include the following
* meta data in its manifest (NB the value is a comma separated list):
- *
* ```
* <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
* android:value="ICON"/>
@@ -1810,6 +1833,7 @@
* any information to the user, then provide an empty content description. If no content
* description is provided, a generic content description will be used instead. Please do not
* include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
*/
public class MonochromaticImageComplicationData
internal constructor(
@@ -1820,7 +1844,10 @@
cachedWireComplicationData: WireComplicationData?,
dataSource: ComponentName?,
@ComplicationPersistencePolicy persistencePolicy: Int,
- @ComplicationDisplayPolicy displayPolicy: Int
+ @ComplicationDisplayPolicy displayPolicy: Int,
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public val fallback: MonochromaticImageComplicationData?,
) :
ComplicationData(
TYPE,
@@ -1829,7 +1856,8 @@
validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
dataSource = dataSource,
persistencePolicy = persistencePolicy,
- displayPolicy = displayPolicy
+ displayPolicy = displayPolicy,
+ fallback = fallback,
) {
/**
* Builder for [MonochromaticImageComplicationData].
@@ -1838,9 +1866,9 @@
*
* @param monochromaticImage The [MonochromaticImage] to be displayed
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications
- * do not have textual representation this attribute can be used for providing such. Please
- * do not include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some complications
+ * do not have textual representation this attribute can be used for providing such. Please do
+ * not include the word 'complication' in the description.
*/
public class Builder(
private val monochromaticImage: MonochromaticImage,
@@ -1870,7 +1898,8 @@
cachedWireComplicationData,
dataSource,
persistencePolicy,
- displayPolicy
+ displayPolicy,
+ fallback,
)
}
@@ -1895,7 +1924,8 @@
"contentDescription=$contentDescription), " +
"tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
"tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
- "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+ "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+ "fallback=$fallback)"
}
/** @hide */
@@ -1910,9 +1940,8 @@
*
* The image is expected to always be displayed.
*
- * A data source that wants to serve a SmallImageComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a SmallImageComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
* ```
* <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
* android:value="SMALL_IMAGE"/>
@@ -1927,6 +1956,7 @@
* any information to the user, then provide an empty content description. If no content
* description is provided, a generic content description will be used instead. Please do not
* include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
*/
public class SmallImageComplicationData
internal constructor(
@@ -1937,7 +1967,10 @@
cachedWireComplicationData: WireComplicationData?,
dataSource: ComponentName?,
@ComplicationPersistencePolicy persistencePolicy: Int,
- @ComplicationDisplayPolicy displayPolicy: Int
+ @ComplicationDisplayPolicy displayPolicy: Int,
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public val fallback: SmallImageComplicationData?,
) :
ComplicationData(
TYPE,
@@ -1946,7 +1979,8 @@
validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
dataSource = dataSource,
persistencePolicy = persistencePolicy,
- displayPolicy = displayPolicy
+ displayPolicy = displayPolicy,
+ fallback = fallback,
) {
/**
* Builder for [SmallImageComplicationData].
@@ -1955,9 +1989,9 @@
*
* @param smallImage The [SmallImage] to be displayed
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications
- * do not have textual representation this attribute can be used for providing such. Please
- * do not include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some complications
+ * do not have textual representation this attribute can be used for providing such. Please do
+ * not include the word 'complication' in the description.
*/
public class Builder(
private val smallImage: SmallImage,
@@ -1987,7 +2021,8 @@
cachedWireComplicationData,
dataSource,
persistencePolicy,
- displayPolicy
+ displayPolicy,
+ fallback,
)
}
@@ -2010,7 +2045,8 @@
"contentDescription=$contentDescription), " +
"tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
"tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
- "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+ "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+ "fallback=$fallback)"
}
override fun hasPlaceholderFields() = smallImage.isPlaceholder()
@@ -2031,9 +2067,8 @@
* part of the watch face or within a complication. The image is large enough to be cover the entire
* screen. The image may be cropped to fit the watch face or complication.
*
- * A data source that wants to serve a PhotoImageComplicationData must include the following
- * meta data in its manifest (NB the value is a comma separated list):
- *
+ * A data source that wants to serve a PhotoImageComplicationData must include the following meta
+ * data in its manifest (NB the value is a comma separated list):
* ```
* <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
* android:value="LARGE_IMAGE"/>
@@ -2049,6 +2084,7 @@
* any information to the user, then provide an empty content description. If no content
* description is provided, a generic content description will be used instead. Please do not
* include the word 'complication' in the description.
+ * @property fallback Used in case any expression has been invalidated.
*/
public class PhotoImageComplicationData
internal constructor(
@@ -2059,7 +2095,10 @@
cachedWireComplicationData: WireComplicationData?,
dataSource: ComponentName?,
@ComplicationPersistencePolicy persistencePolicy: Int,
- @ComplicationDisplayPolicy displayPolicy: Int
+ @ComplicationDisplayPolicy displayPolicy: Int,
+ // TODO(b/269414040): Unhide complication expression APIs.
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public val fallback: PhotoImageComplicationData?,
) :
ComplicationData(
TYPE,
@@ -2068,7 +2107,8 @@
validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
dataSource = dataSource,
persistencePolicy = persistencePolicy,
- displayPolicy = displayPolicy
+ displayPolicy = displayPolicy,
+ fallback = fallback,
) {
/**
* Builder for [PhotoImageComplicationData].
@@ -2077,9 +2117,9 @@
*
* @param photoImage The [Icon] to be displayed
* @param contentDescription Defines localized text that briefly describes content of the
- * complication. This property is used primarily for accessibility. Since some complications
- * do not have textual representation this attribute can be used for providing such. Please
- * do not include the word 'complication' in the description.
+ * complication. This property is used primarily for accessibility. Since some complications
+ * do not have textual representation this attribute can be used for providing such. Please do
+ * not include the word 'complication' in the description.
*/
public class Builder(
private val photoImage: Icon,
@@ -2110,7 +2150,8 @@
cachedWireComplicationData,
dataSource,
persistencePolicy,
- displayPolicy
+ displayPolicy,
+ fallback,
)
}
@@ -2133,7 +2174,8 @@
"contentDescription=$contentDescription), " +
"tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
"tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
- "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
+ "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy, " +
+ "fallback=$fallback)"
}
override fun hasPlaceholderFields() = photoImage.isPlaceholder()
@@ -2191,7 +2233,7 @@
cachedWireComplicationData: WireComplicationData?,
dataSource: ComponentName?,
@ComplicationPersistencePolicy persistencePolicy: Int,
- @ComplicationDisplayPolicy displayPolicy: Int
+ @ComplicationDisplayPolicy displayPolicy: Int,
) :
ComplicationData(
TYPE,
@@ -2199,7 +2241,8 @@
cachedWireComplicationData = cachedWireComplicationData,
dataSource = dataSource,
persistencePolicy = persistencePolicy,
- displayPolicy = displayPolicy
+ displayPolicy = displayPolicy,
+ fallback = null,
) {
/** Builder for [NoPermissionComplicationData]. */
public class Builder : BaseBuilder<Builder, NoPermissionComplicationData>() {
@@ -2234,7 +2277,7 @@
cachedWireComplicationData,
dataSource,
persistencePolicy,
- displayPolicy
+ displayPolicy,
)
}
@@ -2271,305 +2314,167 @@
}
}
-@Suppress("NewApi")
-internal fun WireComplicationData.toPlaceholderComplicationData(): ComplicationData? {
- try {
- // Make sure we use the correct dataSource, persistencePolicy & displayPolicy.
- val dataSourceCopy = dataSource
- val persistencePolicyCopy = persistencePolicy
- val displayPolicyCopy = displayPolicy
- return when (type) {
- NoDataComplicationData.TYPE.toWireComplicationType() -> null
- ShortTextComplicationData.TYPE.toWireComplicationType() -> {
- ShortTextComplicationData.Builder(
- shortText!!.toApiComplicationTextPlaceholderAware(),
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setMonochromaticImage(parseIconPlaceholderAware())
- setSmallImage(parseSmallImagePlaceholderAware())
- setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
- setDataSource(dataSourceCopy)
- setPersistencePolicy(persistencePolicyCopy)
- setDisplayPolicy(displayPolicyCopy)
- }
- .build()
+internal fun WireComplicationData.toPlaceholderComplicationData(): ComplicationData? =
+ when (type) {
+ NoDataComplicationData.TYPE.toWireComplicationType() -> null
+ EmptyComplicationData.TYPE.toWireComplicationType() -> null
+ NotConfiguredComplicationData.TYPE.toWireComplicationType() -> null
+ else ->
+ toApiComplicationData(placeholderAware = true).let {
+ if (it is NoDataComplicationData) null else it
}
- LongTextComplicationData.TYPE.toWireComplicationType() -> {
- LongTextComplicationData.Builder(
- longText!!.toApiComplicationTextPlaceholderAware(),
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setMonochromaticImage(parseIconPlaceholderAware())
- setSmallImage(parseSmallImagePlaceholderAware())
- setTitle(longTitle?.toApiComplicationTextPlaceholderAware())
- setDataSource(dataSourceCopy)
- setPersistencePolicy(persistencePolicyCopy)
- setDisplayPolicy(displayPolicyCopy)
- }
- .build()
- }
- RangedValueComplicationData.TYPE.toWireComplicationType() ->
- RangedValueComplicationData.Builder(
- value = rangedValue,
- valueExpression = rangedValueExpression,
- min = rangedMinValue,
- max = rangedMaxValue,
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setMonochromaticImage(parseIconPlaceholderAware())
- setSmallImage(parseSmallImagePlaceholderAware())
- setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
- setText(shortText?.toApiComplicationTextPlaceholderAware())
- setDataSource(dataSourceCopy)
- colorRamp?.let { setColorRamp(ColorRamp(it, isColorRampInterpolated!!)) }
- setPersistencePolicy(persistencePolicyCopy)
- setDisplayPolicy(displayPolicyCopy)
- setValueType(rangedValueType)
- }
- .build()
- MonochromaticImageComplicationData.TYPE.toWireComplicationType() ->
- MonochromaticImageComplicationData(
- parseIconPlaceholderAware()!!,
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
- tapAction,
- parseTimeRange(),
- this,
- dataSourceCopy,
- persistencePolicyCopy,
- displayPolicyCopy
- )
- SmallImageComplicationData.TYPE.toWireComplicationType() ->
- SmallImageComplicationData(
- parseSmallImagePlaceholderAware()!!,
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
- tapAction,
- parseTimeRange(),
- this,
- dataSourceCopy,
- persistencePolicyCopy,
- displayPolicyCopy
- )
- PhotoImageComplicationData.TYPE.toWireComplicationType() ->
- PhotoImageComplicationData(
- parseLargeImagePlaceholderAware()!!,
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
- tapAction,
- parseTimeRange(),
- this,
- dataSourceCopy,
- persistencePolicyCopy,
- displayPolicyCopy
- )
- GoalProgressComplicationData.TYPE.toWireComplicationType() ->
- GoalProgressComplicationData.Builder(
- value = rangedValue,
- valueExpression = rangedValueExpression,
- targetValue = targetValue,
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setMonochromaticImage(parseIconPlaceholderAware())
- setSmallImage(parseSmallImagePlaceholderAware())
- setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
- setText(shortText?.toApiComplicationTextPlaceholderAware())
- setDataSource(dataSourceCopy)
- colorRamp?.let { setColorRamp(ColorRamp(it, isColorRampInterpolated!!)) }
- setPersistencePolicy(persistencePolicyCopy)
- setDisplayPolicy(displayPolicyCopy)
- }
- .build()
- WeightedElementsComplicationData.TYPE.toWireComplicationType() ->
- WeightedElementsComplicationData.Builder(
- elements =
- if (elementWeights!!.isEmpty()) {
- WeightedElementsComplicationData.PLACEHOLDER
- } else {
- val elementWeights = this.elementWeights!!
- val elementColors = this.elementColors!!
- require(elementWeights.size == elementColors.size) {
- "elementWeights and elementColors must have the same size"
- }
- elementWeights
- .mapIndexed { index, weight ->
- WeightedElementsComplicationData.Element(
- weight,
- elementColors[index]
- )
- }
- .toList()
- },
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setElementBackgroundColor(elementBackgroundColor)
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setMonochromaticImage(parseIconPlaceholderAware())
- setSmallImage(parseSmallImagePlaceholderAware())
- setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
- setText(shortText?.toApiComplicationTextPlaceholderAware())
- setDataSource(dataSourceCopy)
- setPersistencePolicy(persistencePolicyCopy)
- setDisplayPolicy(displayPolicyCopy)
- }
- .build()
- else -> null
- }
- } catch (e: Exception) {
- Log.e(
- TAG,
- "WireComplicationData.toPlaceholderComplicationData failed for " +
- toStringNoRedaction(),
- e
- )
- throw e
}
-}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public fun WireComplicationData.toApiComplicationData(): ComplicationData =
+ toApiComplicationData(placeholderAware = false)
+
@Suppress("NewApi")
-public fun WireComplicationData.toApiComplicationData(): ComplicationData {
+private fun WireComplicationData.toApiComplicationData(
+ placeholderAware: Boolean
+): ComplicationData {
try {
return when (type) {
- NoDataComplicationData.TYPE.toWireComplicationType() -> {
- placeholder?.toPlaceholderComplicationData()?.let {
- NoDataComplicationData(it, this@toApiComplicationData)
- }
- ?: NoDataComplicationData(null, this@toApiComplicationData)
- }
+ NoDataComplicationData.TYPE.toWireComplicationType() ->
+ NoDataComplicationData(placeholder?.toPlaceholderComplicationData(), this)
EmptyComplicationData.TYPE.toWireComplicationType() -> EmptyComplicationData()
NotConfiguredComplicationData.TYPE.toWireComplicationType() ->
NotConfiguredComplicationData()
ShortTextComplicationData.TYPE.toWireComplicationType() ->
- ShortTextComplicationData.Builder(
- shortText!!.toApiComplicationText(),
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setCommon(this@toApiComplicationData)
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setTitle(shortTitle?.toApiComplicationText())
- setMonochromaticImage(parseIcon())
- setSmallImage(parseSmallImage())
- }
- .build()
+ ShortTextComplicationData(
+ text = shortText!!.toApiComplicationText(placeholderAware),
+ title = shortTitle?.toApiComplicationText(placeholderAware),
+ monochromaticImage = parseIcon(placeholderAware),
+ smallImage = parseSmallImage(placeholderAware),
+ contentDescription = contentDescription?.toApiComplicationText()
+ ?: ComplicationText.EMPTY,
+ tapAction = tapAction,
+ validTimeRange = parseTimeRange(),
+ cachedWireComplicationData = this,
+ dataSource = dataSource,
+ persistencePolicy = persistencePolicy,
+ displayPolicy = displayPolicy,
+ fallback = placeholder?.toTypedApiComplicationData(),
+ )
LongTextComplicationData.TYPE.toWireComplicationType() ->
- LongTextComplicationData.Builder(
- longText!!.toApiComplicationText(),
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setCommon(this@toApiComplicationData)
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setTitle(longTitle?.toApiComplicationText())
- setMonochromaticImage(parseIcon())
- setSmallImage(parseSmallImage())
- }
- .build()
+ LongTextComplicationData(
+ text = longText!!.toApiComplicationText(placeholderAware),
+ title = longTitle?.toApiComplicationText(placeholderAware),
+ monochromaticImage = parseIcon(placeholderAware),
+ smallImage = parseSmallImage(placeholderAware),
+ contentDescription = contentDescription?.toApiComplicationText()
+ ?: ComplicationText.EMPTY,
+ tapAction = tapAction,
+ validTimeRange = parseTimeRange(),
+ cachedWireComplicationData = this,
+ dataSource = dataSource,
+ persistencePolicy = persistencePolicy,
+ displayPolicy = displayPolicy,
+ fallback = placeholder?.toTypedApiComplicationData(),
+ )
RangedValueComplicationData.TYPE.toWireComplicationType() ->
- RangedValueComplicationData.Builder(
- value = rangedValue,
- valueExpression = rangedValueExpression,
- min = rangedMinValue,
- max = rangedMaxValue,
- contentDescription = contentDescription?.toApiComplicationText()
- ?: ComplicationText.EMPTY
- )
- .apply {
- setCommon(this@toApiComplicationData)
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setMonochromaticImage(parseIcon())
- setSmallImage(parseSmallImage())
- setTitle(shortTitle?.toApiComplicationText())
- setText(shortText?.toApiComplicationText())
- colorRamp?.let { setColorRamp(ColorRamp(it, isColorRampInterpolated!!)) }
- setValueType(rangedValueType)
- }
- .build()
+ RangedValueComplicationData(
+ value = rangedValue,
+ valueExpression = rangedValueExpression,
+ min = rangedMinValue,
+ max = rangedMaxValue,
+ monochromaticImage = parseIcon(placeholderAware),
+ smallImage = parseSmallImage(placeholderAware),
+ title = shortTitle?.toApiComplicationText(placeholderAware),
+ text = shortText?.toApiComplicationText(placeholderAware),
+ contentDescription = contentDescription?.toApiComplicationText()
+ ?: ComplicationText.EMPTY,
+ tapAction = tapAction,
+ validTimeRange = parseTimeRange(),
+ cachedWireComplicationData = this,
+ dataSource = dataSource,
+ colorRamp = colorRamp?.let { ColorRamp(it, isColorRampInterpolated!!) },
+ valueType = rangedValueType,
+ persistencePolicy = persistencePolicy,
+ displayPolicy = displayPolicy,
+ fallback = placeholder?.toTypedApiComplicationData(),
+ )
MonochromaticImageComplicationData.TYPE.toWireComplicationType() ->
- MonochromaticImageComplicationData.Builder(
- parseIcon()!!,
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setCommon(this@toApiComplicationData)
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- }
- .build()
+ MonochromaticImageComplicationData(
+ monochromaticImage = parseIcon(placeholderAware)!!,
+ contentDescription = contentDescription?.toApiComplicationText()
+ ?: ComplicationText.EMPTY,
+ tapAction = tapAction,
+ validTimeRange = parseTimeRange(),
+ cachedWireComplicationData = this,
+ dataSource = dataSource,
+ persistencePolicy = persistencePolicy,
+ displayPolicy = displayPolicy,
+ fallback = placeholder?.toTypedApiComplicationData(),
+ )
SmallImageComplicationData.TYPE.toWireComplicationType() ->
- SmallImageComplicationData.Builder(
- parseSmallImage()!!,
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setCommon(this@toApiComplicationData)
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- }
- .build()
+ SmallImageComplicationData(
+ smallImage = parseSmallImage(placeholderAware)!!,
+ contentDescription = contentDescription?.toApiComplicationText()
+ ?: ComplicationText.EMPTY,
+ tapAction = tapAction,
+ validTimeRange = parseTimeRange(),
+ cachedWireComplicationData = this,
+ dataSource = dataSource,
+ persistencePolicy = persistencePolicy,
+ displayPolicy = displayPolicy,
+ fallback = placeholder?.toTypedApiComplicationData(),
+ )
PhotoImageComplicationData.TYPE.toWireComplicationType() ->
- PhotoImageComplicationData.Builder(
- largeImage!!,
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setCommon(this@toApiComplicationData)
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- }
- .build()
+ PhotoImageComplicationData(
+ photoImage = parseLargeImage(placeholderAware)!!,
+ contentDescription = contentDescription?.toApiComplicationText()
+ ?: ComplicationText.EMPTY,
+ tapAction = tapAction,
+ validTimeRange = parseTimeRange(),
+ cachedWireComplicationData = this,
+ dataSource = dataSource,
+ persistencePolicy = persistencePolicy,
+ displayPolicy = displayPolicy,
+ fallback = placeholder?.toTypedApiComplicationData(),
+ )
NoPermissionComplicationData.TYPE.toWireComplicationType() ->
- NoPermissionComplicationData.Builder()
- .apply {
- setCommon(this@toApiComplicationData)
- setMonochromaticImage(parseIcon())
- setSmallImage(parseSmallImage())
- setTitle(shortTitle?.toApiComplicationText())
- setText(shortText?.toApiComplicationText())
- }
- .build()
+ NoPermissionComplicationData(
+ text = shortText?.toApiComplicationText(),
+ title = shortTitle?.toApiComplicationText(),
+ monochromaticImage = parseIcon(),
+ smallImage = parseSmallImage(),
+ cachedWireComplicationData = this,
+ dataSource = dataSource,
+ persistencePolicy = persistencePolicy,
+ displayPolicy = displayPolicy,
+ )
GoalProgressComplicationData.TYPE.toWireComplicationType() ->
- GoalProgressComplicationData.Builder(
- value = rangedValue,
- valueExpression = rangedValueExpression,
- targetValue = targetValue,
- contentDescription = contentDescription?.toApiComplicationText()
- ?: ComplicationText.EMPTY
- )
- .apply {
- setCommon(this@toApiComplicationData)
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setMonochromaticImage(parseIcon())
- setSmallImage(parseSmallImage())
- setTitle(shortTitle?.toApiComplicationText())
- setText(shortText?.toApiComplicationText())
- colorRamp?.let { setColorRamp(ColorRamp(it, isColorRampInterpolated!!)) }
- }
- .build()
- WeightedElementsComplicationData.TYPE.toWireComplicationType() -> {
- val elementWeights = this.elementWeights!!
- val elementColors = this.elementColors!!
- require(elementWeights.size == elementColors.size) {
- "elementWeights and elementColors must have the same size"
- }
- WeightedElementsComplicationData.Builder(
- elements =
+ GoalProgressComplicationData(
+ value = rangedValue,
+ valueExpression = rangedValueExpression,
+ targetValue = targetValue,
+ monochromaticImage = parseIcon(placeholderAware),
+ smallImage = parseSmallImage(placeholderAware),
+ title = shortTitle?.toApiComplicationText(placeholderAware),
+ text = shortText?.toApiComplicationText(placeholderAware),
+ contentDescription = contentDescription?.toApiComplicationText()
+ ?: ComplicationText.EMPTY,
+ tapAction = tapAction,
+ validTimeRange = parseTimeRange(),
+ cachedWireComplicationData = this,
+ dataSource = dataSource,
+ colorRamp = colorRamp?.let { ColorRamp(it, isColorRampInterpolated!!) },
+ persistencePolicy = persistencePolicy,
+ displayPolicy = displayPolicy,
+ fallback = placeholder?.toTypedApiComplicationData(),
+ )
+ WeightedElementsComplicationData.TYPE.toWireComplicationType() ->
+ WeightedElementsComplicationData(
+ elements =
+ if (placeholderAware && elementWeights!!.isEmpty()) {
+ WeightedElementsComplicationData.PLACEHOLDER
+ } else {
+ val elementWeights = this.elementWeights!!
+ val elementColors = this.elementColors!!
+ require(elementWeights.size == elementColors.size) {
+ "elementWeights and elementColors must have the same size"
+ }
elementWeights
.mapIndexed { index, weight ->
WeightedElementsComplicationData.Element(
@@ -2577,21 +2482,23 @@
elementColors[index]
)
}
- .toList(),
- contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
- )
- .apply {
- setCommon(this@toApiComplicationData)
- setElementBackgroundColor(elementBackgroundColor)
- setTapAction(tapAction)
- setValidTimeRange(parseTimeRange())
- setMonochromaticImage(parseIcon())
- setSmallImage(parseSmallImage())
- setTitle(shortTitle?.toApiComplicationText())
- setText(shortText?.toApiComplicationText())
- }
- .build()
- }
+ .toList()
+ },
+ elementBackgroundColor = elementBackgroundColor,
+ monochromaticImage = parseIcon(placeholderAware),
+ smallImage = parseSmallImage(placeholderAware),
+ title = shortTitle?.toApiComplicationText(placeholderAware),
+ text = shortText?.toApiComplicationText(placeholderAware),
+ contentDescription = contentDescription?.toApiComplicationText()
+ ?: ComplicationText.EMPTY,
+ tapAction = tapAction,
+ validTimeRange = parseTimeRange(),
+ cachedWireComplicationData = this,
+ dataSource = dataSource,
+ persistencePolicy = persistencePolicy,
+ displayPolicy = displayPolicy,
+ fallback = placeholder?.toTypedApiComplicationData(),
+ )
else -> NoDataComplicationData()
}
} catch (e: Exception) {
@@ -2604,6 +2511,11 @@
}
}
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Suppress("UNCHECKED_CAST")
+public fun <T : ComplicationData> WireComplicationData.toTypedApiComplicationData(): T =
+ toApiComplicationData() as T
+
private fun WireComplicationData.parseTimeRange() =
if ((startDateTimeMillis == 0L) and (endDateTimeMillis == Long.MAX_VALUE)) {
null
@@ -2614,36 +2526,18 @@
)
}
-private fun WireComplicationData.parseIcon() =
+private fun WireComplicationData.parseIcon(placeholderAware: Boolean = false) =
icon?.let {
- MonochromaticImage.Builder(it).apply { setAmbientImage(burnInProtectionIcon) }.build()
- }
-
-private fun WireComplicationData.parseIconPlaceholderAware() =
- icon?.let {
- if (it.isPlaceholder()) {
+ if (placeholderAware && it.isPlaceholder()) {
MonochromaticImage.PLACEHOLDER
} else {
MonochromaticImage.Builder(it).apply { setAmbientImage(burnInProtectionIcon) }.build()
}
}
-private fun WireComplicationData.parseSmallImage() =
+private fun WireComplicationData.parseSmallImage(placeholderAware: Boolean = false) =
smallImage?.let {
- val imageStyle =
- when (smallImageStyle) {
- WireComplicationData.IMAGE_STYLE_ICON -> SmallImageType.ICON
- WireComplicationData.IMAGE_STYLE_PHOTO -> SmallImageType.PHOTO
- else -> SmallImageType.PHOTO
- }
- SmallImage.Builder(it, imageStyle)
- .apply { setAmbientImage(burnInProtectionSmallImage) }
- .build()
- }
-
-private fun WireComplicationData.parseSmallImagePlaceholderAware() =
- smallImage?.let {
- if (it.isPlaceholder()) {
+ if (placeholderAware && it.isPlaceholder()) {
SmallImage.PLACEHOLDER
} else {
val imageStyle =
@@ -2658,9 +2552,9 @@
}
}
-private fun WireComplicationData.parseLargeImagePlaceholderAware() =
+private fun WireComplicationData.parseLargeImage(placeholderAware: Boolean = false) =
largeImage?.let {
- if (it.isPlaceholder()) {
+ if (placeholderAware && it.isPlaceholder()) {
PhotoImageComplicationData.PLACEHOLDER
} else {
it
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 60db4fe..de0c253 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
@@ -71,10 +71,7 @@
/** @hide */
@RestrictTo(RestrictTo.Scope.SUBCLASSES) public fun getTimeDependentText(): TimeDependentText
- /**
- * Converts this value to [WireComplicationText] object used for serialization.
- *
- */
+ /** Converts this value to [WireComplicationText] object used for serialization. */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun toWireComplicationText(): WireComplicationText
@@ -548,12 +545,10 @@
}
/** Converts a [WireComplicationText] into an equivalent [ComplicationText] instead. */
-internal fun WireComplicationText.toApiComplicationText(): ComplicationText =
- DelegatingComplicationText(this)
-
-/** Converts a [WireComplicationText] into an equivalent [ComplicationText] instead. */
-internal fun WireComplicationText.toApiComplicationTextPlaceholderAware(): ComplicationText =
- if (isPlaceholder) {
+internal fun WireComplicationText.toApiComplicationText(
+ placeholderAware: Boolean = false
+): ComplicationText =
+ if (placeholderAware && isPlaceholder) {
ComplicationText.PLACEHOLDER
} else {
DelegatingComplicationText(this)
@@ -615,8 +610,8 @@
* A [ComplicationText] where the system evaluates a [DynamicString] on behalf of the watch face. By
* the time this reaches the watch face's Renderer, it'll have been converted to a plain
* ComplicationText.
- *
*/
+// TODO(b/269414040): Unhide complication expression APIs.
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class ComplicationTextExpression(public val expression: DynamicString) : ComplicationText {
private val delegate = DelegatingComplicationText(WireComplicationText(expression))
diff --git a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt
index 3f25f2a..588241b 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/android/support/wearable/complications/ComplicationDataTest.kt
@@ -25,15 +25,20 @@
import android.support.wearable.complications.ComplicationText.plainText
import androidx.test.core.app.ApplicationProvider
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
import androidx.wear.watchface.complications.data.SharedRobolectricTestRunner
+import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import org.junit.Assert
import org.junit.Assert.assertThrows
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(SharedRobolectricTestRunner::class)
public class ComplicationDataTest {
+ @get:Rule val expect = Expect.create()
+
private val mPendingIntent: PendingIntent? =
PendingIntent.getBroadcast(
ApplicationProvider.getApplicationContext(),
@@ -1069,6 +1074,54 @@
assertThat(entry.placeholder!!.type).isEqualTo(ComplicationData.TYPE_LONG_TEXT)
}
+ enum class HasExpressionWithExpressionScenario(val data: ComplicationData) {
+ RANGED_VALUE(
+ ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+ .setRangedValueExpression(DynamicFloat.constant(1f))
+ .build()
+ ),
+ LONG_TEXT(
+ ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+ .setLongText(ComplicationText(DynamicString.constant("Long Text")))
+ .build()
+ ),
+ LONG_TITLE(
+ ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+ .setLongTitle(ComplicationText(DynamicString.constant("Long Title")))
+ .build()
+ ),
+ SHORT_TEXT(
+ ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+ .setShortText(ComplicationText(DynamicString.constant("Short Text")))
+ .build()
+ ),
+ SHORT_TITLE(
+ ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+ .setShortTitle(ComplicationText(DynamicString.constant("Short Title")))
+ .build()
+ ),
+ CONTENT_DESCRIPTION(
+ ComplicationData.Builder(ComplicationData.TYPE_NO_DATA)
+ .setContentDescription(ComplicationText(DynamicString.constant("Description")))
+ .build()
+ ),
+ }
+
+ @Test
+ fun hasExpression_withExpression_returnsTrue() {
+ for (scenario in HasExpressionWithExpressionScenario.values()) {
+ expect.withMessage(scenario.name).that(scenario.data.hasExpression()).isTrue()
+ }
+ }
+
+ @Test
+ fun hasExpression_withoutExpression_returnsFalse() {
+ val data =
+ ComplicationData.Builder(ComplicationData.TYPE_NO_DATA).setRangedValue(10f).build()
+
+ assertThat(data.hasExpression()).isFalse()
+ }
+
private companion object {
val TEST_CONTENT_DESCRIPTION: CharSequence = "This is a test description!"
const val TEST_LONG_TITLE = "what a long title such a long title"
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 05f6f9f..c913c25 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
@@ -26,7 +26,6 @@
import androidx.wear.protolayout.expression.pipeline.StateStore
import androidx.wear.protolayout.expression.pipeline.TimeGateway
import androidx.wear.watchface.complications.data.ComplicationDataExpressionEvaluator.Companion.INVALID_DATA
-import androidx.wear.watchface.complications.data.ComplicationDataExpressionEvaluator.Companion.hasExpression
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import java.time.Instant
@@ -59,10 +58,14 @@
@Test
fun evaluate_noExpression_returnsUnevaluated() = runBlocking {
+ val data =
+ WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+ .setRangedValue(10f)
+ .build()
+
val evaluator = ComplicationDataExpressionEvaluator()
- assertThat(evaluator.evaluate(DATA_WITH_NO_EXPRESSION).firstOrNull())
- .isEqualTo(DATA_WITH_NO_EXPRESSION)
+ assertThat(evaluator.evaluate(data).firstOrNull()).isEqualTo(data)
}
/**
@@ -203,8 +206,7 @@
for (scenario in DataWithExpressionScenario.values()) {
// Defensive copy due to in-place evaluation.
val expressed = WireComplicationData.Builder(scenario.expressed).build()
- val stateStore =
- StateStore(mapOf())
+ val stateStore = StateStore(mapOf())
val evaluator = ComplicationDataExpressionEvaluator(stateStore)
val allEvaluations =
evaluator
@@ -294,57 +296,7 @@
)
}
- enum class HasExpressionDataWithExpressionScenario(val data: WireComplicationData) {
- RANGED_VALUE(
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
- .setRangedValueExpression(DynamicFloat.constant(1f))
- .build()
- ),
- LONG_TEXT(
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
- .setLongText(WireComplicationText(DynamicString.constant("Long Text")))
- .build()
- ),
- LONG_TITLE(
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
- .setLongTitle(WireComplicationText(DynamicString.constant("Long Title")))
- .build()
- ),
- SHORT_TEXT(
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
- .setShortText(WireComplicationText(DynamicString.constant("Short Text")))
- .build()
- ),
- SHORT_TITLE(
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
- .setShortTitle(WireComplicationText(DynamicString.constant("Short Title")))
- .build()
- ),
- CONTENT_DESCRIPTION(
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
- .setContentDescription(WireComplicationText(DynamicString.constant("Description")))
- .build()
- ),
- }
-
- @Test
- fun hasExpression_dataWithExpression_returnsTrue() {
- for (scenario in HasExpressionDataWithExpressionScenario.values()) {
- expect.withMessage(scenario.name).that(hasExpression(scenario.data)).isTrue()
- }
- }
-
- @Test
- fun hasExpression_dataWithoutExpression_returnsFalse() {
- assertThat(hasExpression(DATA_WITH_NO_EXPRESSION)).isFalse()
- }
-
private companion object {
- val DATA_WITH_NO_EXPRESSION =
- WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
- .setRangedValue(10f)
- .build()
-
/** 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) }
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
index f75fff1..356a70f 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
@@ -163,7 +163,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=1, " +
- "displayPolicy=1)"
+ "displayPolicy=1, fallback=null)"
)
}
@@ -217,7 +217,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
}
@@ -261,7 +261,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
}
@@ -316,7 +316,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
}
@@ -367,7 +367,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
- "persistencePolicy=0, displayPolicy=0)"
+ "persistencePolicy=0, displayPolicy=0, fallback=null)"
)
}
@@ -421,7 +421,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), dataSource=" +
"ComponentInfo{com.pkg_a/com.a}, colorRamp=null, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
}
@@ -471,7 +471,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
- "persistencePolicy=0, displayPolicy=0)"
+ "persistencePolicy=0, displayPolicy=0, fallback=null)"
)
}
@@ -535,7 +535,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
- "persistencePolicy=0, displayPolicy=0)"
+ "persistencePolicy=0, displayPolicy=0, fallback=null)"
)
}
@@ -582,7 +582,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
- "persistencePolicy=0, displayPolicy=0)"
+ "persistencePolicy=0, displayPolicy=0, fallback=null)"
)
}
@@ -633,7 +633,7 @@
"startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
- "persistencePolicy=0, displayPolicy=0)"
+ "persistencePolicy=0, displayPolicy=0, fallback=null)"
)
}
@@ -683,7 +683,7 @@
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, " +
"colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
- "interpolated=true), persistencePolicy=0, displayPolicy=0)"
+ "interpolated=true), persistencePolicy=0, displayPolicy=0, fallback=null)"
)
}
@@ -746,7 +746,7 @@
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, " +
"colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
- "interpolated=true), persistencePolicy=0, displayPolicy=0)"
+ "interpolated=true), persistencePolicy=0, displayPolicy=0, fallback=null)"
)
}
@@ -801,7 +801,7 @@
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, " +
"colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
- "interpolated=true), persistencePolicy=0, displayPolicy=0)"
+ "interpolated=true), persistencePolicy=0, displayPolicy=0, fallback=null)"
)
}
@@ -874,7 +874,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
}
@@ -943,7 +943,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
}
@@ -983,7 +983,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
}
@@ -1023,7 +1023,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
}
@@ -1094,7 +1094,7 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
}
@@ -1227,7 +1227,8 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+ "displayPolicy=0, fallback=null), " +
+ "tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"persistencePolicy=0, displayPolicy=0)"
@@ -1279,7 +1280,8 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+ "displayPolicy=0, fallback=null), " +
+ "tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"persistencePolicy=0, displayPolicy=0)"
@@ -1340,7 +1342,8 @@
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, " +
"persistencePolicy=0, " +
- "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+ "displayPolicy=0, fallback=null), " +
+ "tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"persistencePolicy=0, displayPolicy=0)"
@@ -1401,7 +1404,7 @@
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, " +
"colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
- "interpolated=false), persistencePolicy=0, displayPolicy=0), " +
+ "interpolated=false), persistencePolicy=0, displayPolicy=0, fallback=null), " +
"tapActionLostDueToSerialization=false, " +
"tapAction=null, validTimeRange=TimeRange(startDateTimeMillis=" +
"-1000000000-01-01T00:00:00Z, " +
@@ -1466,7 +1469,8 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+ "displayPolicy=0, fallback=null), " +
+ "tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"persistencePolicy=0, displayPolicy=0)"
@@ -1532,7 +1536,8 @@
"dataSource=ComponentInfo{com.pkg_a/com.a}, " +
"colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], " +
"interpolated=true), persistencePolicy=0, " +
- "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+ "displayPolicy=0, fallback=null), " +
+ "tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"persistencePolicy=0, displayPolicy=0)"
@@ -1583,7 +1588,8 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+ "displayPolicy=0, fallback=null), " +
+ "tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"persistencePolicy=0, displayPolicy=0)"
@@ -1635,7 +1641,8 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+ "displayPolicy=0, fallback=null), " +
+ "tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"persistencePolicy=0, displayPolicy=0)"
@@ -1686,7 +1693,8 @@
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
- "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+ "displayPolicy=0, fallback=null), " +
+ "tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
"endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
"persistencePolicy=0, displayPolicy=0)"
@@ -2856,7 +2864,7 @@
"mSurroundingText=REDACTED, mTimeDependentText=null, mExpression=null}, " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
assertThat(data.asWireComplicationData().toString())
.isEqualTo("ComplicationData{mType=3, mFields=REDACTED}")
@@ -2882,7 +2890,7 @@
"mSurroundingText=REDACTED, mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
assertThat(data.asWireComplicationData().toString())
.isEqualTo("ComplicationData{mType=4, mFields=REDACTED}")
@@ -2911,7 +2919,7 @@
"ComplicationText{mSurroundingText=REDACTED, mTimeDependentText=null, " +
"mExpression=null}), tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(REDACTED), dataSource=null, colorRamp=null, " +
- "persistencePolicy=0, displayPolicy=0)"
+ "persistencePolicy=0, displayPolicy=0, fallback=null)"
)
assertThat(data.asWireComplicationData().toString())
.isEqualTo("ComplicationData{mType=5, mFields=REDACTED}")
@@ -2940,7 +2948,7 @@
"tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
"TimeRange(REDACTED), dataSource=null, colorRamp=ColorRamp(colors=[-65536, " +
"-16711936, -16776961], interpolated=true), persistencePolicy=0, " +
- "displayPolicy=0)"
+ "displayPolicy=0, fallback=null)"
)
assertThat(data.asWireComplicationData().toString())
.isEqualTo("ComplicationData{mType=13, mFields=REDACTED}")
@@ -2967,7 +2975,8 @@
"mTimeDependentText=null, mExpression=null}), " +
"tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
- "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+ "displayPolicy=0, fallback=null), " +
+ "tapActionLostDueToSerialization=false, tapAction=null, " +
"validTimeRange=TimeRange(REDACTED), persistencePolicy=0, displayPolicy=0)"
)
assertThat(data.asWireComplicationData().toString())
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 d777b03..32cdbdc 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
@@ -1835,6 +1835,7 @@
EditorService.globalEditorService.unregisterObserver(observerId)
}
+ @SdkSuppress(maxSdkVersion = 32) // b/275361339
@Test
@Suppress("Deprecation") // userStyleSettings
public fun commit_headless() {
@@ -1886,6 +1887,7 @@
EditorService.globalEditorService.unregisterObserver(observerId)
}
+ @SdkSuppress(maxSdkVersion = 32) // b/275361339
@SuppressLint("NewApi")
@Suppress("Deprecation") // userStyleSettings
@Test
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 8c7e17e..eafefe5 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,6 +21,7 @@
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
@@ -38,6 +39,7 @@
InteractiveInstanceManager.setParameterlessEngine(null)
}
+ @SdkSuppress(maxSdkVersion = 32) // b/275361339
@Test
fun measuresWatchFaceIconsFromCustomContext() {
val context: Context = ApplicationProvider.getApplicationContext()
diff --git a/wear/wear-phone-interactions/src/main/stableAidlImports/android/os/Bundle.aidl b/wear/wear-phone-interactions/src/main/stableAidlImports/android/os/Bundle.aidl
deleted file mode 100644
index 9642d31..0000000
--- a/wear/wear-phone-interactions/src/main/stableAidlImports/android/os/Bundle.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.os;
-
-@JavaOnlyStableParcelable parcelable Bundle;
diff --git a/window/extensions/extensions/api/current.ignore b/window/extensions/extensions/api/current.ignore
deleted file mode 100644
index c3f19a2..0000000
--- a/window/extensions/extensions/api/current.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-RemovedDeprecatedMethod: androidx.window.extensions.layout.WindowLayoutComponent#addWindowLayoutInfoListener(android.content.Context, java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo>):
- Removed deprecated method androidx.window.extensions.layout.WindowLayoutComponent.addWindowLayoutInfoListener(android.content.Context,java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo>)
diff --git a/window/extensions/extensions/api/restricted_current.ignore b/window/extensions/extensions/api/restricted_current.ignore
deleted file mode 100644
index c3f19a2..0000000
--- a/window/extensions/extensions/api/restricted_current.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-RemovedDeprecatedMethod: androidx.window.extensions.layout.WindowLayoutComponent#addWindowLayoutInfoListener(android.content.Context, java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo>):
- Removed deprecated method androidx.window.extensions.layout.WindowLayoutComponent.addWindowLayoutInfoListener(android.content.Context,java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo>)
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt b/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt
deleted file mode 100644
index e8fc72a..0000000
--- a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingRuleConstructionTests.kt
+++ /dev/null
@@ -1,820 +0,0 @@
-/*
- * 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.
- */
-
-package androidx.window.embedding
-
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.graphics.Rect
-import android.os.Build
-import androidx.annotation.RequiresApi
-import androidx.test.core.app.ApplicationProvider
-import androidx.window.embedding.EmbeddingAspectRatio.Companion.ALWAYS_ALLOW
-import androidx.window.embedding.EmbeddingAspectRatio.Companion.ALWAYS_DISALLOW
-import androidx.window.embedding.EmbeddingAspectRatio.Companion.ratio
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.BOTTOM_TO_TOP
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LEFT_TO_RIGHT
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LOCALE
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.RIGHT_TO_LEFT
-import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.TOP_TO_BOTTOM
-import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
-import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
-import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW
-import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
-import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ADJACENT
-import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ALWAYS
-import androidx.window.embedding.SplitRule.FinishBehavior.Companion.NEVER
-import androidx.window.test.R
-import junit.framework.TestCase.assertNull
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertThrows
-import org.junit.Assert.assertTrue
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.Test
-
-/**
- * Tests creation of all embedding rule types via builders and from XML.
- * @see SplitPairRule
- * @see SplitRule
- * @see ActivityRule
- */
-class EmbeddingRuleConstructionTests {
- private val application = ApplicationProvider.getApplicationContext<Context>()
- private val ruleController = RuleController.getInstance(application)
- private val density = application.resources.displayMetrics.density
- private lateinit var validBounds: Rect
- private lateinit var invalidBounds: Rect
-
- @Before
- fun setUp() {
- validBounds = minValidWindowBounds()
- invalidBounds = almostValidWindowBounds()
- ruleController.clearRules()
- }
-
- /**
- * Verifies that default params are set correctly when reading {@link SplitPairRule} from XML.
- */
- @Test
- fun testDefaults_SplitPairRule_Xml() {
- val rules = RuleController
- .parseRules(application, R.xml.test_split_config_default_split_pair_rule)
- assertEquals(1, rules.size)
- val rule: SplitPairRule = rules.first() as SplitPairRule
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
- .setLayoutDirection(LOCALE)
- .build()
- assertNull(rule.tag)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
- assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
- assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
- assertEquals(NEVER, rule.finishPrimaryWithSecondary)
- assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
- assertEquals(false, rule.clearTop)
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- assertTrue(rule.checkParentBounds(density, validBounds))
- assertFalse(rule.checkParentBounds(density, invalidBounds))
- }
-
- /**
- * Verifies that params are set correctly when reading {@link SplitPairRule} from XML.
- * @see R.xml.test_split_config_custom_split_pair_rule for customized value.
- */
- @Test
- fun testCustom_SplitPairRule_Xml() {
- val rules = RuleController
- .parseRules(application, R.xml.test_split_config_custom_split_pair_rule)
- assertEquals(1, rules.size)
- val rule: SplitPairRule = rules.first() as SplitPairRule
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.1f))
- .setLayoutDirection(RIGHT_TO_LEFT)
- .build()
- assertEquals("rule2", rule.tag)
- assertEquals(123, rule.minWidthDp)
- assertEquals(456, rule.minHeightDp)
- assertEquals(789, rule.minSmallestWidthDp)
- assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
- assertEquals(ALWAYS_DISALLOW, rule.maxAspectRatioInLandscape)
- assertEquals(ALWAYS, rule.finishPrimaryWithSecondary)
- assertEquals(NEVER, rule.finishSecondaryWithPrimary)
- assertEquals(true, rule.clearTop)
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- }
-
- /** Verifies that horizontal layout are set correctly when reading [SplitPairRule] from XML. */
- @Test
- fun testHorizontalLayout_SplitPairRule_Xml() {
- val rules = RuleController
- .parseRules(application, R.xml.test_split_config_split_pair_rule_horizontal_layout)
- assertEquals(1, rules.size)
- val rule: SplitPairRule = rules.first() as SplitPairRule
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
- .setLayoutDirection(TOP_TO_BOTTOM)
- .build()
- assertEquals(TEST_TAG, rule.tag)
- assertEquals(NEVER, rule.finishPrimaryWithSecondary)
- assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
- assertEquals(false, rule.clearTop)
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- assertTrue(rule.checkParentBounds(density, validBounds))
- assertFalse(rule.checkParentBounds(density, invalidBounds))
- }
-
- /**
- * Verifies that default params are set correctly when creating {@link SplitPairRule} with a
- * builder.
- */
- @Test
- fun testDefaults_SplitPairRule_Builder() {
- val rule = SplitPairRule.Builder(HashSet()).build()
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
- .setLayoutDirection(LOCALE)
- .build()
- assertNull(rule.tag)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
- assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
- assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
- assertEquals(NEVER, rule.finishPrimaryWithSecondary)
- assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
- assertEquals(false, rule.clearTop)
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- assertTrue(rule.checkParentBounds(density, validBounds))
- assertFalse(rule.checkParentBounds(density, invalidBounds))
- }
-
- /**
- * Verifies that the params are set correctly when creating {@link SplitPairRule} with a
- * builder.
- */
- @Test
- fun test_SplitPairRule_Builder() {
- val filters = HashSet<SplitPairFilter>()
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
- .setLayoutDirection(LEFT_TO_RIGHT)
- .build()
- filters.add(
- SplitPairFilter(
- ComponentName("a", "b"),
- ComponentName("c", "d"),
- "ACTION"
- )
- )
- val rule = SplitPairRule.Builder(filters)
- .setMinWidthDp(123)
- .setMinHeightDp(456)
- .setMinSmallestWidthDp(789)
- .setMaxAspectRatioInPortrait(ratio(1.23f))
- .setMaxAspectRatioInLandscape(ratio(4.56f))
- .setFinishPrimaryWithSecondary(ADJACENT)
- .setFinishSecondaryWithPrimary(ADJACENT)
- .setClearTop(true)
- .setDefaultSplitAttributes(expectedSplitLayout)
- .setTag(TEST_TAG)
- .build()
- assertEquals(ADJACENT, rule.finishPrimaryWithSecondary)
- assertEquals(ADJACENT, rule.finishSecondaryWithPrimary)
- assertEquals(true, rule.clearTop)
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- assertEquals(TEST_TAG, rule.tag)
- assertEquals(filters, rule.filters)
- assertEquals(123, rule.minWidthDp)
- assertEquals(456, rule.minHeightDp)
- assertEquals(789, rule.minSmallestWidthDp)
- assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
- assertEquals(4.56f, rule.maxAspectRatioInLandscape.value)
- }
-
- /**
- * Verifies that illegal parameter values are not allowed when creating {@link SplitPairRule}
- * with a builder.
- */
- @Test
- fun test_SplitPairRule_Builder_illegalArguments() {
- assertThrows(IllegalArgumentException::class.java) {
- SplitPairRule.Builder(HashSet())
- .setMinWidthDp(-1)
- .setMinHeightDp(456)
- .setMinSmallestWidthDp(789)
- .build()
- }
- assertThrows(IllegalArgumentException::class.java) {
- SplitPairRule.Builder(HashSet())
- .setMinWidthDp(123)
- .setMinHeightDp(-1)
- .setMinSmallestWidthDp(789)
- .build()
- }
- assertThrows(IllegalArgumentException::class.java) {
- SplitPairRule.Builder(HashSet())
- .setMinWidthDp(123)
- .setMinHeightDp(456)
- .setMinSmallestWidthDp(-1)
- .build()
- }
- assertThrows(IllegalArgumentException::class.java) {
- SplitPairRule.Builder(HashSet())
- .setMaxAspectRatioInPortrait(ratio(-1f))
- .build()
- }
- assertThrows(IllegalArgumentException::class.java) {
- SplitPairRule.Builder(HashSet())
- .setMaxAspectRatioInLandscape(ratio(-1f))
- .build()
- }
- }
-
- /**
- * Verifies that the SplitPairRule verifies that the parent bounds satisfy
- * maxAspectRatioInPortrait.
- */
- @Test
- fun testSplitPairRule_maxAspectRatioInPortrait() {
- // Always allow split
- var rule = SplitPairRule.Builder(HashSet())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
- .build()
- var width = 100
- var height = 1000
- var bounds = Rect(0, 0, width, height)
- assertTrue(rule.checkParentBounds(density, bounds))
-
- // Always disallow split in portrait
- rule = SplitPairRule.Builder(HashSet())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_DISALLOW)
- .build()
- width = 100
- height = 101
- bounds = Rect(0, 0, width, height)
- assertFalse(rule.checkParentBounds(density, bounds))
- // Ignore if the bounds in landscape
- bounds = Rect(0, 0, height, width)
- assertTrue(rule.checkParentBounds(density, bounds))
-
- // Compare the aspect ratio in portrait
- rule = SplitPairRule.Builder(HashSet())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ratio(1.1f))
- .build()
- // Equals to the max aspect ratio
- width = 100
- height = 110
- bounds = Rect(0, 0, width, height)
- assertTrue(rule.checkParentBounds(density, bounds))
- // Greater than the max aspect ratio
- width = 100
- height = 111
- bounds = Rect(0, 0, width, height)
- assertFalse(rule.checkParentBounds(density, bounds))
- // Ignore if the bounds in landscape
- bounds = Rect(0, 0, height, width)
- assertTrue(rule.checkParentBounds(density, bounds))
- }
-
- /**
- * Verifies that the SplitPairRule verifies that the parent bounds satisfy
- * maxAspectRatioInLandscape.
- */
- @Test
- fun testSplitPairRule_maxAspectRatioInLandscape() {
- // Always allow split
- var rule = SplitPairRule.Builder(HashSet())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
- .build()
- var width = 1000
- var height = 100
- var bounds = Rect(0, 0, width, height)
- assertTrue(rule.checkParentBounds(density, bounds))
-
- // Always disallow split in landscape
- rule = SplitPairRule.Builder(HashSet())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_DISALLOW)
- .build()
- width = 101
- height = 100
- bounds = Rect(0, 0, width, height)
- assertFalse(rule.checkParentBounds(density, bounds))
- // Ignore if the bounds in portrait
- bounds = Rect(0, 0, height, width)
- assertTrue(rule.checkParentBounds(density, bounds))
-
- // Compare the aspect ratio in landscape
- rule = SplitPairRule.Builder(HashSet())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ratio(1.1f))
- .build()
- // Equals to the max aspect ratio
- width = 110
- height = 100
- bounds = Rect(0, 0, width, height)
- assertTrue(rule.checkParentBounds(density, bounds))
- // Greater than the max aspect ratio
- width = 111
- height = 100
- bounds = Rect(0, 0, width, height)
- assertFalse(rule.checkParentBounds(density, bounds))
- // Ignore if the bounds in portrait
- bounds = Rect(0, 0, height, width)
- assertTrue(rule.checkParentBounds(density, bounds))
- }
-
- /**
- * Verifies that default params are set correctly when reading {@link SplitPlaceholderRule} from
- * XML.
- */
- @Test
- fun testDefaults_SplitPlaceholderRule_Xml() {
- val rules = RuleController
- .parseRules(application, R.xml.test_split_config_default_split_placeholder_rule)
- assertEquals(1, rules.size)
- val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
- .setLayoutDirection(LOCALE)
- .build()
- assertNull(rule.tag)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
- assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
- assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
- assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
- assertEquals(false, rule.isSticky)
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- assertTrue(rule.checkParentBounds(density, validBounds))
- assertFalse(rule.checkParentBounds(density, invalidBounds))
- }
-
- /**
- * Verifies that params are set correctly when reading {@link SplitPlaceholderRule} from XML.
- * @see R.xml.test_split_config_custom_split_placeholder_rule for customized value.
- */
- @Test
- fun testCustom_SplitPlaceholderRule_Xml() {
- val rules = RuleController
- .parseRules(application, R.xml.test_split_config_custom_split_placeholder_rule)
- assertEquals(1, rules.size)
- val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.1f))
- .setLayoutDirection(RIGHT_TO_LEFT)
- .build()
- assertEquals("rule3", rule.tag)
- assertEquals(123, rule.minWidthDp)
- assertEquals(456, rule.minHeightDp)
- assertEquals(789, rule.minSmallestWidthDp)
- assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
- assertEquals(ALWAYS_DISALLOW, rule.maxAspectRatioInLandscape)
- assertEquals(ADJACENT, rule.finishPrimaryWithPlaceholder)
- assertEquals(true, rule.isSticky)
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- }
-
- /**
- * Verifies that horizontal layout are set correctly when reading [SplitPlaceholderRule]
- * from XML.
- */
- @RequiresApi(Build.VERSION_CODES.M)
- @Test
- fun testHorizontalLayout_SplitPlaceholderRule_Xml() {
- assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
- val rules = RuleController
- .parseRules(application, R.xml.test_split_config_split_placeholder_horizontal_layout)
- assertEquals(1, rules.size)
- val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
- .setLayoutDirection(BOTTOM_TO_TOP)
- .build()
- assertEquals(TEST_TAG, rule.tag)
- assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
- assertEquals(false, rule.isSticky)
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- assertTrue(rule.checkParentBounds(density, validBounds))
- assertFalse(rule.checkParentBounds(density, invalidBounds))
- }
-
- /**
- * Verifies that default params are set correctly when creating {@link SplitPlaceholderRule}
- * with a builder.
- */
- @Test
- fun testDefaults_SplitPlaceholderRule_Builder() {
- val rule = SplitPlaceholderRule.Builder(HashSet(), Intent()).build()
- assertNull(rule.tag)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
- assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
- assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
- assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
- assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
- assertEquals(false, rule.isSticky)
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
- .setLayoutDirection(LOCALE)
- .build()
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- assertTrue(rule.checkParentBounds(density, minValidWindowBounds()))
- assertFalse(rule.checkParentBounds(density, almostValidWindowBounds()))
- }
-
- /**
- * Verifies that the params are set correctly when creating {@link SplitPlaceholderRule} with a
- * builder.
- */
- @Test
- fun test_SplitPlaceholderRule_Builder() {
- val filters = HashSet<ActivityFilter>()
- filters.add(
- ActivityFilter(
- ComponentName("a", "b"),
- "ACTION"
- )
- )
- val intent = Intent("ACTION")
- val expectedSplitLayout = SplitAttributes.Builder()
- .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
- .setLayoutDirection(LEFT_TO_RIGHT)
- .build()
- val rule = SplitPlaceholderRule.Builder(filters, intent)
- .setMinWidthDp(123)
- .setMinHeightDp(456)
- .setMinSmallestWidthDp(789)
- .setMaxAspectRatioInPortrait(ratio(1.23f))
- .setMaxAspectRatioInLandscape(ratio(4.56f))
- .setFinishPrimaryWithPlaceholder(ADJACENT)
- .setSticky(true)
- .setDefaultSplitAttributes(expectedSplitLayout)
- .setTag(TEST_TAG)
- .build()
- assertEquals(ADJACENT, rule.finishPrimaryWithPlaceholder)
- assertEquals(true, rule.isSticky)
- assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
- assertEquals(filters, rule.filters)
- assertEquals(intent, rule.placeholderIntent)
- assertEquals(123, rule.minWidthDp)
- assertEquals(456, rule.minHeightDp)
- assertEquals(789, rule.minSmallestWidthDp)
- assertEquals(TEST_TAG, rule.tag)
- assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
- assertEquals(4.56f, rule.maxAspectRatioInLandscape.value)
- }
-
- /**
- * Verifies that illegal parameter values are not allowed when creating
- * {@link SplitPlaceholderRule} with a builder.
- */
- @Test
- fun test_SplitPlaceholderRule_Builder_illegalArguments() {
- assertThrows(IllegalArgumentException::class.java) {
- SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(-1)
- .setMinHeightDp(456)
- .setMinSmallestWidthDp(789)
- .build()
- }
- assertThrows(IllegalArgumentException::class.java) {
- SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(123)
- .setMinHeightDp(-1)
- .setMinSmallestWidthDp(789)
- .build()
- }
- assertThrows(IllegalArgumentException::class.java) {
- SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(123)
- .setMinHeightDp(456)
- .setMinSmallestWidthDp(-1)
- .build()
- }
- assertThrows(IllegalArgumentException::class.java) {
- SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(123)
- .setMinHeightDp(456)
- .setMinSmallestWidthDp(789)
- .setFinishPrimaryWithPlaceholder(NEVER)
- .build()
- }
- assertThrows(IllegalArgumentException::class.java) {
- SplitPairRule.Builder(HashSet())
- .setMaxAspectRatioInPortrait(ratio(-1f))
- .build()
- }
- assertThrows(IllegalArgumentException::class.java) {
- SplitPairRule.Builder(HashSet())
- .setMaxAspectRatioInLandscape(ratio(-1f))
- .build()
- }
- }
-
- /**
- * Verifies that the SplitPlaceholderRule verifies that the parent bounds satisfy
- * maxAspectRatioInPortrait.
- */
- @Test
- fun testSplitPlaceholderRule_maxAspectRatioInPortrait() {
- // Always allow split
- var rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
- .build()
- var width = 100
- var height = 1000
- var bounds = Rect(0, 0, width, height)
- assertTrue(rule.checkParentBounds(density, bounds))
-
- // Always disallow split in portrait
- rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_DISALLOW)
- .build()
- width = 100
- height = 101
- bounds = Rect(0, 0, width, height)
- assertFalse(rule.checkParentBounds(density, bounds))
- // Ignore if the bounds in landscape
- bounds = Rect(0, 0, height, width)
- assertTrue(rule.checkParentBounds(density, bounds))
-
- // Compare the aspect ratio in portrait
- rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ratio(1.1f))
- .build()
- // Equals to the max aspect ratio
- width = 100
- height = 110
- bounds = Rect(0, 0, width, height)
- assertTrue(rule.checkParentBounds(density, bounds))
- // Greater than the max aspect ratio
- width = 100
- height = 111
- bounds = Rect(0, 0, width, height)
- assertFalse(rule.checkParentBounds(density, bounds))
- // Ignore if the bounds in landscape
- bounds = Rect(0, 0, height, width)
- assertTrue(rule.checkParentBounds(density, bounds))
- }
-
- /**
- * Verifies that the SplitPlaceholderRule verifies that the parent bounds satisfy
- * maxAspectRatioInLandscape.
- */
- @Test
- fun testSplitPlaceholderRule_maxAspectRatioInLandscape() {
- // Always allow split
- var rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_ALLOW)
- .build()
- var width = 1000
- var height = 100
- var bounds = Rect(0, 0, width, height)
- assertTrue(rule.checkParentBounds(density, bounds))
- // Ignore if the bounds in portrait
- bounds = Rect(0, 0, height, width)
- assertTrue(rule.checkParentBounds(density, bounds))
-
- // Always disallow split in landscape
- rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ALWAYS_DISALLOW)
- .build()
- width = 101
- height = 100
- bounds = Rect(0, 0, width, height)
- assertFalse(rule.checkParentBounds(density, bounds))
- // Ignore if the bounds in portrait
- bounds = Rect(0, 0, height, width)
- assertTrue(rule.checkParentBounds(density, bounds))
-
- // Compare the aspect ratio in landscape
- rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
- .setMinWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinHeightDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMinSmallestWidthDp(SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
- .setMaxAspectRatioInPortrait(ALWAYS_ALLOW)
- .setMaxAspectRatioInLandscape(ratio(1.1f))
- .build()
- // Equals to the max aspect ratio
- width = 110
- height = 100
- bounds = Rect(0, 0, width, height)
- assertTrue(rule.checkParentBounds(density, bounds))
- // Greater than the max aspect ratio
- width = 111
- height = 100
- bounds = Rect(0, 0, width, height)
- assertFalse(rule.checkParentBounds(density, bounds))
- // Ignore if the bounds in portrait
- bounds = Rect(0, 0, height, width)
- assertTrue(rule.checkParentBounds(density, bounds))
- }
-
- /**
- * Verifies that default params are set correctly when reading {@link ActivityRule} from XML.
- */
- @Test
- fun testDefaults_ActivityRule_Xml() {
- val rules = RuleController
- .parseRules(application, R.xml.test_split_config_default_activity_rule)
- assertEquals(1, rules.size)
- val rule: ActivityRule = rules.first() as ActivityRule
- assertNull(rule.tag)
- assertFalse(rule.alwaysExpand)
- }
-
- /**
- * Verifies that params are set correctly when reading {@link ActivityRule} from XML.
- * @see R.xml.test_split_config_custom_activity_rule for customized value.
- */
- @Test
- fun testCustom_ActivityRule_Xml() {
- val rules = RuleController
- .parseRules(application, R.xml.test_split_config_custom_activity_rule)
- assertEquals(1, rules.size)
- val rule: ActivityRule = rules.first() as ActivityRule
- assertEquals("rule1", rule.tag)
- assertTrue(rule.alwaysExpand)
- }
-
- /**
- * Verifies that [ActivityRule.tag] and [ActivityRule.alwaysExpand] are set correctly when
- * reading [ActivityRule] from XML.
- */
- @Test
- fun testSetTagAndAlwaysExpand_ActivityRule_Xml() {
- val rules = RuleController
- .parseRules(application, R.xml.test_split_config_activity_rule_with_tag)
- assertEquals(1, rules.size)
- val rule: ActivityRule = rules.first() as ActivityRule
- assertEquals(TEST_TAG, rule.tag)
- assertTrue(rule.alwaysExpand)
- }
-
- /**
- * Verifies that default params are set correctly when creating {@link ActivityRule} with a
- * builder.
- */
- @Test
- fun testDefaults_ActivityRule_Builder() {
- val rule = ActivityRule.Builder(HashSet()).build()
- assertFalse(rule.alwaysExpand)
- }
-
- /**
- * Verifies that the params are set correctly when creating {@link ActivityRule} with a builder.
- */
- @Test
- fun test_ActivityRule_Builder() {
- val filters = HashSet<ActivityFilter>()
- filters.add(
- ActivityFilter(
- ComponentName("a", "b"),
- "ACTION"
- )
- )
- val rule = ActivityRule.Builder(filters)
- .setAlwaysExpand(true)
- .setTag(TEST_TAG)
- .build()
- assertTrue(rule.alwaysExpand)
- assertEquals(TEST_TAG, rule.tag)
- assertEquals(filters, rule.filters)
- }
-
- private fun minValidWindowBounds(): Rect {
- // Get the screen's density scale
- val scale: Float = density
- // Convert the dps to pixels, based on density scale
- val minValidWidthPx = (SPLIT_MIN_DIMENSION_DP_DEFAULT * scale + 0.5f).toInt()
-
- return Rect(0, 0, minValidWidthPx, minValidWidthPx)
- }
-
- private fun almostValidWindowBounds(): Rect {
- // Get the screen's density scale
- val scale: Float = density
- // Convert the dps to pixels, based on density scale
- val minValidWidthPx = ((SPLIT_MIN_DIMENSION_DP_DEFAULT) - 1 * scale + 0.5f).toInt()
-
- return Rect(0, 0, minValidWidthPx, minValidWidthPx)
- }
-
- @Test
- fun testIllegalTag_XML() {
- assertThrows(IllegalArgumentException::class.java) {
- RuleController.parseRules(application, R.xml.test_split_config_duplicated_tag)
- }
- }
-
- @Test
- fun testReplacingRuleWithTag() {
- var rules = RuleController
- .parseRules(application, R.xml.test_split_config_activity_rule_with_tag)
- assertEquals(1, rules.size)
- var rule = rules.first()
- assertEquals(TEST_TAG, rule.tag)
- val staticRule = rule as ActivityRule
- assertTrue(staticRule.alwaysExpand)
- ruleController.setRules(rules)
-
- val filters = HashSet<ActivityFilter>()
- filters.add(
- ActivityFilter(
- ComponentName("a", "b"),
- "ACTION"
- )
- )
- val rule1 = ActivityRule.Builder(filters)
- .setAlwaysExpand(true)
- .setTag(TEST_TAG)
- .build()
- ruleController.addRule(rule1)
-
- rules = ruleController.getRules()
- assertEquals(1, rules.size)
- rule = rules.first()
- assertEquals(rule1, rule)
-
- val intent = Intent("ACTION")
- val rule2 = SplitPlaceholderRule.Builder(filters, intent)
- .setMinWidthDp(123)
- .setMinHeightDp(456)
- .setMinSmallestWidthDp(789)
- .setTag(TEST_TAG)
- .build()
-
- ruleController.addRule(rule2)
-
- rules = ruleController.getRules()
- assertEquals(1, rules.size)
- rule = rules.first()
- assertEquals(rule2, rule)
- }
-
- companion object {
- const val TEST_TAG = "test"
- }
-}
\ No newline at end of file
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt b/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt
new file mode 100644
index 0000000..2ea8eed
--- /dev/null
+++ b/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt
@@ -0,0 +1,331 @@
+/*
+ * 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.embedding
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.test.core.app.ApplicationProvider
+import androidx.window.embedding.EmbeddingAspectRatio.Companion.ALWAYS_DISALLOW
+import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.BOTTOM_TO_TOP
+import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LOCALE
+import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.RIGHT_TO_LEFT
+import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.TOP_TO_BOTTOM
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ADJACENT
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ALWAYS
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.NEVER
+import androidx.window.test.R
+import junit.framework.TestCase.assertNull
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Tests creation of all embedding rule types and from XML.
+ * @see SplitPairRule
+ * @see SplitRule
+ * @see ActivityRule
+ * @see RuleParser
+ */
+class RuleParserTests {
+ private val application = ApplicationProvider.getApplicationContext<Context>()
+ private val ruleController = RuleController.getInstance(application)
+ private val density = application.resources.displayMetrics.density
+ private lateinit var validBounds: Rect
+ private lateinit var invalidBounds: Rect
+
+ @Before
+ fun setUp() {
+ validBounds = minValidWindowBounds()
+ invalidBounds = almostValidWindowBounds()
+ ruleController.clearRules()
+ }
+
+ /**
+ * Verifies that default params are set correctly when reading {@link SplitPairRule} from XML.
+ */
+ @Test
+ fun testDefaults_SplitPairRule_Xml() {
+ val rules = RuleController
+ .parseRules(application, R.xml.test_split_config_default_split_pair_rule)
+ assertEquals(1, rules.size)
+ val rule: SplitPairRule = rules.first() as SplitPairRule
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
+ .setLayoutDirection(LOCALE)
+ .build()
+ assertNull(rule.tag)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
+ assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
+ assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
+ assertEquals(NEVER, rule.finishPrimaryWithSecondary)
+ assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
+ assertEquals(false, rule.clearTop)
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ assertTrue(rule.checkParentBounds(density, validBounds))
+ assertFalse(rule.checkParentBounds(density, invalidBounds))
+ }
+
+ /**
+ * Verifies that params are set correctly when reading {@link SplitPairRule} from XML.
+ * @see R.xml.test_split_config_custom_split_pair_rule for customized value.
+ */
+ @Test
+ fun testCustom_SplitPairRule_Xml() {
+ val rules = RuleController
+ .parseRules(application, R.xml.test_split_config_custom_split_pair_rule)
+ assertEquals(1, rules.size)
+ val rule: SplitPairRule = rules.first() as SplitPairRule
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.1f))
+ .setLayoutDirection(RIGHT_TO_LEFT)
+ .build()
+ assertEquals("rule2", rule.tag)
+ assertEquals(123, rule.minWidthDp)
+ assertEquals(456, rule.minHeightDp)
+ assertEquals(789, rule.minSmallestWidthDp)
+ assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
+ assertEquals(ALWAYS_DISALLOW, rule.maxAspectRatioInLandscape)
+ assertEquals(ALWAYS, rule.finishPrimaryWithSecondary)
+ assertEquals(NEVER, rule.finishSecondaryWithPrimary)
+ assertEquals(true, rule.clearTop)
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ }
+
+ /** Verifies that horizontal layout are set correctly when reading [SplitPairRule] from XML. */
+ @Test
+ fun testHorizontalLayout_SplitPairRule_Xml() {
+ val rules = RuleController
+ .parseRules(application, R.xml.test_split_config_split_pair_rule_horizontal_layout)
+ assertEquals(1, rules.size)
+ val rule: SplitPairRule = rules.first() as SplitPairRule
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
+ .setLayoutDirection(TOP_TO_BOTTOM)
+ .build()
+ assertEquals(TEST_TAG, rule.tag)
+ assertEquals(NEVER, rule.finishPrimaryWithSecondary)
+ assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
+ assertEquals(false, rule.clearTop)
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ assertTrue(rule.checkParentBounds(density, validBounds))
+ assertFalse(rule.checkParentBounds(density, invalidBounds))
+ }
+
+ /**
+ * Verifies that default params are set correctly when reading {@link SplitPlaceholderRule} from
+ * XML.
+ */
+ @Test
+ fun testDefaults_SplitPlaceholderRule_Xml() {
+ val rules = RuleController
+ .parseRules(application, R.xml.test_split_config_default_split_placeholder_rule)
+ assertEquals(1, rules.size)
+ val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
+ .setLayoutDirection(LOCALE)
+ .build()
+ assertNull(rule.tag)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
+ assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
+ assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
+ assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
+ assertEquals(false, rule.isSticky)
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ assertTrue(rule.checkParentBounds(density, validBounds))
+ assertFalse(rule.checkParentBounds(density, invalidBounds))
+ }
+
+ /**
+ * Verifies that params are set correctly when reading {@link SplitPlaceholderRule} from XML.
+ * @see R.xml.test_split_config_custom_split_placeholder_rule for customized value.
+ */
+ @Test
+ fun testCustom_SplitPlaceholderRule_Xml() {
+ val rules = RuleController
+ .parseRules(application, R.xml.test_split_config_custom_split_placeholder_rule)
+ assertEquals(1, rules.size)
+ val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.1f))
+ .setLayoutDirection(RIGHT_TO_LEFT)
+ .build()
+ assertEquals("rule3", rule.tag)
+ assertEquals(123, rule.minWidthDp)
+ assertEquals(456, rule.minHeightDp)
+ assertEquals(789, rule.minSmallestWidthDp)
+ assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
+ assertEquals(ALWAYS_DISALLOW, rule.maxAspectRatioInLandscape)
+ assertEquals(ADJACENT, rule.finishPrimaryWithPlaceholder)
+ assertEquals(true, rule.isSticky)
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ }
+
+ /**
+ * Verifies that horizontal layout are set correctly when reading [SplitPlaceholderRule]
+ * from XML.
+ */
+ @RequiresApi(Build.VERSION_CODES.M)
+ @Test
+ fun testHorizontalLayout_SplitPlaceholderRule_Xml() {
+ assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ val rules = RuleController
+ .parseRules(application, R.xml.test_split_config_split_placeholder_horizontal_layout)
+ assertEquals(1, rules.size)
+ val rule: SplitPlaceholderRule = rules.first() as SplitPlaceholderRule
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
+ .setLayoutDirection(BOTTOM_TO_TOP)
+ .build()
+ assertEquals(TEST_TAG, rule.tag)
+ assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
+ assertEquals(false, rule.isSticky)
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ assertTrue(rule.checkParentBounds(density, validBounds))
+ assertFalse(rule.checkParentBounds(density, invalidBounds))
+ }
+
+ /**
+ * Verifies that default params are set correctly when reading {@link ActivityRule} from XML.
+ */
+ @Test
+ fun testDefaults_ActivityRule_Xml() {
+ val rules = RuleController
+ .parseRules(application, R.xml.test_split_config_default_activity_rule)
+ assertEquals(1, rules.size)
+ val rule: ActivityRule = rules.first() as ActivityRule
+ assertNull(rule.tag)
+ assertFalse(rule.alwaysExpand)
+ }
+
+ /**
+ * Verifies that params are set correctly when reading {@link ActivityRule} from XML.
+ * @see R.xml.test_split_config_custom_activity_rule for customized value.
+ */
+ @Test
+ fun testCustom_ActivityRule_Xml() {
+ val rules = RuleController
+ .parseRules(application, R.xml.test_split_config_custom_activity_rule)
+ assertEquals(1, rules.size)
+ val rule: ActivityRule = rules.first() as ActivityRule
+ assertEquals("rule1", rule.tag)
+ assertTrue(rule.alwaysExpand)
+ }
+
+ /**
+ * Verifies that [ActivityRule.tag] and [ActivityRule.alwaysExpand] are set correctly when
+ * reading [ActivityRule] from XML.
+ */
+ @Test
+ fun testSetTagAndAlwaysExpand_ActivityRule_Xml() {
+ val rules = RuleController
+ .parseRules(application, R.xml.test_split_config_activity_rule_with_tag)
+ assertEquals(1, rules.size)
+ val rule: ActivityRule = rules.first() as ActivityRule
+ assertEquals(TEST_TAG, rule.tag)
+ assertTrue(rule.alwaysExpand)
+ }
+
+ private fun minValidWindowBounds(): Rect {
+ // Get the screen's density scale
+ val scale: Float = density
+ // Convert the dps to pixels, based on density scale
+ val minValidWidthPx = (SPLIT_MIN_DIMENSION_DP_DEFAULT * scale + 0.5f).toInt()
+
+ return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+ }
+
+ private fun almostValidWindowBounds(): Rect {
+ // Get the screen's density scale
+ val scale: Float = density
+ // Convert the dps to pixels, based on density scale
+ val minValidWidthPx = ((SPLIT_MIN_DIMENSION_DP_DEFAULT) - 1 * scale + 0.5f).toInt()
+
+ return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+ }
+
+ @Test
+ fun testIllegalTag_XML() {
+ assertThrows(IllegalArgumentException::class.java) {
+ RuleController.parseRules(application, R.xml.test_split_config_duplicated_tag)
+ }
+ }
+
+ @Test
+ fun testReplacingRuleWithTag() {
+ var rules = RuleController
+ .parseRules(application, R.xml.test_split_config_activity_rule_with_tag)
+ assertEquals(1, rules.size)
+ var rule = rules.first()
+ assertEquals(TEST_TAG, rule.tag)
+ val staticRule = rule as ActivityRule
+ assertTrue(staticRule.alwaysExpand)
+ ruleController.setRules(rules)
+
+ val filters = HashSet<ActivityFilter>()
+ filters.add(
+ ActivityFilter(
+ ComponentName("a", "b"),
+ "ACTION"
+ )
+ )
+ val rule1 = ActivityRule.Builder(filters)
+ .setAlwaysExpand(true)
+ .setTag(TEST_TAG)
+ .build()
+ ruleController.addRule(rule1)
+
+ rules = ruleController.getRules()
+ assertEquals(1, rules.size)
+ rule = rules.first()
+ assertEquals(rule1, rule)
+
+ val intent = Intent("ACTION")
+ val rule2 = SplitPlaceholderRule.Builder(filters, intent)
+ .setMinWidthDp(123)
+ .setMinHeightDp(456)
+ .setMinSmallestWidthDp(789)
+ .setTag(TEST_TAG)
+ .build()
+
+ ruleController.addRule(rule2)
+
+ rules = ruleController.getRules()
+ assertEquals(1, rules.size)
+ rule = rules.first()
+ assertEquals(rule2, rule)
+ }
+
+ companion object {
+ const val TEST_TAG = "test"
+ }
+}
\ No newline at end of file
diff --git a/window/window/src/test/java/androidx/window/embedding/ActivityRuleTest.kt b/window/window/src/test/java/androidx/window/embedding/ActivityRuleTest.kt
index aa266a3..8af21e3 100644
--- a/window/window/src/test/java/androidx/window/embedding/ActivityRuleTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/ActivityRuleTest.kt
@@ -16,12 +16,16 @@
package androidx.window.embedding
+import android.content.ComponentName
import androidx.window.core.ActivityComponentInfo
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+@RunWith(RobolectricTestRunner::class)
class ActivityRuleTest {
@Test
@@ -60,7 +64,39 @@
assertEquals(firstRule.hashCode(), secondRule.hashCode())
}
+ /**
+ * Verifies that default params are set correctly when creating [ActivityRule] with a
+ * builder.
+ */
+ @Test
+ fun testDefaults_ActivityRule_Builder() {
+ val rule = ActivityRule.Builder(HashSet()).build()
+ assertFalse(rule.alwaysExpand)
+ }
+
+ /**
+ * Verifies that the params are set correctly when creating [ActivityRule] with a builder.
+ */
+ @Test
+ fun test_ActivityRule_Builder() {
+ val filters = HashSet<ActivityFilter>()
+ filters.add(
+ ActivityFilter(
+ ComponentName("a", "b"),
+ "ACTION"
+ )
+ )
+ val rule = ActivityRule.Builder(filters)
+ .setAlwaysExpand(true)
+ .setTag(TEST_TAG)
+ .build()
+ assertTrue(rule.alwaysExpand)
+ assertEquals(TEST_TAG, rule.tag)
+ assertEquals(filters, rule.filters)
+ }
+
companion object {
+ private const val TEST_TAG = "test"
val FILTER_WITH_ACTIVITY = ActivityFilter(
ActivityComponentInfo("package", "className"),
null
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt
index e8b48a2..d02808d 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt
@@ -16,10 +16,27 @@
package androidx.window.embedding
+import android.content.ComponentName
+import android.graphics.Rect
import androidx.window.core.ActivityComponentInfo
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ALWAYS
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.NEVER
+import junit.framework.TestCase
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+/**
+ * Unit test for [SplitPairRule] to check the construction is correct by using it's builder.
+ */
+@RunWith(RobolectricTestRunner::class)
internal class SplitPairRuleTest {
@Test
@@ -43,4 +60,270 @@
null
)
}
+
+ /*------------------------------Class Test------------------------------*/
+ /**
+ * Test hashcode and equals are properly calculated for 2 equal [SplitPairRule]
+ */
+ @Test
+ fun equalsImpliesHashCode() {
+ val filter = SplitPairFilter(
+ ComponentName("a", "b"),
+ ComponentName("c", "d"),
+ "ACTION"
+ )
+ val firstRule = SplitPairRule.Builder(setOf(filter)).build()
+ val secondRule = SplitPairRule.Builder(setOf(filter)).build()
+ assertEquals(firstRule, secondRule)
+ assertEquals(firstRule.hashCode(), secondRule.hashCode())
+ }
+
+ /*------------------------------Builder Test------------------------------*/
+ /**
+ * Verifies that default params are set correctly when creating [SplitPairRule] with a
+ * builder.
+ */
+ @Test
+ fun testDefaults_SplitPairRule_Builder() {
+ val rule = SplitPairRule.Builder(HashSet()).build()
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+ .build()
+ TestCase.assertNull(rule.tag)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
+ assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
+ assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
+ assertEquals(NEVER, rule.finishPrimaryWithSecondary)
+ assertEquals(ALWAYS, rule.finishSecondaryWithPrimary)
+ assertEquals(false, rule.clearTop)
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ assertTrue(rule.checkParentBounds(density, validBounds))
+ assertFalse(rule.checkParentBounds(density, invalidBounds))
+ }
+
+ /**
+ * Verifies that the params are set correctly when creating [SplitPairRule] with a
+ * builder.
+ */
+ @Test
+ fun test_SplitPairRule_Builder() {
+ val filters = HashSet<SplitPairFilter>()
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+ .build()
+ filters.add(
+ SplitPairFilter(
+ ComponentName("a", "b"),
+ ComponentName("c", "d"),
+ "ACTION"
+ )
+ )
+ val rule = SplitPairRule.Builder(filters)
+ .setMinWidthDp(123)
+ .setMinHeightDp(456)
+ .setMinSmallestWidthDp(789)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.23f))
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(4.56f))
+ .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.ADJACENT)
+ .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ADJACENT)
+ .setClearTop(true)
+ .setDefaultSplitAttributes(expectedSplitLayout)
+ .setTag(TEST_TAG)
+ .build()
+ assertEquals(SplitRule.FinishBehavior.ADJACENT, rule.finishPrimaryWithSecondary)
+ assertEquals(SplitRule.FinishBehavior.ADJACENT, rule.finishSecondaryWithPrimary)
+ assertEquals(true, rule.clearTop)
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ assertEquals(TEST_TAG, rule.tag)
+ assertEquals(filters, rule.filters)
+ assertEquals(123, rule.minWidthDp)
+ assertEquals(456, rule.minHeightDp)
+ assertEquals(789, rule.minSmallestWidthDp)
+ assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
+ assertEquals(4.56f, rule.maxAspectRatioInLandscape.value)
+ }
+
+ /*------------------------------Functional Test------------------------------*/
+ /**
+ * Verifies that illegal parameter values are not allowed when creating [SplitPairRule]
+ * with a builder.
+ */
+ @Test
+ fun test_SplitPairRule_Builder_illegalArguments() {
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPairRule.Builder(HashSet())
+ .setMinWidthDp(-1)
+ .setMinHeightDp(456)
+ .setMinSmallestWidthDp(789)
+ .build()
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPairRule.Builder(HashSet())
+ .setMinWidthDp(123)
+ .setMinHeightDp(-1)
+ .setMinSmallestWidthDp(789)
+ .build()
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPairRule.Builder(HashSet())
+ .setMinWidthDp(123)
+ .setMinHeightDp(456)
+ .setMinSmallestWidthDp(-1)
+ .build()
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPairRule.Builder(HashSet())
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(-1f))
+ .build()
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPairRule.Builder(HashSet())
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(-1f))
+ .build()
+ }
+ }
+
+ /**
+ * Verifies that the [SplitPairRule] verifies that the parent bounds satisfy
+ * maxAspectRatioInPortrait.
+ */
+ @Test
+ fun testSplitPairRule_maxAspectRatioInPortrait() {
+ // Always allow split
+ var rule = SplitPairRule.Builder(HashSet())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .build()
+ var width = 100
+ var height = 1000
+ var bounds = Rect(0, 0, width, height)
+ assertTrue(rule.checkParentBounds(density, bounds))
+
+ // Always disallow split in portrait
+ rule = SplitPairRule.Builder(HashSet())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_DISALLOW)
+ .build()
+ width = 100
+ height = 101
+ bounds = Rect(0, 0, width, height)
+ assertFalse(rule.checkParentBounds(density, bounds))
+ // Ignore if the bounds in landscape
+ bounds = Rect(0, 0, height, width)
+ assertTrue(rule.checkParentBounds(density, bounds))
+
+ // Compare the aspect ratio in portrait
+ rule = SplitPairRule.Builder(HashSet())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.1f))
+ .build()
+ // Equals to the max aspect ratio
+ width = 100
+ height = 110
+ bounds = Rect(0, 0, width, height)
+ assertTrue(rule.checkParentBounds(density, bounds))
+ // Greater than the max aspect ratio
+ width = 100
+ height = 111
+ bounds = Rect(0, 0, width, height)
+ assertFalse(rule.checkParentBounds(density, bounds))
+ // Ignore if the bounds in landscape
+ bounds = Rect(0, 0, height, width)
+ assertTrue(rule.checkParentBounds(density, bounds))
+ }
+
+ /**
+ * Verifies that the [SplitPairRule] verifies that the parent bounds satisfy
+ * maxAspectRatioInLandscape.
+ */
+ @Test
+ fun testSplitPairRule_maxAspectRatioInLandscape() {
+ // Always allow split
+ var rule = SplitPairRule.Builder(HashSet())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .build()
+ var width = 1000
+ var height = 100
+ var bounds = Rect(0, 0, width, height)
+ assertTrue(rule.checkParentBounds(density, bounds))
+
+ // Always disallow split in landscape
+ rule = SplitPairRule.Builder(HashSet())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_DISALLOW)
+ .build()
+ width = 101
+ height = 100
+ bounds = Rect(0, 0, width, height)
+ assertFalse(rule.checkParentBounds(density, bounds))
+ // Ignore if the bounds in portrait
+ bounds = Rect(0, 0, height, width)
+ assertTrue(rule.checkParentBounds(density, bounds))
+
+ // Compare the aspect ratio in landscape
+ rule = SplitPairRule.Builder(HashSet())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(1.1f))
+ .build()
+ // Equals to the max aspect ratio
+ width = 110
+ height = 100
+ bounds = Rect(0, 0, width, height)
+ assertTrue(rule.checkParentBounds(density, bounds))
+ // Greater than the max aspect ratio
+ width = 111
+ height = 100
+ bounds = Rect(0, 0, width, height)
+ assertFalse(rule.checkParentBounds(density, bounds))
+ // Ignore if the bounds in portrait
+ bounds = Rect(0, 0, height, width)
+ assertTrue(rule.checkParentBounds(density, bounds))
+ }
+
+ companion object {
+
+ private const val density = 2f
+ private const val TEST_TAG = "test"
+ private val validBounds: Rect = minValidWindowBounds()
+ private val invalidBounds: Rect = almostValidWindowBounds()
+
+ private fun minValidWindowBounds(): Rect {
+ // Get the screen's density scale
+ val scale: Float = density
+ // Convert the dps to pixels, based on density scale
+ val minValidWidthPx = (SPLIT_MIN_DIMENSION_DP_DEFAULT * scale + 0.5f).toInt()
+ return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+ }
+
+ private fun almostValidWindowBounds(): Rect {
+ // Get the screen's density scale
+ val scale: Float = density
+ // Convert the dps to pixels, based on density scale
+ val minValidWidthPx = ((SPLIT_MIN_DIMENSION_DP_DEFAULT) - 1 * scale + 0.5f).toInt()
+ return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+ }
+ }
}
\ No newline at end of file
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt
new file mode 100644
index 0000000..0fa21d3
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt
@@ -0,0 +1,314 @@
+/*
+ * 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.embedding
+
+import android.content.ComponentName
+import android.content.Intent
+import android.graphics.Rect
+import androidx.window.core.ActivityComponentInfo
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.ALWAYS
+import androidx.window.embedding.SplitRule.FinishBehavior.Companion.NEVER
+import junit.framework.TestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+/**
+ * Unit test for [SplitPlaceholderRule] to check the construction is correct by using it's builder.
+ */
+@RunWith(RobolectricTestRunner::class)
+internal class SplitPlaceHolderRuleTest {
+
+ /*------------------------------Class Test------------------------------*/
+ /**
+ * Test hashcode and equals are properly calculated for 2 equal [SplitPlaceholderRule]
+ */
+ @Test
+ fun equalsImpliesHashCode() {
+ val filter = ActivityFilter(ActivityComponentInfo("a", "b"), "ACTION")
+ val placeholderIntent = Intent()
+ val firstRule = SplitPlaceholderRule.Builder(setOf(filter), placeholderIntent).build()
+ val secondRule = SplitPlaceholderRule.Builder(setOf(filter), placeholderIntent).build()
+ assertEquals(firstRule, secondRule)
+ assertEquals(firstRule.hashCode(), secondRule.hashCode())
+ }
+
+ /*------------------------------Builder Test------------------------------*/
+ /**
+ * Verifies that default params are set correctly when creating [SplitPlaceholderRule]
+ * with a builder.
+ */
+ @Test
+ fun testDefaults_SplitPlaceholderRule_Builder() {
+ val rule = SplitPlaceholderRule.Builder(HashSet(), Intent()).build()
+ TestCase.assertNull(rule.tag)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minHeightDp)
+ assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minSmallestWidthDp)
+ assertEquals(SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT, rule.maxAspectRatioInPortrait)
+ assertEquals(SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT, rule.maxAspectRatioInLandscape)
+ assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
+ assertEquals(false, rule.isSticky)
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+ .build()
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ assertTrue(rule.checkParentBounds(density, validBounds))
+ assertFalse(rule.checkParentBounds(density, invalidBounds))
+ }
+
+ /**
+ * Verifies that the params are set correctly when creating [SplitPlaceholderRule] with a
+ * builder.
+ */
+ @Test
+ fun test_SplitPlaceholderRule_Builder() {
+ val filters = HashSet<ActivityFilter>()
+ filters.add(
+ ActivityFilter(
+ ComponentName("a", "b"),
+ "ACTION"
+ )
+ )
+ val intent = Intent("ACTION")
+ val expectedSplitLayout = SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+ .build()
+ val rule = SplitPlaceholderRule.Builder(filters, intent)
+ .setMinWidthDp(123)
+ .setMinHeightDp(456)
+ .setMinSmallestWidthDp(789)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.23f))
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(4.56f))
+ .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ADJACENT)
+ .setSticky(true)
+ .setDefaultSplitAttributes(expectedSplitLayout)
+ .setTag(TEST_TAG)
+ .build()
+ assertEquals(SplitRule.FinishBehavior.ADJACENT, rule.finishPrimaryWithPlaceholder)
+ assertEquals(true, rule.isSticky)
+ assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
+ assertEquals(filters, rule.filters)
+ assertEquals(intent, rule.placeholderIntent)
+ assertEquals(123, rule.minWidthDp)
+ assertEquals(456, rule.minHeightDp)
+ assertEquals(789, rule.minSmallestWidthDp)
+ assertEquals(TEST_TAG, rule.tag)
+ assertEquals(1.23f, rule.maxAspectRatioInPortrait.value)
+ assertEquals(4.56f, rule.maxAspectRatioInLandscape.value)
+ }
+
+ /**
+ * Verifies that illegal parameter values are not allowed when creating
+ * [SplitPlaceholderRule] with a builder.
+ */
+ @Test
+ fun test_SplitPlaceholderRule_Builder_illegalArguments() {
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(-1)
+ .setMinHeightDp(456)
+ .setMinSmallestWidthDp(789)
+ .build()
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(123)
+ .setMinHeightDp(-1)
+ .setMinSmallestWidthDp(789)
+ .build()
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(123)
+ .setMinHeightDp(456)
+ .setMinSmallestWidthDp(-1)
+ .build()
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(123)
+ .setMinHeightDp(456)
+ .setMinSmallestWidthDp(789)
+ .setFinishPrimaryWithPlaceholder(NEVER)
+ .build()
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPairRule.Builder(HashSet())
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(-1f))
+ .build()
+ }
+ assertThrows(IllegalArgumentException::class.java) {
+ SplitPairRule.Builder(HashSet())
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(-1f))
+ .build()
+ }
+ }
+
+ /*------------------------------Functional Test------------------------------*/
+ /**
+ * Verifies that the [SplitPlaceholderRule] verifies that the parent bounds satisfy
+ * maxAspectRatioInPortrait.
+ */
+ @Test
+ fun testSplitPlaceholderRule_maxAspectRatioInPortrait() {
+ // Always allow split
+ var rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .build()
+ var width = 100
+ var height = 1000
+ var bounds = Rect(0, 0, width, height)
+ assertTrue(rule.checkParentBounds(density, bounds))
+
+ // Always disallow split in portrait
+ rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_DISALLOW)
+ .build()
+ width = 100
+ height = 101
+ bounds = Rect(0, 0, width, height)
+ assertFalse(rule.checkParentBounds(density, bounds))
+ // Ignore if the bounds in landscape
+ bounds = Rect(0, 0, height, width)
+ assertTrue(rule.checkParentBounds(density, bounds))
+
+ // Compare the aspect ratio in portrait
+ rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.1f))
+ .build()
+ // Equals to the max aspect ratio
+ width = 100
+ height = 110
+ bounds = Rect(0, 0, width, height)
+ assertTrue(rule.checkParentBounds(density, bounds))
+ // Greater than the max aspect ratio
+ width = 100
+ height = 111
+ bounds = Rect(0, 0, width, height)
+ assertFalse(rule.checkParentBounds(density, bounds))
+ // Ignore if the bounds in landscape
+ bounds = Rect(0, 0, height, width)
+ assertTrue(rule.checkParentBounds(density, bounds))
+ }
+
+ /**
+ * Verifies that the [SplitPlaceholderRule] verifies that the parent bounds satisfy
+ * maxAspectRatioInLandscape.
+ */
+ @Test
+ fun testSplitPlaceholderRule_maxAspectRatioInLandscape() {
+ // Always allow split
+ var rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .build()
+ var width = 1000
+ var height = 100
+ var bounds = Rect(0, 0, width, height)
+ assertTrue(rule.checkParentBounds(density, bounds))
+ // Ignore if the bounds in portrait
+ bounds = Rect(0, 0, height, width)
+ assertTrue(rule.checkParentBounds(density, bounds))
+
+ // Always disallow split in landscape
+ rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_DISALLOW)
+ .build()
+ width = 101
+ height = 100
+ bounds = Rect(0, 0, width, height)
+ assertFalse(rule.checkParentBounds(density, bounds))
+ // Ignore if the bounds in portrait
+ bounds = Rect(0, 0, height, width)
+ assertTrue(rule.checkParentBounds(density, bounds))
+
+ // Compare the aspect ratio in landscape
+ rule = SplitPlaceholderRule.Builder(HashSet(), Intent())
+ .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW)
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ratio(1.1f))
+ .build()
+ // Equals to the max aspect ratio
+ width = 110
+ height = 100
+ bounds = Rect(0, 0, width, height)
+ assertTrue(rule.checkParentBounds(density, bounds))
+ // Greater than the max aspect ratio
+ width = 111
+ height = 100
+ bounds = Rect(0, 0, width, height)
+ assertFalse(rule.checkParentBounds(density, bounds))
+ // Ignore if the bounds in portrait
+ bounds = Rect(0, 0, height, width)
+ assertTrue(rule.checkParentBounds(density, bounds))
+ }
+
+ companion object {
+
+ private const val density = 2f
+ private const val TEST_TAG = "test"
+ private val validBounds: Rect = minValidWindowBounds()
+ private val invalidBounds: Rect = almostValidWindowBounds()
+
+ private fun minValidWindowBounds(): Rect {
+ // Get the screen's density scale
+ val scale: Float = density
+ // Convert the dps to pixels, based on density scale
+ val minValidWidthPx = (SPLIT_MIN_DIMENSION_DP_DEFAULT * scale + 0.5f).toInt()
+ return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+ }
+
+ private fun almostValidWindowBounds(): Rect {
+ // Get the screen's density scale
+ val scale: Float = density
+ // Convert the dps to pixels, based on density scale
+ val minValidWidthPx = ((SPLIT_MIN_DIMENSION_DP_DEFAULT) - 1 * scale + 0.5f).toInt()
+ return Rect(0, 0, minValidWidthPx, minValidWidthPx)
+ }
+ }
+}
\ No newline at end of file
diff --git a/work/work-runtime/api/current.ignore b/work/work-runtime/api/current.ignore
index 7b196e5..ecd7f87 100644
--- a/work/work-runtime/api/current.ignore
+++ b/work/work-runtime/api/current.ignore
@@ -1,14 +1,4 @@
// Baseline format: 1.0
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfoByIdFlow(java.util.UUID):
- Added method androidx.work.WorkManager.getWorkInfoByIdFlow(java.util.UUID)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosByTagFlow(String):
- Added method androidx.work.WorkManager.getWorkInfosByTagFlow(String)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosFlow(androidx.work.WorkQuery):
- Added method androidx.work.WorkManager.getWorkInfosFlow(androidx.work.WorkQuery)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosForUniqueWorkFlow(String):
- Added method androidx.work.WorkManager.getWorkInfosForUniqueWorkFlow(String)
-
-
ChangedType: androidx.work.Configuration#getInitializationExceptionHandler():
Method androidx.work.Configuration.getInitializationExceptionHandler has changed return type from androidx.core.util.Consumer<java.lang.Throwable!> to androidx.core.util.Consumer<java.lang.Throwable>
ChangedType: androidx.work.Configuration#getSchedulingExceptionHandler():
diff --git a/work/work-runtime/api/restricted_current.ignore b/work/work-runtime/api/restricted_current.ignore
index 7b196e5..ecd7f87 100644
--- a/work/work-runtime/api/restricted_current.ignore
+++ b/work/work-runtime/api/restricted_current.ignore
@@ -1,14 +1,4 @@
// Baseline format: 1.0
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfoByIdFlow(java.util.UUID):
- Added method androidx.work.WorkManager.getWorkInfoByIdFlow(java.util.UUID)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosByTagFlow(String):
- Added method androidx.work.WorkManager.getWorkInfosByTagFlow(String)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosFlow(androidx.work.WorkQuery):
- Added method androidx.work.WorkManager.getWorkInfosFlow(androidx.work.WorkQuery)
-AddedAbstractMethod: androidx.work.WorkManager#getWorkInfosForUniqueWorkFlow(String):
- Added method androidx.work.WorkManager.getWorkInfosForUniqueWorkFlow(String)
-
-
ChangedType: androidx.work.Configuration#getInitializationExceptionHandler():
Method androidx.work.Configuration.getInitializationExceptionHandler has changed return type from androidx.core.util.Consumer<java.lang.Throwable!> to androidx.core.util.Consumer<java.lang.Throwable>
ChangedType: androidx.work.Configuration#getSchedulingExceptionHandler():