Merge "Change all annotation-sampled dependencies to compileOnly." into androidx-master-dev
diff --git a/benchmark/benchmark-macro-runtime/build.gradle b/benchmark/benchmark-macro-runtime/build.gradle
index 8c8ca06..7886270 100644
--- a/benchmark/benchmark-macro-runtime/build.gradle
+++ b/benchmark/benchmark-macro-runtime/build.gradle
@@ -36,6 +36,7 @@
api(KOTLIN_STDLIB)
api(KOTLIN_COROUTINES_ANDROID)
api("androidx.annotation:annotation:1.1.0")
+ implementation(project(":benchmark:benchmark-perfetto"))
implementation(GUAVA_ANDROID)
implementation(ANDROIDX_TEST_EXT_JUNIT)
implementation(ANDROIDX_TEST_UIAUTOMATOR)
diff --git a/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/MacroBenchmarkTest.kt b/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/MacroBenchmarkTest.kt
index 155f131..0c028b6 100644
--- a/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/MacroBenchmarkTest.kt
+++ b/benchmark/benchmark-macro-runtime/src/androidTest/java/androidx/benchmark/macro/MacroBenchmarkTest.kt
@@ -42,7 +42,11 @@
@LargeTest
@Ignore("Not running the test in CI")
fun basicTest() {
- val collectors = listOf<Collector<*>>(CpuUsageCollector(), AppStartupCollector())
+ val collectors = listOf<Collector<*>>(
+ CpuUsageCollector(),
+ AppStartupCollector(),
+ PerfettoCollector("basicTest")
+ )
val loopManager = LoopManager(packageName, instrumentation, collectors)
loopManager.measureRepeated(2) { _ ->
diff --git a/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/PerfettoCollector.kt b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/PerfettoCollector.kt
new file mode 100644
index 0000000..2b0cbef
--- /dev/null
+++ b/benchmark/benchmark-macro-runtime/src/main/java/androidx/benchmark/macro/PerfettoCollector.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark.macro
+
+import android.os.Build
+import android.util.Log
+import androidx.benchmark.perfetto.PerfettoCapture
+import androidx.benchmark.perfetto.destinationPath
+import androidx.benchmark.perfetto.reportAdditionalFileToCopy
+
+/**
+ * Helps capture Perfetto traces.
+ */
+class PerfettoCollector(private val traceName: String) : Collector<Unit> {
+ private var capture: PerfettoCapture? = null
+
+ init {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ capture = PerfettoCapture()
+ }
+ }
+
+ override fun start(): Boolean {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ Log.d(TAG, "Recording perfetto trace $traceName")
+ capture?.start()
+ }
+ return true
+ }
+
+ override fun stop(): Boolean {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ val destination = destinationPath(traceName).absolutePath
+ capture?.stop(destination)
+ reportAdditionalFileToCopy("perfetto_trace", destination)
+ }
+ return true
+ }
+
+ override fun metrics(): Map<String, Unit> {
+ return emptyMap()
+ }
+
+ companion object {
+ private const val TAG = "PerfettoCollector"
+ }
+}
diff --git a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
index 82b0c02..77e056a 100644
--- a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
+++ b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
@@ -18,6 +18,7 @@
import android.content.Context
import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
import androidx.test.platform.app.InstrumentationRegistry
import java.io.File
@@ -30,8 +31,9 @@
* - App tags are not available, due to lack of `<profileable shell=true>`. Can potentially hack
* around this for individual tags within test infra as needed.
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(29)
-internal class PerfettoCapture {
+class PerfettoCapture {
private val helper = PerfettoHelper()
/**
diff --git a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt
index fa0d614..7de75b3 100644
--- a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt
+++ b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt
@@ -21,6 +21,7 @@
import android.os.Environment
import android.util.Log
import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.rules.TestRule
import org.junit.runner.Description
@@ -71,19 +72,11 @@
@RequiresApi(Build.VERSION_CODES.Q)
internal fun PerfettoCapture.recordAndReportFile(traceName: String, block: () -> Unit) {
- // NOTE: this method of getting additionalTestOutputDir duplicates behavior in
- // `androidx.benchmark.Arguments`, and should be unified at some point.
- val additionalTestOutputDir = InstrumentationRegistry.getArguments()
- .getString("additionalTestOutputDir")
- @Suppress("DEPRECATION") // Legacy code path for versions of agp older than 3.6
- val testOutputDir = additionalTestOutputDir?.let { File(it) }
- ?: Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
-
try {
Log.d(PerfettoRule.TAG, "Recording perfetto trace $traceName")
start()
block()
- val dst = File(testOutputDir, traceName)
+ val dst = destinationPath(traceName)
stop(dst.absolutePath)
reportAdditionalFileToCopy("perfetto_trace", dst.absolutePath)
} finally {
@@ -91,13 +84,29 @@
}
}
+/*
+ NOTE: this method of getting additionalTestOutputDir duplicates behavior in
+ androidx.benchmark.Arguments`, and should be unified at some point.
+*/
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun destinationPath(traceName: String): File {
+ val additionalTestOutputDir = InstrumentationRegistry.getArguments()
+ .getString("additionalTestOutputDir")
+
+ @Suppress("DEPRECATION") // Legacy code path for versions of agp older than 3.6
+ val testOutputDir = additionalTestOutputDir?.let { File(it) }
+ ?: Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ return File(testOutputDir, traceName)
+}
+
/**
* Report additional file to be copied.
*
* Note that this is a temporary reimplementation of
* `InstrumentationResults.reportAdditionalFileToCopy`, and should be unified at some point.
*/
-private fun reportAdditionalFileToCopy(
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun reportAdditionalFileToCopy(
@Suppress("SameParameterValue") key: String,
absoluteFilePath: String
) {
@@ -107,4 +116,4 @@
InstrumentationRegistry
.getInstrumentation()
.sendStatus(2, bundle)
-}
\ No newline at end of file
+}
diff --git a/biometric/biometric/src/androidTest/java/androidx/biometric/DeviceUtilsTest.java b/biometric/biometric/src/androidTest/java/androidx/biometric/DeviceUtilsTest.java
index 508f1b3..791d38f 100644
--- a/biometric/biometric/src/androidTest/java/androidx/biometric/DeviceUtilsTest.java
+++ b/biometric/biometric/src/androidTest/java/androidx/biometric/DeviceUtilsTest.java
@@ -112,19 +112,23 @@
@Test
public void testCanAssumeStrongBiometrics() {
- final String[] modelPrefixes = {"foo", "bar"};
+ final String[] modelNames = {"S", "flame", "My phone"};
when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getStringArray(R.array.assume_strong_biometrics_prefixes))
- .thenReturn(modelPrefixes);
+ when(mResources.getStringArray(R.array.assume_strong_biometrics_models))
+ .thenReturn(modelNames);
final boolean isPreApi30 = Build.VERSION.SDK_INT < Build.VERSION_CODES.R;
- assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "foo")).isEqualTo(isPreApi30);
- assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "bar")).isEqualTo(isPreApi30);
- assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "foobar")).isEqualTo(isPreApi30);
- assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "bar123")).isEqualTo(isPreApi30);
- assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "baz")).isFalse();
- assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "abcxyz")).isFalse();
- assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "bazfoo")).isFalse();
- assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "FooBar")).isFalse();
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "S")).isEqualTo(isPreApi30);
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "flame")).isEqualTo(isPreApi30);
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "My phone"))
+ .isEqualTo(isPreApi30);
+
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "s")).isFalse();
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "Y")).isFalse();
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "Flame")).isFalse();
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "coral")).isFalse();
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "My Phone")).isFalse();
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "Myphone")).isFalse();
+ assertThat(DeviceUtils.canAssumeStrongBiometrics(mContext, "My phone2")).isFalse();
}
}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/DeviceUtils.java b/biometric/biometric/src/main/java/androidx/biometric/DeviceUtils.java
index caf16d5..077c509 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/DeviceUtils.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/DeviceUtils.java
@@ -96,7 +96,7 @@
// Android 11 (API 30) and above may downgrade a sensor's security class at runtime.
return false;
}
- return isModelInPrefixList(context, model, R.array.assume_strong_biometrics_prefixes);
+ return isModelInList(context, model, R.array.assume_strong_biometrics_models);
}
/**
@@ -106,7 +106,7 @@
* @param context The application or activity context.
* @param vendor Case-insensitive name of the device vendor.
* @param resId Resource ID for the string array of vendor names to check against.
- * @return Whether the vendor name matches one in the given string array.
+ * @return Whether the vendor name matches one in the string array.
*/
private static boolean isVendorInList(@NonNull Context context, String vendor, int resId) {
if (vendor == null) {
@@ -128,7 +128,7 @@
* @param context The application or activity context.
* @param model Model name of the current device.
* @param resId Resource ID for the string array of device model prefixes to check against.
- * @return Whether the model matches one in the given string array.
+ * @return Whether the model matches a prefix in the string array.
*/
private static boolean isModelInPrefixList(@NonNull Context context, String model, int resId) {
if (model == null) {
@@ -150,7 +150,7 @@
* @param context The application or activity context.
* @param model Model name of the current device.
* @param resId Resource ID for the string array of device model prefixes to check against.
- * @return Whether the model matches one in the given string array.
+ * @return Whether the model matches one in the string array.
*/
private static boolean isModelInList(@NonNull Context context, String model, int resId) {
if (model == null) {
diff --git a/biometric/biometric/src/main/res/values/devices.xml b/biometric/biometric/src/main/res/values/devices.xml
index 4a7c2ac..2089d3f 100644
--- a/biometric/biometric/src/main/res/values/devices.xml
+++ b/biometric/biometric/src/main/res/values/devices.xml
@@ -78,6 +78,8 @@
List of known device models for which all biometric sensors checked by
BiometricManager#canAuthenticate() meet or exceed the Class 3 (Strong) security threshold.
-->
- <string-array name="assume_strong_biometrics_prefixes">
+ <string-array name="assume_strong_biometrics_models">
+ <item>Pixel 4</item>
+ <item>Pixel 4 XL</item>
</string-array>
</resources>
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index d5fa477..9a24321 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -104,7 +104,7 @@
}
public final class Composer<N> {
- ctor public Composer(androidx.compose.runtime.SlotTable slotTable, @androidx.compose.runtime.ComposeCompilerApi androidx.compose.runtime.Applier<N> applier, @androidx.compose.runtime.ExperimentalComposeApi androidx.compose.runtime.Recomposer recomposer);
+ ctor public Composer(androidx.compose.runtime.SlotTable slotTable, @androidx.compose.runtime.ComposeCompilerApi androidx.compose.runtime.Applier<N> applier, internal androidx.compose.runtime.CompositionReference parentReference);
method @androidx.compose.runtime.InternalComposeApi public void applyChanges();
method @androidx.compose.runtime.ComposeCompilerApi public inline <T> T! cache(boolean invalid, kotlin.jvm.functions.Function0<? extends T> block);
method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(Object? value);
@@ -129,7 +129,6 @@
method public int getCurrentCompoundKeyHash();
method public boolean getDefaultsInvalid();
method public boolean getInserting();
- method public androidx.compose.runtime.Recomposer getRecomposer();
method public boolean getSkipping();
method public androidx.compose.runtime.SlotTable getSlotTable();
method @androidx.compose.runtime.ComposeCompilerApi public Object joinKey(Object? left, Object? right);
@@ -153,7 +152,6 @@
property public final int currentCompoundKeyHash;
property public final boolean defaultsInvalid;
property public final boolean inserting;
- property public final androidx.compose.runtime.Recomposer recomposer;
property public final boolean skipping;
property public final androidx.compose.runtime.SlotTable slotTable;
}
@@ -168,10 +166,6 @@
method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
- @Deprecated public interface CompositionCoroutineScope extends kotlinx.coroutines.CoroutineScope {
- method @Deprecated public default suspend Object? awaitFrame(kotlin.coroutines.Continuation<? super java.lang.Long> p);
- }
-
@Deprecated public interface CompositionFrameClock extends androidx.compose.runtime.dispatch.MonotonicFrameClock {
method @Deprecated public default suspend <R> Object? awaitFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
}
@@ -184,7 +178,7 @@
}
public final class CompositionKt {
- method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition compositionFor(Object key, androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.Recomposer recomposer, optional androidx.compose.runtime.CompositionReference? parent, optional kotlin.jvm.functions.Function0<kotlin.Unit> onCreated);
+ method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition compositionFor(Object key, androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionReference parent, optional kotlin.jvm.functions.Function0<kotlin.Unit> onCreated);
}
public interface CompositionLifecycleObserver {
@@ -195,6 +189,9 @@
public abstract class CompositionReference {
}
+ public final class CompositionReferenceKt {
+ }
+
public final class DerivedStateKt {
method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
}
@@ -352,18 +349,24 @@
property public final T! value;
}
- public final class Recomposer {
+ public final class Recomposer extends androidx.compose.runtime.CompositionReference {
ctor public Recomposer(androidx.compose.runtime.EmbeddingContext embeddingContext);
ctor public Recomposer();
method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public void composeInitial$metalava_module(androidx.compose.runtime.Composer<?> composer, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public kotlin.coroutines.CoroutineContext? getApplyingCoroutineContext$metalava_module();
+ method public boolean getCollectingKeySources$metalava_module();
+ method public int getCompoundHashKey$metalava_module();
method public androidx.compose.runtime.EmbeddingContext getEmbeddingContext();
- method public androidx.compose.runtime.dispatch.MonotonicFrameClock getFrameClock();
method public boolean hasInvalidations();
+ method public void invalidate$metalava_module(androidx.compose.runtime.Composer<?> composer);
method public suspend Object? recomposeAndApplyChanges(long frameCount, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public void recordInspectionTable$metalava_module(java.util.Set<androidx.compose.runtime.SlotTable> table);
method public suspend Object? runRecomposeAndApplyChanges(kotlin.coroutines.Continuation<?> p);
- method public void setEmbeddingContext(androidx.compose.runtime.EmbeddingContext p);
+ property public kotlin.coroutines.CoroutineContext? applyingCoroutineContext;
+ property public boolean collectingKeySources;
+ property public int compoundHashKey;
property public final androidx.compose.runtime.EmbeddingContext embeddingContext;
- property public final androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock;
field public static final androidx.compose.runtime.Recomposer.Companion Companion;
}
@@ -565,12 +568,6 @@
method @androidx.compose.runtime.Composable public static void LaunchedTask(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
method @androidx.compose.runtime.Composable public static void LaunchedTask(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
method @androidx.compose.runtime.Composable public static void LaunchedTask(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated public static suspend Object? awaitDispose(androidx.compose.runtime.CompositionCoroutineScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDispose, optional kotlin.coroutines.Continuation<?> p);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object? key, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
method @androidx.compose.runtime.Composable public static inline kotlinx.coroutines.CoroutineScope rememberCoroutineScope(optional kotlin.jvm.functions.Function0<? extends kotlin.coroutines.CoroutineContext> getContext);
}
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index d5fa477..9a24321 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -104,7 +104,7 @@
}
public final class Composer<N> {
- ctor public Composer(androidx.compose.runtime.SlotTable slotTable, @androidx.compose.runtime.ComposeCompilerApi androidx.compose.runtime.Applier<N> applier, @androidx.compose.runtime.ExperimentalComposeApi androidx.compose.runtime.Recomposer recomposer);
+ ctor public Composer(androidx.compose.runtime.SlotTable slotTable, @androidx.compose.runtime.ComposeCompilerApi androidx.compose.runtime.Applier<N> applier, internal androidx.compose.runtime.CompositionReference parentReference);
method @androidx.compose.runtime.InternalComposeApi public void applyChanges();
method @androidx.compose.runtime.ComposeCompilerApi public inline <T> T! cache(boolean invalid, kotlin.jvm.functions.Function0<? extends T> block);
method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(Object? value);
@@ -129,7 +129,6 @@
method public int getCurrentCompoundKeyHash();
method public boolean getDefaultsInvalid();
method public boolean getInserting();
- method public androidx.compose.runtime.Recomposer getRecomposer();
method public boolean getSkipping();
method public androidx.compose.runtime.SlotTable getSlotTable();
method @androidx.compose.runtime.ComposeCompilerApi public Object joinKey(Object? left, Object? right);
@@ -153,7 +152,6 @@
property public final int currentCompoundKeyHash;
property public final boolean defaultsInvalid;
property public final boolean inserting;
- property public final androidx.compose.runtime.Recomposer recomposer;
property public final boolean skipping;
property public final androidx.compose.runtime.SlotTable slotTable;
}
@@ -168,10 +166,6 @@
method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
- @Deprecated public interface CompositionCoroutineScope extends kotlinx.coroutines.CoroutineScope {
- method @Deprecated public default suspend Object? awaitFrame(kotlin.coroutines.Continuation<? super java.lang.Long> p);
- }
-
@Deprecated public interface CompositionFrameClock extends androidx.compose.runtime.dispatch.MonotonicFrameClock {
method @Deprecated public default suspend <R> Object? awaitFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
}
@@ -184,7 +178,7 @@
}
public final class CompositionKt {
- method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition compositionFor(Object key, androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.Recomposer recomposer, optional androidx.compose.runtime.CompositionReference? parent, optional kotlin.jvm.functions.Function0<kotlin.Unit> onCreated);
+ method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition compositionFor(Object key, androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionReference parent, optional kotlin.jvm.functions.Function0<kotlin.Unit> onCreated);
}
public interface CompositionLifecycleObserver {
@@ -195,6 +189,9 @@
public abstract class CompositionReference {
}
+ public final class CompositionReferenceKt {
+ }
+
public final class DerivedStateKt {
method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
}
@@ -352,18 +349,24 @@
property public final T! value;
}
- public final class Recomposer {
+ public final class Recomposer extends androidx.compose.runtime.CompositionReference {
ctor public Recomposer(androidx.compose.runtime.EmbeddingContext embeddingContext);
ctor public Recomposer();
method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public void composeInitial$metalava_module(androidx.compose.runtime.Composer<?> composer, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public kotlin.coroutines.CoroutineContext? getApplyingCoroutineContext$metalava_module();
+ method public boolean getCollectingKeySources$metalava_module();
+ method public int getCompoundHashKey$metalava_module();
method public androidx.compose.runtime.EmbeddingContext getEmbeddingContext();
- method public androidx.compose.runtime.dispatch.MonotonicFrameClock getFrameClock();
method public boolean hasInvalidations();
+ method public void invalidate$metalava_module(androidx.compose.runtime.Composer<?> composer);
method public suspend Object? recomposeAndApplyChanges(long frameCount, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public void recordInspectionTable$metalava_module(java.util.Set<androidx.compose.runtime.SlotTable> table);
method public suspend Object? runRecomposeAndApplyChanges(kotlin.coroutines.Continuation<?> p);
- method public void setEmbeddingContext(androidx.compose.runtime.EmbeddingContext p);
+ property public kotlin.coroutines.CoroutineContext? applyingCoroutineContext;
+ property public boolean collectingKeySources;
+ property public int compoundHashKey;
property public final androidx.compose.runtime.EmbeddingContext embeddingContext;
- property public final androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock;
field public static final androidx.compose.runtime.Recomposer.Companion Companion;
}
@@ -565,12 +568,6 @@
method @androidx.compose.runtime.Composable public static void LaunchedTask(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
method @androidx.compose.runtime.Composable public static void LaunchedTask(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
method @androidx.compose.runtime.Composable public static void LaunchedTask(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated public static suspend Object? awaitDispose(androidx.compose.runtime.CompositionCoroutineScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDispose, optional kotlin.coroutines.Continuation<?> p);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object? key, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
method @androidx.compose.runtime.Composable public static inline kotlinx.coroutines.CoroutineScope rememberCoroutineScope(optional kotlin.jvm.functions.Function0<? extends kotlin.coroutines.CoroutineContext> getContext);
}
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index e0c38a8..dfa4f02 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -104,7 +104,7 @@
}
public final class Composer<N> {
- ctor public Composer(androidx.compose.runtime.SlotTable slotTable, @androidx.compose.runtime.ComposeCompilerApi androidx.compose.runtime.Applier<N> applier, @androidx.compose.runtime.ExperimentalComposeApi androidx.compose.runtime.Recomposer recomposer);
+ ctor public Composer(androidx.compose.runtime.SlotTable slotTable, @androidx.compose.runtime.ComposeCompilerApi androidx.compose.runtime.Applier<N> applier, internal androidx.compose.runtime.CompositionReference parentReference);
method @androidx.compose.runtime.InternalComposeApi public void applyChanges();
method @androidx.compose.runtime.ComposeCompilerApi public inline <T> T! cache(boolean invalid, kotlin.jvm.functions.Function0<? extends T> block);
method @androidx.compose.runtime.ComposeCompilerApi public boolean changed(Object? value);
@@ -130,7 +130,6 @@
method public int getCurrentCompoundKeyHash();
method public boolean getDefaultsInvalid();
method public boolean getInserting();
- method public androidx.compose.runtime.Recomposer getRecomposer();
method public boolean getSkipping();
method public androidx.compose.runtime.SlotTable getSlotTable();
method @androidx.compose.runtime.ComposeCompilerApi public Object joinKey(Object? left, Object? right);
@@ -155,7 +154,6 @@
property public final int currentCompoundKeyHash;
property public final boolean defaultsInvalid;
property public final boolean inserting;
- property public final androidx.compose.runtime.Recomposer recomposer;
property public final boolean skipping;
property public final androidx.compose.runtime.SlotTable slotTable;
}
@@ -182,10 +180,6 @@
method public void setContent(kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
- @Deprecated public interface CompositionCoroutineScope extends kotlinx.coroutines.CoroutineScope {
- method @Deprecated public default suspend Object? awaitFrame(kotlin.coroutines.Continuation<? super java.lang.Long> p);
- }
-
@Deprecated public interface CompositionFrameClock extends androidx.compose.runtime.dispatch.MonotonicFrameClock {
method @Deprecated public default suspend <R> Object? awaitFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
}
@@ -198,7 +192,7 @@
}
public final class CompositionKt {
- method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition compositionFor(Object key, androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.Recomposer recomposer, optional androidx.compose.runtime.CompositionReference? parent, optional kotlin.jvm.functions.Function0<kotlin.Unit> onCreated);
+ method @androidx.compose.runtime.ExperimentalComposeApi public static androidx.compose.runtime.Composition compositionFor(Object key, androidx.compose.runtime.Applier<?> applier, androidx.compose.runtime.CompositionReference parent, optional kotlin.jvm.functions.Function0<kotlin.Unit> onCreated);
}
public interface CompositionLifecycleObserver {
@@ -209,6 +203,9 @@
public abstract class CompositionReference {
}
+ public final class CompositionReferenceKt {
+ }
+
@kotlin.PublishedApi internal final class CompositionScopedCoroutineScopeCanceller implements androidx.compose.runtime.CompositionLifecycleObserver {
ctor public CompositionScopedCoroutineScopeCanceller(kotlinx.coroutines.CoroutineScope coroutineScope);
method public kotlinx.coroutines.CoroutineScope getCoroutineScope();
@@ -378,18 +375,24 @@
property public final T! value;
}
- public final class Recomposer {
+ public final class Recomposer extends androidx.compose.runtime.CompositionReference {
ctor public Recomposer(androidx.compose.runtime.EmbeddingContext embeddingContext);
ctor public Recomposer();
method public suspend Object? awaitIdle(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public void composeInitial$metalava_module(androidx.compose.runtime.Composer<?> composer, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public kotlin.coroutines.CoroutineContext? getApplyingCoroutineContext$metalava_module();
+ method public boolean getCollectingKeySources$metalava_module();
+ method public int getCompoundHashKey$metalava_module();
method public androidx.compose.runtime.EmbeddingContext getEmbeddingContext();
- method public androidx.compose.runtime.dispatch.MonotonicFrameClock getFrameClock();
method public boolean hasInvalidations();
+ method public void invalidate$metalava_module(androidx.compose.runtime.Composer<?> composer);
method public suspend Object? recomposeAndApplyChanges(long frameCount, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public void recordInspectionTable$metalava_module(java.util.Set<androidx.compose.runtime.SlotTable> table);
method public suspend Object? runRecomposeAndApplyChanges(kotlin.coroutines.Continuation<?> p);
- method public void setEmbeddingContext(androidx.compose.runtime.EmbeddingContext p);
+ property public kotlin.coroutines.CoroutineContext? applyingCoroutineContext;
+ property public boolean collectingKeySources;
+ property public int compoundHashKey;
property public final androidx.compose.runtime.EmbeddingContext embeddingContext;
- property public final androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock;
field public static final androidx.compose.runtime.Recomposer.Companion Companion;
}
@@ -592,13 +595,7 @@
method @androidx.compose.runtime.Composable public static void LaunchedTask(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
method @androidx.compose.runtime.Composable public static void LaunchedTask(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
method @androidx.compose.runtime.Composable public static void LaunchedTask(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated public static suspend Object? awaitDispose(androidx.compose.runtime.CompositionCoroutineScope, optional kotlin.jvm.functions.Function0<kotlin.Unit> onDispose, optional kotlin.coroutines.Continuation<?> p);
method @kotlin.PublishedApi internal static kotlinx.coroutines.CoroutineScope createCompositionCoroutineScope(kotlin.coroutines.CoroutineContext coroutineContext, androidx.compose.runtime.Composer<?> composer);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object? key, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
- method @Deprecated @androidx.compose.runtime.Composable public static void launchInComposition(Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.CompositionCoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
method @androidx.compose.runtime.Composable public static inline kotlinx.coroutines.CoroutineScope rememberCoroutineScope(optional kotlin.jvm.functions.Function0<? extends kotlin.coroutines.CoroutineContext> getContext);
}
diff --git a/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
index 9998e17..9adeb79 100644
--- a/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
+++ b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
@@ -506,7 +506,6 @@
// TODO(b/150390669): Review use of @ComposableContract(tracked = false)
subcomposeInto(
container,
- Recomposer.current(),
ref.value
) @ComposableContract(tracked = false) {
block()
diff --git a/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
index 49c2735..a9b066f 100644
--- a/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
+++ b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
@@ -124,7 +124,6 @@
@OptIn(ExperimentalComposeApi::class)
subcomposeInto(
container,
- Recomposer.current(),
reference
) @ComposableContract(tracked = false) {
block()
diff --git a/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt
index 48f208f..0d2eafc 100644
--- a/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt
+++ b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt
@@ -170,7 +170,8 @@
var rememberCoroutineScopeFrameClock: MonotonicFrameClock? = null
compose {
- recomposerClock = currentComposer.recomposer.frameClock
+ recomposerClock = currentComposer.parentReference
+ .applyingCoroutineContext?.get(MonotonicFrameClock)
LaunchedTask {
launchedTaskClock = coroutineContext[MonotonicFrameClock]
}
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
index 01402b1..78cf0fe 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
@@ -23,6 +23,7 @@
import androidx.compose.runtime.SlotTable.Companion.EMPTY
import androidx.compose.runtime.tooling.InspectionTables
+import kotlin.coroutines.CoroutineContext
internal typealias Change<N> = (
applier: Applier<N>,
@@ -316,10 +317,9 @@
val applier: Applier<N>,
/**
- * Manager for scheduling recompositions.
+ * Parent of this composition; a [Recomposer] for root-level compositions.
*/
- @ExperimentalComposeApi
- val recomposer: Recomposer
+ internal val parentReference: CompositionReference
) {
init {
FrameManager.ensureStarted()
@@ -352,7 +352,6 @@
private val invalidateStack = Stack<RecomposeScope>()
- internal var parentReference: CompositionReference? = null
internal var isComposing = false
private set
internal var isDisposed = false
@@ -499,18 +498,18 @@
private fun startRoot() {
reader = slotTable.openReader()
startGroup(rootKey)
- parentReference?.let { parentRef ->
- parentRef.startComposing()
- parentProvider = parentRef.getAmbientScope()
- providersInvalidStack.push(providersInvalid.asInt())
- providersInvalid = changed(parentProvider)
- collectKeySources = parentRef.collectingKeySources
- resolveAmbient(InspectionTables, parentProvider)?.let {
- it.add(slotTable)
- parentRef.recordInspectionTable(it)
- }
- startGroup(parentRef.compoundHashKey)
+
+ // parent reference management
+ parentReference.startComposing()
+ parentProvider = parentReference.getAmbientScope()
+ providersInvalidStack.push(providersInvalid.asInt())
+ providersInvalid = changed(parentProvider)
+ collectKeySources = parentReference.collectingKeySources
+ resolveAmbient(InspectionTables, parentProvider)?.let {
+ it.add(slotTable)
+ parentReference.recordInspectionTable(it)
}
+ startGroup(parentReference.compoundHashKey)
}
/**
@@ -518,10 +517,9 @@
* the composition.
*/
private fun endRoot() {
- parentReference?.let { parentRef ->
- endGroup()
- parentRef.doneComposing()
- }
+ endGroup()
+ parentReference.doneComposing()
+
endGroup()
recordEndRoot()
finalizeCompose()
@@ -757,7 +755,7 @@
internal fun dispose() {
trace("Compose:Composer.dispose") {
- parentReference?.unregisterComposer(this)
+ parentReference.unregisterComposer(this)
invalidateStack.clear()
invalidations.clear()
changes.clear()
@@ -1219,17 +1217,8 @@
return ref.ref
}
- private fun <T> resolveAmbient(key: Ambient<T>, scope: AmbientMap): T {
- if (scope.contains(key)) return scope.getValueOf(key)
-
- val ref = parentReference
-
- if (ref != null) {
- return ref.getAmbient(key)
- }
-
- return key.defaultValueHolder.value
- }
+ private fun <T> resolveAmbient(key: Ambient<T>, scope: AmbientMap): T =
+ if (scope.contains(key)) scope.getValueOf(key) else parentReference.getAmbient(key)
internal fun <T> parentAmbient(key: Ambient<T>): T = resolveAmbient(key, currentAmbientScope())
@@ -1772,11 +1761,7 @@
// composition.
return InvalidationResult.IMMINENT
}
- if (parentReference != null) {
- parentReference?.invalidate()
- } else {
- recomposer.scheduleRecompose(this)
- }
+ parentReference.invalidate(this)
return if (isComposing) InvalidationResult.DEFERRED else InvalidationResult.SCHEDULED
}
@@ -2313,7 +2298,7 @@
}
}
- override fun <N> registerComposer(composer: Composer<N>) {
+ override fun registerComposer(composer: Composer<*>) {
composers.add(composer)
}
@@ -2322,10 +2307,14 @@
composers.remove(composer)
}
- override fun invalidate() {
- // continue invalidating up the spine of AmbientReferences
- parentReference?.invalidate()
+ override val applyingCoroutineContext: CoroutineContext?
+ get() = parentReference.applyingCoroutineContext
+ override fun composeInitial(composer: Composer<*>, composable: @Composable () -> Unit) {
+ parentReference.composeInitial(composer, composable)
+ }
+
+ override fun invalidate(composer: Composer<*>) {
invalidate(scope)
}
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
index 894cb3a..f77223e 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
@@ -57,7 +57,6 @@
* for a given [key]. If the same [key] is passed in subsequent calls, the same [Composition]
* instance will be returned.
* @param applier The [Applier] instance to be used in the composition.
- * @param recomposer The [Recomposer] instance to be used for composition.
* @param parent The parent composition reference, if applicable. Default is null.
* @param onCreated A function which will be executed only when the Composition is created.
*
@@ -69,12 +68,10 @@
fun compositionFor(
key: Any,
applier: Applier<*>,
- recomposer: Recomposer,
- parent: CompositionReference? = null,
+ parent: CompositionReference,
onCreated: () -> Unit = {}
): Composition = Compositions.findOrCreate(key) {
CompositionImpl(
- recomposer,
parent,
composerFactory = { slots, rcmpsr -> Composer(slots, applier, rcmpsr) },
onDispose = { Compositions.onDisposed(key) }
@@ -89,21 +86,19 @@
* @param onDispose A callback to be triggered when [dispose] is called.
*/
private class CompositionImpl(
- private val recomposer: Recomposer,
- parent: CompositionReference?,
- composerFactory: (SlotTable, Recomposer) -> Composer<*>,
+ private val parent: CompositionReference,
+ composerFactory: (SlotTable, CompositionReference) -> Composer<*>,
private val onDispose: () -> Unit
) : Composition {
private val slotTable: SlotTable = SlotTable()
- private val composer: Composer<*> = composerFactory(slotTable, recomposer).also {
- it.parentReference = parent
- parent?.registerComposer(it)
+ private val composer: Composer<*> = composerFactory(slotTable, parent).also {
+ parent.registerComposer(it)
}
/**
* Return true if this is a root (non-sub-) composition.
*/
- val isRoot: Boolean = parent == null
+ val isRoot: Boolean = parent is Recomposer
private var disposed = false
@@ -112,7 +107,7 @@
override fun setContent(content: @Composable () -> Unit) {
check(!disposed) { "The composition is disposed" }
this.composable = content
- recomposer.composeInitial(composable, composer)
+ parent.composeInitial(composer, composable)
}
override fun dispose() {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt
index 6ac0fd5..53a9fab 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt
@@ -16,6 +16,10 @@
package androidx.compose.runtime
+import kotlin.coroutines.CoroutineContext
+
+private val EmptyAmbientMap: AmbientMap = buildableMapOf()
+
/**
* A [CompositionReference] is an opaque type that is used to logically "link" two compositions
* together. The [CompositionReference] instance represents a reference to the "parent" composition
@@ -23,17 +27,22 @@
* "child" composition. This reference ensures that invalidations and ambients flow logically
* through the two compositions as if they were not separate.
*
+ * The "parent" of a root composition is a [Recomposer].
+ *
* @see compositionReference
*/
abstract class CompositionReference internal constructor() {
internal abstract val compoundHashKey: Int
internal abstract val collectingKeySources: Boolean
+ internal abstract val applyingCoroutineContext: CoroutineContext?
internal abstract fun recordInspectionTable(table: MutableSet<SlotTable>)
- internal abstract fun <T> getAmbient(key: Ambient<T>): T
- internal abstract fun invalidate()
- internal abstract fun <N> registerComposer(composer: Composer<N>)
- internal abstract fun unregisterComposer(composer: Composer<*>)
- internal abstract fun getAmbientScope(): AmbientMap
- internal abstract fun startComposing()
- internal abstract fun doneComposing()
+ internal abstract fun composeInitial(composer: Composer<*>, composable: @Composable () -> Unit)
+ internal abstract fun invalidate(composer: Composer<*>)
+
+ internal open fun <T> getAmbient(key: Ambient<T>): T = key.defaultValueHolder.value
+ internal open fun getAmbientScope(): AmbientMap = EmptyAmbientMap
+ internal open fun registerComposer(composer: Composer<*>) {}
+ internal open fun unregisterComposer(composer: Composer<*>) {}
+ internal open fun startComposing() {}
+ internal open fun doneComposing() {}
}
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
index 9bf8ef9..daa5de5 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
@@ -33,11 +33,9 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
-import kotlinx.coroutines.plus
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlin.coroutines.Continuation
@@ -45,6 +43,9 @@
import kotlin.coroutines.coroutineContext
import kotlin.coroutines.resume
+// TODO: Can we use rootKey for this since all compositions will have an eventual Recomposer parent?
+private const val RecomposerCompoundHashKey = 1000
+
/**
* Runs [block] with a new, active [Recomposer] applying changes in the calling [CoroutineContext].
*/
@@ -59,9 +60,10 @@
/**
* The scheduler for performing recomposition and applying updates to one or more [Composition]s.
- * [frameClock] is used to align changes with display frames.
*/
-class Recomposer(var embeddingContext: EmbeddingContext = EmbeddingContext()) {
+class Recomposer(
+ val embeddingContext: EmbeddingContext = EmbeddingContext()
+) : CompositionReference() {
/**
* This collection is its own lock, shared with [invalidComposersAwaiter]
@@ -82,7 +84,8 @@
/**
* Enforces that only one caller of [runRecomposeAndApplyChanges] is active at a time
- * while carrying its calling scope. Used to [launchEffect] on the apply dispatcher.
+ * while carrying its calling scope. Used to offer [applyingCoroutineContext] to
+ * [CompositionReference] consumers.
*/
// TODO(adamp) convert to atomicfu once ready
private val applyingScope = AtomicReference<CoroutineScope?>(null)
@@ -96,7 +99,6 @@
}
}
}
- val frameClock: MonotonicFrameClock get() = broadcastFrameClock
/**
* Await the invalidation of any associated [Composer]s, recompose them, and apply their
@@ -211,26 +213,12 @@
}
}
- @Suppress("DEPRECATION")
- private class CompositionCoroutineScopeImpl(
- override val coroutineContext: CoroutineContext
- ) : CompositionCoroutineScope
-
- @Suppress("DEPRECATION")
- @OptIn(ExperimentalCoroutinesApi::class)
- internal fun launchEffect(
- block: suspend CompositionCoroutineScope.() -> Unit
- ): Job = applyingScope.get()?.launch {
- CompositionCoroutineScopeImpl(coroutineContext).block()
- } ?: error("apply scope missing; runRecomposeAndApplyChanges must be running")
-
- // TODO this is temporary until more of this logic moves to Composition
- internal val applyingCoroutineContext: CoroutineContext?
+ override val applyingCoroutineContext: CoroutineContext?
get() = applyingScope.get()?.coroutineContext
- internal fun composeInitial(
- composable: @Composable () -> Unit,
- composer: Composer<*>
+ override fun composeInitial(
+ composer: Composer<*>,
+ composable: @Composable () -> Unit
) {
if (composer.disposeHook == null) {
// This will eventually move to the recomposer once it tracks active compositions.
@@ -313,17 +301,6 @@
fun hasInvalidations(): Boolean =
!idlingLatch.isOpen || synchronized(invalidComposers) { invalidComposers.isNotEmpty() }
- internal fun scheduleRecompose(composer: Composer<*>) {
- synchronized(invalidComposers) {
- invalidComposers.add(composer)
- invalidComposersAwaiter?.let {
- invalidComposersAwaiter = null
- idlingLatch.closeLatch()
- it.resume(Unit)
- }
- }
- }
-
/**
* Suspends until the currently pending recomposition frame is complete.
* Any recomposition for this recomposer triggered by actions before this call begins
@@ -334,6 +311,30 @@
*/
suspend fun awaitIdle(): Unit = idlingLatch.await()
+ // Recomposer always starts with a constant compound hash
+ override val compoundHashKey: Int
+ get() = RecomposerCompoundHashKey
+
+ // Collecting key sources happens at the level of a composer; starts as false
+ override val collectingKeySources: Boolean
+ get() = false
+
+ override fun recordInspectionTable(table: MutableSet<SlotTable>) {
+ // TODO: The root recomposer might be a better place to set up inspection
+ // than the current configuration with an ambient
+ }
+
+ override fun invalidate(composer: Composer<*>) {
+ synchronized(invalidComposers) {
+ invalidComposers.add(composer)
+ invalidComposersAwaiter?.let {
+ invalidComposersAwaiter = null
+ idlingLatch.closeLatch()
+ it.resume(Unit)
+ }
+ }
+ }
+
companion object {
private val embeddingContext by lazy { EmbeddingContext() }
/**
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SuspendingEffects.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SuspendingEffects.kt
index cd99e06..7023821 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SuspendingEffects.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SuspendingEffects.kt
@@ -18,210 +18,13 @@
@file:OptIn(InternalComposeApi::class)
package androidx.compose.runtime
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
-@Suppress("DEPRECATION")
-@Deprecated("No replacement; removing CompositionCoroutineScope in migration to LaunchedTask")
-private class SuspendingEffect(
- private val recomposer: Recomposer,
- private val block: suspend CompositionCoroutineScope.() -> Unit
-) : CompositionLifecycleObserver {
-
- private var job: Job? = null
-
- override fun onEnter() {
- job?.cancel("Old job was still running!")
- job = recomposer.launchEffect(block)
- }
-
- override fun onLeave() {
- job?.cancel()
- job = null
- }
-}
-
-/**
- * A [CoroutineScope] used for launching [side effects][launchInComposition] of a composition
- * that also permits [awaiting][MonotonicFrameClock.withFrameNanos] the next presentation
- * frame of the composition. This can be useful for performing the next action of an animation
- * while the effect is still present in the composition.
- */
-@Deprecated("No replacement; LaunchedTask uses CoroutineScope as a receiver directly")
-interface CompositionCoroutineScope : CoroutineScope {
- // This method deliberately shadows the awaitFrame method from kotlinx-coroutines-android
- // to redirect usage to the CompositionFrameClock API in effect blocks.
- @Suppress("RedundantSuspendModifier", "DeprecatedCallableAddReplaceWith")
- @Deprecated(
- "use withFrameNanos to perform work on a composition frame",
- level = DeprecationLevel.ERROR
- )
- suspend fun awaitFrame(): Long = error("awaitFrame should not be used; use withFrameNanos")
-}
-
-/**
- * Suspends the current coroutine until the effect is **disposed** and the
- * [CompositionCoroutineScope] is cancelled, and invokes [onDispose] before resuming.
- * [awaitDispose] never resumes normally and will always throw either
- * [kotlinx.coroutines.CancellationException] or the exception that failed the current
- * [kotlinx.coroutines.Job].
- */
-@Suppress("unused", "DeprecatedCallableAddReplaceWith", "DEPRECATION")
-@Deprecated("No replacement; LaunchedTask uses CoroutineScope as a receiver directly")
-suspend fun CompositionCoroutineScope.awaitDispose(onDispose: () -> Unit = {}): Nothing = try {
- suspendCancellableCoroutine<Nothing> { /* Suspend until cancellation */ }
-} finally {
- onDispose()
-}
-
-/**
- * Launch a suspending side effect when this composition is committed and cancel it
- * when [launchInComposition] leaves the composition. [block] will run in the **apply** scope of the
- * composition's [Recomposer], which is usually your UI's main thread.
- *
- * [block] will be launched **once** when this call enters the composition; recomposition will not
- * cause [block] to launch again. To re-launch a suspend function when inputs change, see the
- * other overloads of [launchInComposition] that accept input value parameters.
- */
-@Suppress("DEPRECATION")
-@Deprecated(
- "Renamed to LaunchedTask; custom scope removed",
- replaceWith = ReplaceWith("LaunchedTask(block)", "androidx.compose.runtime.LaunchedTask")
-)
-@Composable
-fun launchInComposition(
- block: suspend CompositionCoroutineScope.() -> Unit
-) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember { SuspendingEffect(recomposer, block) }
-}
-
-/**
- * Launch a suspending side effect when this composition is committed and cancel it
- * when [launchInComposition] leaves the composition. If [key] has changed since the last
- * recomposition, cancel the currently running [block] and launch again. [block] will run in the
- * **apply** scope of the composition's [Recomposer], which is usually your UI's main thread.
- *
- * This function should **not** be used to (re-)launch ongoing tasks in response to callback
- * events by way of storing callback data in [MutableState] passed to [key]. Instead, see
- * [rememberCoroutineScope] to obtain a [CoroutineScope] that may be used to launch ongoing jobs
- * scoped to the composition in response to event callbacks.
- */
-@Suppress("DEPRECATION")
-@Deprecated(
- "Renamed to LaunchedTask; custom scope removed",
- replaceWith = ReplaceWith("LaunchedTask(key, block)", "androidx.compose.runtime.LaunchedTask")
-)
-@Composable
-fun launchInComposition(
- key: Any?,
- block: suspend CompositionCoroutineScope.() -> Unit
-) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember(key) { SuspendingEffect(recomposer, block) }
-}
-
-/**
- * Launch a suspending side effect when this composition is committed and cancel it
- * when [launchInComposition] leaves the composition. If [key1] or [key2] has changed since the last
- * recomposition, cancel the currently running [block] and launch again. By default [block] will
- * run in the **apply** scope of the composition's [Recomposer], which is usually your UI's main
- * thread.
- *
- * This function should **not** be used to (re-)launch ongoing tasks in response to callback
- * events by way of storing callback data in [MutableState] passed to [key1] or [key2]. Instead, see
- * [rememberCoroutineScope] to obtain a [CoroutineScope] that may be used to launch ongoing jobs
- * scoped to the composition in response to event callbacks.
- */
-@Suppress("DEPRECATION")
-@Deprecated(
- "Renamed to LaunchedTask; custom scope removed",
- replaceWith = ReplaceWith(
- "LaunchedTask(key1, key2, block)",
- "androidx.compose.runtime.LaunchedTask"
- )
-)
-@Composable
-fun launchInComposition(
- key1: Any?,
- key2: Any?,
- block: suspend CompositionCoroutineScope.() -> Unit
-) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember(key1, key2) { SuspendingEffect(recomposer, block) }
-}
-
-/**
- * Launch a suspending side effect when this composition is committed and cancel it
- * when [launchInComposition] leaves the composition. If [key1], [key2] or [key3] has changed since
- * the last recomposition, cancel the currently running [block] and launch again. By default [block]
- * will run in the **apply** scope of the composition's [Recomposer], which is usually your UI's
- * main thread.
- *
- * This function should **not** be used to (re-)launch ongoing tasks in response to callback
- * events by way of storing callback data in [MutableState] passed to [key1], [key2] or [key3].
- * Instead, see [rememberCoroutineScope] to obtain a [CoroutineScope] that may be used to launch
- * ongoing jobs scoped to the composition in response to event callbacks.
- */
-@Suppress("DEPRECATION")
-@Deprecated(
- "Renamed to LaunchedTask; custom scope removed",
- replaceWith = ReplaceWith(
- "LaunchedTask(key1, key2, key3, block)",
- "androidx.compose.runtime.LaunchedTask"
- )
-)
-@Composable
-fun launchInComposition(
- key1: Any?,
- key2: Any?,
- key3: Any?,
- block: suspend CompositionCoroutineScope.() -> Unit
-) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember(key1, key2, key3) { SuspendingEffect(recomposer, block) }
-}
-
-/**
- * Launch a suspending side effect when this composition is committed and cancel it
- * when [launchInComposition] leaves the composition. If [keys] have changed since the last
- * recomposition, cancel the currently running [block] and launch again. By default [block] will
- * run in the **apply** scope of the composition's [Recomposer], which is usually your UI's main
- * thread.
- *
- * This function should **not** be used to (re-)launch ongoing tasks in response to callback
- * events by way of storing callback data in [MutableState] passed to [keys]. Instead, see
- * [rememberCoroutineScope] to obtain a [CoroutineScope] that may be used to launch ongoing jobs
- * scoped to the composition in response to event callbacks.
- */
-@Suppress("DEPRECATION")
-@Deprecated(
- "Renamed to LaunchedTask; custom scope removed",
- replaceWith = ReplaceWith(
- "LaunchedTask(*keys, block)",
- "androidx.compose.runtime.LaunchedTask"
- )
-)
-@Composable
-fun launchInComposition(
- vararg keys: Any?,
- block: suspend CompositionCoroutineScope.() -> Unit
-) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember(*keys) { SuspendingEffect(recomposer, block) }
-}
-
@PublishedApi
internal class CompositionScopedCoroutineScopeCanceller(
val coroutineScope: CoroutineScope
@@ -248,7 +51,7 @@
}
)
} else {
- val applyContext = composer.recomposer.applyingCoroutineContext
+ val applyContext = composer.parentReference.applyingCoroutineContext
if (applyContext == null) {
CoroutineScope(
Job().apply {
@@ -279,7 +82,7 @@
* interaction where the response to that event needs to unfold over time and be cancelled if the
* composable managing that process leaves the composition. Jobs should never be launched into
* **any** coroutine scope as a side effect of composition itself. For scoped ongoing jobs
- * initiated by composition, see [launchInComposition].
+ * initiated by composition, see [LaunchedTask].
*
* This function will not throw if preconditions are not met, as composable functions do not yet
* fully support exceptions. Instead the returned scope's [CoroutineScope.coroutineContext] will
@@ -300,7 +103,7 @@
}
private class LaunchedTaskImpl(
- private val recomposer: Recomposer,
+ private val parentRef: CompositionReference,
private val task: suspend CoroutineScope.() -> Unit
) : CompositionLifecycleObserver {
@@ -309,7 +112,7 @@
override fun onEnter() {
job?.cancel("Old job was still running!")
val scope = CoroutineScope(
- recomposer.applyingCoroutineContext
+ parentRef.applyingCoroutineContext
?: error("cannot launch LaunchedTask - Recomposer is not running")
)
job = scope.launch(block = task)
@@ -333,9 +136,8 @@
fun LaunchedTask(
block: suspend CoroutineScope.() -> Unit
) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember { LaunchedTaskImpl(recomposer, block) }
+ val parentRef = currentComposer.parentReference
+ remember { LaunchedTaskImpl(parentRef, block) }
}
/**
@@ -354,9 +156,8 @@
key: Any?,
block: suspend CoroutineScope.() -> Unit
) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember(key) { LaunchedTaskImpl(recomposer, block) }
+ val parentRef = currentComposer.parentReference
+ remember(key) { LaunchedTaskImpl(parentRef, block) }
}
/**
@@ -376,9 +177,8 @@
key2: Any?,
block: suspend CoroutineScope.() -> Unit
) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember(key1, key2) { LaunchedTaskImpl(recomposer, block) }
+ val parentRef = currentComposer.parentReference
+ remember(key1, key2) { LaunchedTaskImpl(parentRef, block) }
}
/**
@@ -399,9 +199,8 @@
key3: Any?,
block: suspend CoroutineScope.() -> Unit
) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember(key1, key2, key3) { LaunchedTaskImpl(recomposer, block) }
+ val parentRef = currentComposer.parentReference
+ remember(key1, key2, key3) { LaunchedTaskImpl(parentRef, block) }
}
/**
@@ -420,7 +219,6 @@
vararg keys: Any?,
block: suspend CoroutineScope.() -> Unit
) {
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- remember(*keys) { LaunchedTaskImpl(recomposer, block) }
+ val parentRef = currentComposer.parentReference
+ remember(*keys) { LaunchedTaskImpl(parentRef, block) }
}
diff --git a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPaint.kt b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPaint.kt
index 695f8f8..a22418d 100644
--- a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPaint.kt
+++ b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPaint.kt
@@ -29,6 +29,10 @@
class DesktopPaint : Paint {
internal val skija = org.jetbrains.skija.Paint()
+ constructor() {
+ filterQuality = FilterQuality.Medium
+ }
+
override fun asFrameworkPaint(): NativePaint = skija
override var alpha: Float
diff --git a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPaintTest.kt b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPaintTest.kt
index 6d71958e..026300e 100644
--- a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPaintTest.kt
+++ b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/DesktopPaintTest.kt
@@ -39,7 +39,7 @@
assertEquals(0f, paint.strokeMiterLimit)
assertEquals(StrokeJoin.Round, paint.strokeJoin)
assertEquals(true, paint.isAntiAlias)
- assertEquals(FilterQuality.None, paint.filterQuality)
+ assertEquals(FilterQuality.Medium, paint.filterQuality)
assertEquals(BlendMode.SrcOver, paint.blendMode)
assertEquals(null, paint.colorFilter)
assertEquals(null, paint.shader)
@@ -98,7 +98,9 @@
srcSize = IntSize(2, 4),
dstOffset = IntOffset(0, 4),
dstSize = IntSize(4, 12),
- paint = redPaint
+ paint = redPaint.apply {
+ filterQuality = FilterQuality.None
+ }
)
canvas.drawImageRect(
image = imageFromResource("androidx/compose/desktop/test.png"),
diff --git a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/canvas/DesktopCanvasTest.kt b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/canvas/DesktopCanvasTest.kt
index 62f78f1..c25ee47 100644
--- a/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/canvas/DesktopCanvasTest.kt
+++ b/compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/canvas/DesktopCanvasTest.kt
@@ -23,6 +23,7 @@
import androidx.compose.ui.graphics.ClipOp
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.DesktopGraphicsTest
+import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.Matrix
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.StrokeCap
@@ -115,7 +116,9 @@
srcSize = IntSize(2, 4),
dstOffset = IntOffset(0, 4),
dstSize = IntSize(4, 12),
- paint = redPaint
+ paint = redPaint.apply {
+ filterQuality = FilterQuality.None
+ }
)
screenshotRule.snap(surface)
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 697d8f9..0df1e30 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2145,7 +2145,7 @@
}
public final class SubcompositionKt {
- method @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, androidx.compose.runtime.Recomposer recomposer, optional androidx.compose.runtime.CompositionReference? parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
}
public final class TestTagKt {
@@ -2189,12 +2189,10 @@
}
public final class WrapperKt {
- method public static androidx.compose.runtime.Composition setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.Recomposer recomposer, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, androidx.compose.runtime.Recomposer recomposer, optional androidx.compose.runtime.CompositionReference? parentComposition, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference? parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public static androidx.compose.runtime.Composition setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- method @Deprecated @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, optional androidx.compose.runtime.CompositionReference? parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
}
}
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 697d8f9..0df1e30 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2145,7 +2145,7 @@
}
public final class SubcompositionKt {
- method @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, androidx.compose.runtime.Recomposer recomposer, optional androidx.compose.runtime.CompositionReference? parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
}
public final class TestTagKt {
@@ -2189,12 +2189,10 @@
}
public final class WrapperKt {
- method public static androidx.compose.runtime.Composition setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.Recomposer recomposer, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, androidx.compose.runtime.Recomposer recomposer, optional androidx.compose.runtime.CompositionReference? parentComposition, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference? parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public static androidx.compose.runtime.Composition setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- method @Deprecated @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, optional androidx.compose.runtime.CompositionReference? parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
}
}
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index b473283..6f742ec 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2222,7 +2222,7 @@
}
public final class SubcompositionKt {
- method @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, androidx.compose.runtime.Recomposer recomposer, optional androidx.compose.runtime.CompositionReference? parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
}
public final class TestTagKt {
@@ -2266,12 +2266,10 @@
}
public final class WrapperKt {
- method public static androidx.compose.runtime.Composition setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.Recomposer recomposer, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, androidx.compose.runtime.Recomposer recomposer, optional androidx.compose.runtime.CompositionReference? parentComposition, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference? parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
+ method public static androidx.compose.runtime.Composition setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method public static androidx.compose.runtime.Composition setContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.view.ViewGroup, optional androidx.compose.runtime.CompositionReference parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
method @Deprecated public static androidx.compose.runtime.Composition setViewContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
- method @Deprecated @MainThread public static androidx.compose.runtime.Composition subcomposeInto(androidx.compose.ui.node.LayoutNode container, optional androidx.compose.runtime.CompositionReference? parent, kotlin.jvm.functions.Function0<kotlin.Unit> composable);
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
index afcc9d8..a232f05 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
@@ -528,7 +528,7 @@
}
}
- assertTrue(stateUsedLatch.await(1, TimeUnit.SECONDS))
+ assertTrue("state was used in setup", stateUsedLatch.await(1, TimeUnit.SECONDS))
stateUsedLatch = CountDownLatch(1)
scenario.onActivity {
@@ -544,7 +544,10 @@
container1.addView(container2)
}
- assertTrue(stateUsedLatch.await(1, TimeUnit.SECONDS))
+ assertTrue(
+ "state was used after reattaching view",
+ stateUsedLatch.await(1, TimeUnit.SECONDS)
+ )
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WrapperTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WrapperTest.kt
index 4291492..25dff54 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WrapperTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WrapperTest.kt
@@ -150,12 +150,11 @@
it.setContent {
val ambient = ambientOf<Float>()
Providers(ambient provides 1f) {
- val recomposer = Recomposer.current()
val composition = compositionReference()
AndroidView({ frameLayout })
onCommit {
- frameLayout.setContent(recomposer, composition) {
+ frameLayout.setContent(composition) {
value = ambient.current
composedLatch.countDown()
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
index 7ea6205..3b8c762 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
@@ -56,18 +56,16 @@
@Deprecated(
"setViewContent was deprecated - use setContent instead",
ReplaceWith(
- "setContent(Recomposer.current(), parent, composable)",
- "androidx.compose.ui.platform.setContent",
- "androidx.compose.runtime.Recomposer"
+ "setContent(parent, composable)",
+ "androidx.compose.ui.platform.setContent"
)
)
fun ViewGroup.setViewContent(
- parent: CompositionReference? = null,
+ parent: CompositionReference = Recomposer.current(),
composable: @Composable () -> Unit
): Composition = compositionFor(
this,
UiApplier(this),
- Recomposer.current(),
parent,
onCreated = {
removeAllViews()
@@ -92,8 +90,7 @@
"setViewContent was deprecated - use setContent instead",
ReplaceWith(
"setContent(composable)",
- "androidx.compose.ui.platform.setContent",
- "androidx.compose.runtime.Recomposer"
+ "androidx.compose.ui.platform.setContent"
)
)
fun Activity.setViewContent(composable: @Composable () -> Unit): Composition {
@@ -107,7 +104,7 @@
.getChildAt(0) as? ViewGroup
?: FrameLayout(this).also { setContentView(it) }
@Suppress("DEPRECATION")
- return root.setViewContent(null, composable)
+ return root.setViewContent(composable = composable)
}
// TODO(chuckj): This is a temporary work-around until subframes exist so that
@@ -117,46 +114,29 @@
@OptIn(ExperimentalComposeApi::class, ExperimentalLayoutNodeApi::class)
internal actual fun actualSubcomposeInto(
container: LayoutNode,
- recomposer: Recomposer,
- parent: CompositionReference?,
+ parent: CompositionReference,
composable: @Composable () -> Unit
): Composition = compositionFor(
container,
UiApplier(container),
- recomposer,
parent
).apply {
setContent(composable)
}
-@Deprecated(
- "Specify Recomposer explicitly",
- ReplaceWith(
- "subcomposeInto(context, container, Recomposer.current(), parent, composable)",
- "androidx.compose.runtime.Recomposer"
- )
-)
-@MainThread
-@OptIn(ExperimentalLayoutNodeApi::class)
-fun subcomposeInto(
- container: LayoutNode,
- parent: CompositionReference? = null,
- composable: @Composable () -> Unit
-): Composition = subcomposeInto(container, Recomposer.current(), parent, composable)
-
/**
* Composes the given composable into the given activity. The [content] will become the root view
* of the given activity.
*
* [Composition.dispose] is called automatically when the Activity is destroyed.
*
- * @param recomposer The [Recomposer] to coordinate scheduling of composition updates
+ * @param parent The parent composition reference to coordinate scheduling of composition updates
* @param content A `@Composable` function declaring the UI contents
*/
fun ComponentActivity.setContent(
// Note: Recomposer.current() is the default here since all Activity view trees are hosted
// on the main thread.
- recomposer: Recomposer = Recomposer.current(),
+ parent: CompositionReference = Recomposer.current(),
content: @Composable () -> Unit
): Composition {
FrameManager.ensureStarted()
@@ -166,26 +146,24 @@
?: AndroidComposeView(this).also {
setContentView(it.view, DefaultLayoutParams)
}
- return doSetContent(composeView, recomposer, null, content)
+ return doSetContent(composeView, parent, content)
}
/**
* Composes the given composable into the given view.
*
- * The new composition can be logically "linked" to an existing one, by providing a non-null
- * [parentComposition]. This will ensure that invalidations and ambients will flow through
+ * The new composition can be logically "linked" to an existing one, by providing a
+ * [parent]. This will ensure that invalidations and ambients will flow through
* the two compositions as if they were not separate.
*
* Note that this [ViewGroup] should have an unique id for the saved instance state mechanism to
* be able to save and restore the values used within the composition. See [View.setId].
*
- * @param recomposer The [Recomposer] to coordinate scheduling of composition updates.
- * @param parentComposition The parent composition reference, if applicable.
+ * @param parent The [Recomposer] or parent composition reference.
* @param content Composable that will be the content of the view.
*/
fun ViewGroup.setContent(
- recomposer: Recomposer,
- parentComposition: CompositionReference? = null,
+ parent: CompositionReference = Recomposer.current(),
content: @Composable () -> Unit
): Composition {
FrameManager.ensureStarted()
@@ -195,32 +173,12 @@
} else {
removeAllViews(); null
} ?: AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
- return doSetContent(composeView, recomposer, parentComposition, content)
+ return doSetContent(composeView, parent, content)
}
-/**
- * Composes the given composable into the given view.
- *
- * Note that this [ViewGroup] should have an unique id for the saved instance state mechanism to
- * be able to save and restore the values used within the composition. See [View.setId].
- *
- * @param content Composable that will be the content of the view.
- */
-@Deprecated(
- "Specify Recomposer explicitly",
- ReplaceWith(
- "setContent(Recomposer.current(), content)",
- "androidx.compose.runtime.Recomposer"
- )
-)
-fun ViewGroup.setContent(
- content: @Composable () -> Unit
-): Composition = setContent(recomposer = Recomposer.current(), content = content)
-
private fun doSetContent(
owner: AndroidOwner,
- recomposer: Recomposer,
- parentComposition: CompositionReference?,
+ parent: CompositionReference,
content: @Composable () -> Unit
): Composition {
if (inspectionWanted(owner)) {
@@ -231,7 +189,7 @@
enableDebugInspectorInfo()
}
@OptIn(ExperimentalComposeApi::class)
- val original = compositionFor(owner.root, UiApplier(owner.root), recomposer, parentComposition)
+ val original = compositionFor(owner.root, UiApplier(owner.root), parent)
val wrapped = owner.view.getTag(R.id.wrapped_composition_tag)
as? WrappedComposition
?: WrappedComposition(owner, original).also {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt
index 1920f11..c2fde88 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.kt
@@ -26,11 +26,8 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composition
import androidx.compose.runtime.CompositionReference
-import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.Recomposer
import androidx.compose.runtime.compositionReference
-import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.onActive
import androidx.compose.runtime.onCommit
import androidx.compose.runtime.remember
@@ -81,15 +78,7 @@
) {
val view = ViewAmbient.current
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
- // The recomposer can't change.
- val dialog = remember(view) {
- DialogWrapper(
- view,
- recomposer
- )
- }
+ val dialog = remember(view) { DialogWrapper(view) }
dialog.onCloseRequest = onDismissRequest
remember(properties) { dialog.setProperties(properties) }
@@ -130,8 +119,7 @@
) : FrameLayout(context), DialogWindowProvider
private class DialogWrapper(
- private val composeView: View,
- private val recomposer: Recomposer
+ private val composeView: View
) : Dialog(composeView.context) {
lateinit var onCloseRequest: () -> Unit
@@ -139,9 +127,10 @@
private var composition: Composition? = null
init {
- window!!.requestFeature(Window.FEATURE_NO_TITLE)
- window!!.setBackgroundDrawableResource(android.R.color.transparent)
- dialogLayout = DialogLayout(context, window!!)
+ val window = window ?: error("Dialog has no window")
+ window.requestFeature(Window.FEATURE_NO_TITLE)
+ window.setBackgroundDrawableResource(android.R.color.transparent)
+ dialogLayout = DialogLayout(context, window)
setContentView(dialogLayout)
ViewTreeLifecycleOwner.set(dialogLayout, ViewTreeLifecycleOwner.get(composeView))
ViewTreeViewModelStoreOwner.set(dialogLayout, ViewTreeViewModelStoreOwner.get(composeView))
@@ -154,8 +143,7 @@
// TODO(b/159900354): Make the Android Dialog full screen and the scrim fully transparent
fun setContent(parentComposition: CompositionReference, children: @Composable () -> Unit) {
- // TODO: This should probably create a child composition of the original
- composition = dialogLayout.setContent(recomposer, parentComposition, children)
+ composition = dialogLayout.setContent(parentComposition, children)
}
private fun setSecureFlagEnabled(secureFlagEnabled: Boolean) {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt
index 4e5941e..49799a9 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.kt
@@ -29,10 +29,8 @@
import android.widget.FrameLayout
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composition
-import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.compositionReference
-import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.onCommit
import androidx.compose.runtime.onDispose
@@ -123,12 +121,9 @@
layout(0, 0) {}
}
- // TODO(lmr): refactor these APIs so that recomposer isn't necessary
- @OptIn(ExperimentalComposeApi::class)
- val recomposer = currentComposer.recomposer
val parentComposition = compositionReference()
onCommit {
- composition = popupLayout.setContent(recomposer, parentComposition) {
+ composition = popupLayout.setContent(parentComposition) {
SimpleStack(
Modifier.semantics { this.popup() }.onGloballyPositioned {
// Get the size of the content
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorCompose.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorCompose.kt
index 2c7420f..ba5e8ae 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorCompose.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorCompose.kt
@@ -21,7 +21,6 @@
import androidx.compose.runtime.Composition
import androidx.compose.runtime.CompositionReference
import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.Recomposer
import androidx.compose.runtime.compositionFor
import androidx.compose.runtime.emit
import androidx.compose.ui.graphics.Brush
@@ -101,13 +100,11 @@
@Suppress("NAME_SHADOWING")
internal fun composeVector(
container: VectorComponent,
- recomposer: Recomposer,
- parent: CompositionReference? = null,
+ parent: CompositionReference,
composable: @Composable (viewportWidth: Float, viewportHeight: Float) -> Unit
): Composition = compositionFor(
container,
VectorApplier(container.root),
- recomposer,
parent
).apply {
setContent {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt
index 6d69b86..b066954 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt
@@ -17,9 +17,7 @@
package androidx.compose.ui.graphics.vector
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.compositionReference
-import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.onDispose
@@ -191,8 +189,6 @@
}
val composition = composeVector(
vector,
- @OptIn(ExperimentalComposeApi::class)
- currentComposer.recomposer,
compositionReference(),
children
)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
index 10a7e44..14601bc 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
@@ -23,7 +23,6 @@
import androidx.compose.runtime.CompositionLifecycleObserver
import androidx.compose.runtime.CompositionReference
import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.Recomposer
import androidx.compose.runtime.compositionReference
import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.emit
@@ -79,8 +78,6 @@
measureBlock: SubcomposeMeasureScope<T>.(Constraints) -> MeasureScope.MeasureResult
) {
val state = remember { SubcomposeLayoutState<T>() }
- // TODO(lelandr): refactor these APIs so that recomposer isn't necessary
- state.recomposer = currentComposer.recomposer
state.compositionRef = compositionReference()
val materialized = currentComposer.materialize(modifier)
@@ -122,8 +119,6 @@
private class SubcomposeLayoutState<T> :
SubcomposeMeasureScope<T>,
CompositionLifecycleObserver {
- // Values set during the composition
- var recomposer: Recomposer? = null
var compositionRef: CompositionReference? = null
// MeasureScope delegation
@@ -190,9 +185,14 @@
private fun subcompose(node: LayoutNode, nodeState: NodeState<T>) {
node.ignoreModelReads {
val content = nodeState.content
- nodeState.composition = subcomposeInto(node, recomposer!!, compositionRef!!) {
- content()
- }
+ nodeState.composition = subcomposeInto(
+ container = node,
+ parent = compositionRef ?: error("parent composition reference not set"),
+ // Do not optimize this by passing nodeState.content directly; the additional
+ // composable function call from the lambda expression affects the scope of
+ // recomposition and recomposition of siblings.
+ composable = { content() }
+ )
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Subcomposition.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Subcomposition.kt
index 4c5b375..d821c90 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Subcomposition.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Subcomposition.kt
@@ -19,7 +19,6 @@
import androidx.compose.runtime.Composition
import androidx.compose.runtime.CompositionReference
import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.Recomposer
import androidx.compose.ui.node.ExperimentalLayoutNodeApi
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.util.annotation.MainThread
@@ -27,8 +26,7 @@
@OptIn(ExperimentalLayoutNodeApi::class)
internal expect fun actualSubcomposeInto(
container: LayoutNode,
- recomposer: Recomposer,
- parent: CompositionReference?,
+ parent: CompositionReference,
composable: @Composable () -> Unit
): Composition
@@ -36,7 +34,6 @@
@MainThread
fun subcomposeInto(
container: LayoutNode,
- recomposer: Recomposer,
- parent: CompositionReference? = null,
+ parent: CompositionReference,
composable: @Composable () -> Unit
-): Composition = actualSubcomposeInto(container, recomposer, parent, composable)
\ No newline at end of file
+): Composition = actualSubcomposeInto(container, parent, composable)
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
index 671389d..5554e8b 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
@@ -68,13 +68,11 @@
@OptIn(ExperimentalComposeApi::class, ExperimentalLayoutNodeApi::class)
internal actual fun actualSubcomposeInto(
container: LayoutNode,
- recomposer: Recomposer,
- parent: CompositionReference?,
+ parent: CompositionReference,
composable: @Composable () -> Unit
): Composition = compositionFor(
container,
DesktopUiApplier(container),
- recomposer,
parent
).apply {
setContent(composable)
diff --git a/navigation/navigation-compose/api/current.txt b/navigation/navigation-compose/api/current.txt
index bb73fec..01cad51 100644
--- a/navigation/navigation-compose/api/current.txt
+++ b/navigation/navigation-compose/api/current.txt
@@ -12,8 +12,15 @@
ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, internal kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
}
+ public final class NamedNavArgument {
+ }
+
+ public final class NamedNavArgumentKt {
+ method @androidx.navigation.NavDestinationDsl public static androidx.navigation.compose.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+ }
+
public final class NavGraphBuilderKt {
- method public static void composable(androidx.navigation.NavGraphBuilder, Object id, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ method public static void composable(androidx.navigation.NavGraphBuilder, Object id, optional java.util.List<androidx.navigation.compose.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
}
public final class NavHostControllerKt {
diff --git a/navigation/navigation-compose/api/public_plus_experimental_current.txt b/navigation/navigation-compose/api/public_plus_experimental_current.txt
index bb73fec..01cad51 100644
--- a/navigation/navigation-compose/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-compose/api/public_plus_experimental_current.txt
@@ -12,8 +12,15 @@
ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, internal kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
}
+ public final class NamedNavArgument {
+ }
+
+ public final class NamedNavArgumentKt {
+ method @androidx.navigation.NavDestinationDsl public static androidx.navigation.compose.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+ }
+
public final class NavGraphBuilderKt {
- method public static void composable(androidx.navigation.NavGraphBuilder, Object id, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ method public static void composable(androidx.navigation.NavGraphBuilder, Object id, optional java.util.List<androidx.navigation.compose.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
}
public final class NavHostControllerKt {
diff --git a/navigation/navigation-compose/api/restricted_current.txt b/navigation/navigation-compose/api/restricted_current.txt
index bb73fec..01cad51 100644
--- a/navigation/navigation-compose/api/restricted_current.txt
+++ b/navigation/navigation-compose/api/restricted_current.txt
@@ -12,8 +12,15 @@
ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, internal kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
}
+ public final class NamedNavArgument {
+ }
+
+ public final class NamedNavArgumentKt {
+ method @androidx.navigation.NavDestinationDsl public static androidx.navigation.compose.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+ }
+
public final class NavGraphBuilderKt {
- method public static void composable(androidx.navigation.NavGraphBuilder, Object id, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+ method public static void composable(androidx.navigation.NavGraphBuilder, Object id, optional java.util.List<androidx.navigation.compose.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
}
public final class NavHostControllerKt {
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt
index e7cbbc9..da6dd7a 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt
@@ -23,6 +23,12 @@
import androidx.compose.material.Scaffold
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.res.stringResource
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.navigate
@@ -30,18 +36,17 @@
import androidx.navigation.compose.samples.Dashboard
import androidx.navigation.compose.samples.Profile
import androidx.navigation.compose.samples.Scrollable
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
@Composable
fun BottomBarNavDemo() {
val navController = rememberNavController()
var selectedItem by remember { mutableStateOf(0) }
- val items = listOf("Profile", "Dashboard", "Scrollable")
+ val items = listOf(
+ stringResource(R.string.profile),
+ stringResource(R.string.dashboard),
+ stringResource(R.string.scrollable)
+ )
Scaffold(
bottomBar = {
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
new file mode 100644
index 0000000..cf07e24
--- /dev/null
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.compose.demos
+
+import android.net.Uri
+import androidx.compose.foundation.Text
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonConstants
+import androidx.compose.material.Divider
+import androidx.compose.material.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.navArgument
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.compose.samples.Dashboard
+import androidx.navigation.compose.samples.Screen
+import androidx.navigation.navDeepLink
+
+@Composable
+fun NavByDeepLinkDemo() {
+ val navController = rememberNavController()
+ val uri = "https://example.com/dashboard?args="
+ NavHost(navController, startDestination = "Profile") {
+ composable("Profile") { ProfileWithDeepLink(navController, uri) }
+ composable(
+ "Dashboard",
+ arguments = listOf(navArgument("args") { defaultValue = "no value given" }),
+ deepLinks = listOf(navDeepLink { uriPattern = "$uri{args}" })
+ ) { backStackEntry ->
+ Dashboard(navController, backStackEntry.arguments?.get("args") as? String)
+ }
+ }
+}
+
+@Composable
+fun ProfileWithDeepLink(navController: NavController, uri: String) {
+ Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
+ Text(text = Screen.Profile.title)
+ Divider(color = Color.Black)
+ val state = savedInstanceState(saver = TextFieldValue.Saver) { TextFieldValue() }
+ Box {
+ TextField(
+ value = state.value,
+ onValueChange = { state.value = it },
+ placeholder = { Text("Enter args here") }
+ )
+ }
+ Divider(color = Color.Black)
+ Button(
+ onClick = { navController.navigate(Uri.parse(uri + state.value.text)) },
+ colors = ButtonConstants.defaultButtonColors(backgroundColor = Color.LightGray),
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = "Navigate By DeepLink")
+ }
+ }
+}
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt
index dc30a43..8c8b507 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt
@@ -24,6 +24,7 @@
listOf(
ComposableDemo("Basic Nav Demo") { BasicNavDemo() },
ComposableDemo("Bottom Bar Nav Demo") { BottomBarNavDemo() },
- ComposableDemo("Navigation with Args") { NavWithArgsDemo() }
+ ComposableDemo("Navigation with Args") { NavWithArgsDemo() },
+ ComposableDemo("Navigation by DeepLink") { NavByDeepLinkDemo() }
)
)
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/res/values/strings.xml b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/res/values/strings.xml
new file mode 100644
index 0000000..11c3506
--- /dev/null
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <string name="profile">Profile</string>
+ <string name="dashboard">Dashboard</string>
+ <string name="scrollable">Scrollable</string>
+</resources>
diff --git a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavGraphBuilderTest.kt b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavGraphBuilderTest.kt
index 8285c60..cdbe1f0 100644
--- a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavGraphBuilderTest.kt
+++ b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavGraphBuilderTest.kt
@@ -16,16 +16,16 @@
package androidx.navigation.compose
-import androidx.compose.runtime.Composable
+import android.net.Uri
import androidx.compose.ui.platform.ContextAmbient
import androidx.core.os.bundleOf
-import androidx.navigation.NavController
-import androidx.navigation.createGraph
+import androidx.navigation.NavDeepLinkRequest
+import androidx.navigation.navDeepLink
import androidx.navigation.testing.TestNavHostController
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.ui.test.createComposeRule
-import com.google.common.truth.Truth.assertWithMessage
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Rule
import org.junit.Test
@@ -40,30 +40,76 @@
@Test
fun testCurrentBackStackEntryNavigate() {
- lateinit var navController: NavController
+ lateinit var navController: TestNavHostController
+ val key = "key"
+ val arg = "myarg"
composeTestRule.setContent {
navController = TestNavHostController(ContextAmbient.current)
navController.navigatorProvider.addNavigator(ComposeNavigator())
- navController.graph =
- navController.createGraph(startDestination = generateId(FIRST_DESTINATION)) {
- composable(FIRST_DESTINATION) { navBackStackEntry ->
- TestWithArgs(navBackStackEntry.arguments?.get("test") as String)
- }
- }
+ NavHost(navController, startDestination = FIRST_DESTINATION) {
+ composable(FIRST_DESTINATION) { }
+ composable(SECOND_DESTINATION) { }
+ }
}
composeTestRule.runOnUiThread {
- navController.navigate(generateId(FIRST_DESTINATION), bundleOf("test" to "arg"))
+ navController.navigate(generateId(SECOND_DESTINATION), bundleOf(key to arg))
+ assertThat(navController.currentBackStackEntry!!.arguments!!.getString(key))
+ .isEqualTo(arg)
+ }
+ }
+
+ @Test
+ fun testDefaultArguments() {
+ lateinit var navController: TestNavHostController
+ val key = "key"
+ val defaultArg = "default"
+ composeTestRule.setContent {
+ navController = TestNavHostController(ContextAmbient.current)
+ navController.navigatorProvider.addNavigator(ComposeNavigator())
+
+ NavHost(navController, startDestination = FIRST_DESTINATION) {
+ composable(FIRST_DESTINATION) { }
+ composable(
+ SECOND_DESTINATION,
+ arguments = listOf(navArgument(key) { defaultValue = defaultArg })
+ ) { }
+ }
+ }
+
+ composeTestRule.runOnUiThread {
+ navController.navigate(generateId(SECOND_DESTINATION))
+ assertThat(navController.currentBackStackEntry!!.arguments!!.getString(key))
+ .isEqualTo(defaultArg)
+ }
+ }
+
+ @Test
+ fun testDeepLink() {
+ lateinit var navController: TestNavHostController
+ val uriString = "https://www.example.com"
+ val deeplink = NavDeepLinkRequest.Builder.fromUri(Uri.parse(uriString)).build()
+ composeTestRule.setContent {
+ navController = TestNavHostController(ContextAmbient.current)
+ navController.navigatorProvider.addNavigator(ComposeNavigator())
+
+ NavHost(navController, startDestination = FIRST_DESTINATION) {
+ composable(FIRST_DESTINATION) { }
+ composable(
+ SECOND_DESTINATION,
+ deepLinks = listOf(navDeepLink { uriPattern = uriString })
+ ) { }
+ }
+ }
+
+ composeTestRule.runOnUiThread {
+ navController.navigate(Uri.parse(uriString))
+ assertThat(navController.currentBackStackEntry!!.destination.hasDeepLink(deeplink))
+ .isTrue()
}
}
}
-@Composable
-fun TestWithArgs(arg: String) {
- assertWithMessage("args should be passed to TestWithArgs Composable")
- .that(arg)
- .isEqualTo("arg")
-}
-
-private const val FIRST_DESTINATION = 1
\ No newline at end of file
+private const val FIRST_DESTINATION = 1
+private const val SECOND_DESTINATION = 2
\ No newline at end of file
diff --git a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
index 6e2cec0..9d1227e 100644
--- a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
+++ b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
@@ -20,6 +20,7 @@
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
import androidx.compose.ui.platform.ContextAmbient
import androidx.navigation.NavDestination
import androidx.navigation.NavDestinationBuilder
@@ -130,6 +131,81 @@
.isEqualTo(generateId("Second"))
}
}
+
+ @Test
+ fun testStateOfInactiveScreenIsRestoredWhenWeGoBackToIt() {
+ var increment = 0
+ var numberOnScreen1 = -1
+ lateinit var navController: NavHostController
+ composeTestRule.setContent {
+ navController = rememberNavController()
+
+ NavHost(navController, startDestination = "First") {
+ composable("First") {
+ numberOnScreen1 = rememberSavedInstanceState { increment++ }
+ }
+ composable("Second") {}
+ }
+ }
+
+ composeTestRule.runOnIdle {
+ assertWithMessage("Initial number should be 0")
+ .that(numberOnScreen1)
+ .isEqualTo(0)
+ numberOnScreen1 = -1
+ navController.navigate("Second")
+ }
+
+ composeTestRule.runOnIdle {
+ navController.popBackStack()
+ }
+
+ composeTestRule.runOnIdle {
+ assertWithMessage("The number should be restored")
+ .that(numberOnScreen1)
+ .isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun stateForScreenRemovedFromBackStackIsNotRestored() {
+ var increment = 0
+ var numberOnScreen2 = -1
+ lateinit var navController: NavHostController
+ composeTestRule.setContent {
+ navController = rememberNavController()
+
+ NavHost(navController, startDestination = "First") {
+ composable("First") {
+ }
+ composable("Second") {
+ numberOnScreen2 = rememberSavedInstanceState { increment++ }
+ }
+ }
+ }
+
+ composeTestRule.runOnIdle {
+ navController.navigate("Second")
+ }
+
+ composeTestRule.runOnIdle {
+ assertWithMessage("Initial number should be 0")
+ .that(numberOnScreen2)
+ .isEqualTo(0)
+ numberOnScreen2 = -1
+ navController.popBackStack()
+ }
+
+ composeTestRule.runOnIdle {
+ navController.navigate("Second")
+ }
+
+ composeTestRule.runOnIdle {
+ assertWithMessage("The number shouldn't be restored")
+ .that(numberOnScreen2)
+ .isEqualTo(1)
+ }
+ }
}
private inline fun NavGraphBuilder.test(
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NamedNavArgument.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NamedNavArgument.kt
new file mode 100644
index 0000000..3589239
--- /dev/null
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NamedNavArgument.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.compose
+
+import androidx.navigation.NavArgument
+import androidx.navigation.NavArgumentBuilder
+import androidx.navigation.NavDestinationDsl
+
+/**
+ * Construct a new [NavArgument]
+ */
+@NavDestinationDsl
+public fun navArgument(
+ name: String,
+ builder: NavArgumentBuilder.() -> Unit
+): NamedNavArgument = NamedNavArgument(name, NavArgumentBuilder().apply(builder).build())
+
+/**
+ * Construct a named [NavArgument] by using the [navArgument] method.
+ */
+public class NamedNavArgument internal constructor(
+ private val name: String,
+ private val argument: NavArgument
+) {
+ internal operator fun component1(): String = name
+ internal operator fun component2(): NavArgument = argument
+}
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt
index 4c78e54..60b02ee 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt
@@ -18,6 +18,7 @@
import androidx.compose.runtime.Composable
import androidx.navigation.NavBackStackEntry
+import androidx.navigation.NavDeepLink
import androidx.navigation.NavGraphBuilder
import androidx.navigation.get
@@ -25,12 +26,25 @@
* Add the [Composable] to the [NavGraphBuilder]
*
* @param id id for the destination
+ * @param arguments list of arguments to associate with destination
+ * @param deepLinks list of deep links to associate with the destinations
* @param content composable for the destination
*/
-public fun NavGraphBuilder.composable(id: Any, content: @Composable (NavBackStackEntry) -> Unit) {
+public fun NavGraphBuilder.composable(
+ id: Any,
+ arguments: List<NamedNavArgument> = listOf(),
+ deepLinks: List<NavDeepLink> = listOf(),
+ content: @Composable (NavBackStackEntry) -> Unit
+) {
addDestination(
ComposeNavigator.Destination(provider[ComposeNavigator::class], content).apply {
setId(generateId(id))
+ arguments.forEach { (argumentName, argument) ->
+ addArgument(argumentName, argument)
+ }
+ deepLinks.forEach { deepLink ->
+ addDeepLink(deepLink)
+ }
}
)
}
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
index c2a6d55..eca2ee0 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
@@ -20,15 +20,21 @@
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Providers
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.onCommit
import androidx.compose.runtime.remember
+import androidx.compose.runtime.savedinstancestate.ExperimentalRestorableStateHolder
+import androidx.compose.runtime.savedinstancestate.RestorableStateHolder
+import androidx.compose.runtime.savedinstancestate.rememberRestorableStateHolder
import androidx.compose.ui.platform.ContextAmbient
import androidx.compose.ui.platform.LifecycleOwnerAmbient
import androidx.compose.ui.platform.ViewModelStoreOwnerAmbient
+import androidx.compose.ui.viewinterop.viewModel
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
import androidx.navigation.NavGraph
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
+import java.util.UUID
/**
* Provides in place in the Compose hierarchy for self contained navigation to occur.
@@ -66,6 +72,7 @@
* @param navController the navController for this host
* @param graph the graph for this host
*/
+@OptIn(ExperimentalRestorableStateHolder::class)
@Composable
internal fun NavHost(navController: NavHostController, graph: NavGraph) {
var context = ContextAmbient.current
@@ -94,24 +101,56 @@
navController.graph = graph
}
+ val restorableStateHolder = rememberRestorableStateHolder<UUID>()
+
// state from the navController back stack
- val currentNavBackStackEntry by navController.currentBackStackEntryAsState()
+ val currentNavBackStackEntry = navController.currentBackStackEntryAsState().value
// If the currentNavBackStackEntry is null, we have popped all of the destinations
// off of the navController back stack and have nothing to show.
if (currentNavBackStackEntry != null) {
- val destination = currentNavBackStackEntry!!.destination
+ val destination = currentNavBackStackEntry.destination
// If the destination is not a compose destination, (e.i. activity, dialog, view, etc)
// then we do nothing and rely on Navigation to show the proper destination
if (destination is ComposeNavigator.Destination) {
// while in the scope of the composable, we provide the navBackStackEntry as the
// ViewModelStoreOwner and LifecycleOwner
Providers(
- ViewModelStoreOwnerAmbient provides currentNavBackStackEntry!!,
- LifecycleOwnerAmbient provides currentNavBackStackEntry!!
+ ViewModelStoreOwnerAmbient provides currentNavBackStackEntry,
+ LifecycleOwnerAmbient provides currentNavBackStackEntry
) {
- destination.content(currentNavBackStackEntry!!)
+ restorableStateHolder.withRestorableState {
+ destination.content(currentNavBackStackEntry)
+ }
}
}
}
}
+
+@OptIn(ExperimentalRestorableStateHolder::class)
+@Composable
+private fun RestorableStateHolder<UUID>.withRestorableState(content: @Composable () -> Unit) {
+ val viewModel = viewModel<BackStackEntryIdViewModel>()
+ viewModel.restorableStateHolder = this
+ withRestorableState(viewModel.id, content)
+}
+
+@OptIn(ExperimentalRestorableStateHolder::class)
+internal class BackStackEntryIdViewModel(handle: SavedStateHandle) : ViewModel() {
+
+ private val IdKey = "RestorableStateHolder_BackStackEntryKey"
+
+ // we create our own id for each back stack entry to support multiple entries of the same
+ // destination. this id will be restored by SavedStateHandle
+ val id: UUID = handle.get<UUID>(IdKey) ?: UUID.randomUUID().also { handle.set(IdKey, it) }
+
+ var restorableStateHolder: RestorableStateHolder<UUID>? = null
+
+ // onCleared will be called on the entries removed from the back stack. here we notify
+ // RestorableStateHolder that we shouldn't save the state for this id, so when we open this
+ // destination again the state will not be restored.
+ override fun onCleared() {
+ super.onCleared()
+ restorableStateHolder?.removeState(id)
+ }
+}
diff --git a/ui/ui-test/src/androidAndroidTest/kotlin/androidx/ui/test/ActivityWithActionBar.kt b/ui/ui-test/src/androidAndroidTest/kotlin/androidx/ui/test/ActivityWithActionBar.kt
index d130e17..eb1db10 100644
--- a/ui/ui-test/src/androidAndroidTest/kotlin/androidx/ui/test/ActivityWithActionBar.kt
+++ b/ui/ui-test/src/androidAndroidTest/kotlin/androidx/ui/test/ActivityWithActionBar.kt
@@ -43,6 +43,6 @@
}
fun setContent(composable: @Composable () -> Unit) {
- composeHolder.setContent(Recomposer.current(), null, composable)
+ composeHolder.setContent(Recomposer.current(), composable)
}
}
diff --git a/ui/ui-test/src/androidAndroidTest/kotlin/androidx/ui/test/TestAnimationClockTest.kt b/ui/ui-test/src/androidAndroidTest/kotlin/androidx/ui/test/TestAnimationClockTest.kt
index 8f57202..b11c95b 100644
--- a/ui/ui-test/src/androidAndroidTest/kotlin/androidx/ui/test/TestAnimationClockTest.kt
+++ b/ui/ui-test/src/androidAndroidTest/kotlin/androidx/ui/test/TestAnimationClockTest.kt
@@ -22,15 +22,14 @@
import androidx.compose.animation.core.transitionDefinition
import androidx.compose.animation.core.tween
import androidx.compose.animation.transition
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.Recomposer
import androidx.compose.runtime.State
-import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.Modifier
@@ -123,10 +122,8 @@
@Test
fun testAnimation_manuallyAdvanceClock_resumed() = runBlocking {
val animationState = mutableStateOf(AnimationStates.From)
- lateinit var recomposer: Recomposer
+ val recomposer = Recomposer.current()
rule.setContent {
- @OptIn(ExperimentalComposeApi::class)
- recomposer = currentComposer.recomposer
Ui(animationState)
}
diff --git a/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt b/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
index 3b65bd0..6bdc2f7 100644
--- a/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
+++ b/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
@@ -432,7 +432,7 @@
}
}
}
- composition = setContent(Recomposer.current(), null, previewComposition)
+ composition = setContent(Recomposer.current(), previewComposition)
}
/**