Merge "Rename UiSavedStateRegistry to SaveableStateRegistry" into androidx-main
diff --git a/activity/activity-ktx/build.gradle b/activity/activity-ktx/build.gradle
index dbb44df..e754ba6 100644
--- a/activity/activity-ktx/build.gradle
+++ b/activity/activity-ktx/build.gradle
@@ -30,10 +30,10 @@
api("androidx.core:core-ktx:1.1.0") {
because 'Mirror activity dependency graph for -ktx artifacts'
}
- api(projectOrArtifact(":lifecycle:lifecycle-runtime-ktx")) {
+ api("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0") {
because 'Mirror activity dependency graph for -ktx artifacts'
}
- api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-ktx"))
+ api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
api("androidx.savedstate:savedstate-ktx:1.1.0") {
because 'Mirror activity dependency graph for -ktx artifacts'
}
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index 8874463..864eb0f 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -23,10 +23,10 @@
api("androidx.annotation:annotation:1.1.0")
implementation("androidx.collection:collection:1.0.0")
api("androidx.core:core:1.1.0")
- api(projectOrArtifact(":lifecycle:lifecycle-runtime"))
- api(projectOrArtifact(":lifecycle:lifecycle-viewmodel"))
+ api("androidx.lifecycle:lifecycle-runtime:2.3.0")
+ api("androidx.lifecycle:lifecycle-viewmodel:2.3.0")
api("androidx.savedstate:savedstate:1.1.0")
- api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate"))
+ api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0")
implementation("androidx.tracing:tracing:1.0.0")
androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 30416f6..05e1d78 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -19,8 +19,8 @@
api("androidx.fragment:fragment:1.3.0-rc01")
api(project(":appcompat:appcompat-resources"))
api("androidx.drawerlayout:drawerlayout:1.0.0")
- implementation("androidx.lifecycle:lifecycle-runtime:2.3.0-rc01")
- implementation("androidx.lifecycle:lifecycle-viewmodel:2.3.0-rc01")
+ implementation("androidx.lifecycle:lifecycle-runtime:2.3.0")
+ implementation("androidx.lifecycle:lifecycle-viewmodel:2.3.0")
api("androidx.savedstate:savedstate:1.1.0")
androidTestImplementation(KOTLIN_STDLIB)
diff --git a/appcompat/integration-tests/receive-content-testapp/build.gradle b/appcompat/integration-tests/receive-content-testapp/build.gradle
new file mode 100644
index 0000000..de5f8c7
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.application")
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 16
+ }
+}
+
+dependencies {
+ api("androidx.annotation:annotation:1.1.0")
+ implementation(project(":appcompat:appcompat"))
+ implementation(CONSTRAINT_LAYOUT, { transitive = true })
+ implementation(GUAVA_ANDROID)
+
+ androidTestImplementation(ANDROIDX_TEST_CORE)
+ androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+ androidTestImplementation(ANDROIDX_TEST_RULES)
+ androidTestImplementation(ANDROIDX_TEST_RUNNER)
+ androidTestImplementation(ESPRESSO_CORE)
+ implementation(ESPRESSO_IDLING_RESOURCE)
+ implementation(TRUTH)
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/lint-baseline.xml b/appcompat/integration-tests/receive-content-testapp/lint-baseline.xml
new file mode 100644
index 0000000..8f1aa4b
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.2.0-beta02" client="gradle" variant="debug" version="4.2.0-beta02">
+
+</issues>
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/AndroidManifest.xml b/appcompat/integration-tests/receive-content-testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6b74a84
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.appcompat.demo.receivecontent">
+
+ <application
+ android:allowBackup="true"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme">
+ <activity
+ android:name="androidx.appcompat.demo.receivecontent.MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/Logcat.java b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/Logcat.java
new file mode 100644
index 0000000..835059f
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/Logcat.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.demo.receivecontent;
+
+final class Logcat {
+ private Logcat() {}
+
+ public static final String TAG = "ReceiveContentDemo";
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MainActivity.java b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MainActivity.java
new file mode 100644
index 0000000..9b2286c
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MainActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.demo.receivecontent;
+
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.AppCompatEditText;
+import androidx.core.view.ViewCompat;
+
+/** Main activity for the app. */
+public class MainActivity extends AppCompatActivity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ AppCompatEditText textInput = findViewById(R.id.text_input);
+ ViewCompat.setOnReceiveContentListener(textInput,
+ MyReceiver.SUPPORTED_MIME_TYPES, new MyReceiver());
+ }
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyExecutors.java b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyExecutors.java
new file mode 100644
index 0000000..d3a32ea
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyExecutors.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.demo.receivecontent;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+final class MyExecutors {
+ private MyExecutors() {}
+
+ private static final Handler MAIN = new Handler(Looper.getMainLooper());
+ private static final ExecutorService BG = Executors.newSingleThreadExecutor();
+
+ @NonNull
+ public static Handler main() {
+ return MAIN;
+ }
+
+ @NonNull
+ public static ExecutorService bg() {
+ return BG;
+ }
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyReceiver.java b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyReceiver.java
new file mode 100644
index 0000000..1b92d5c
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/java/androidx/appcompat/demo/receivecontent/MyReceiver.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.demo.receivecontent;
+
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.net.Uri;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.ContentInfoCompat;
+import androidx.core.view.OnReceiveContentListener;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Sample {@link OnReceiveContentListener} implementation that accepts all URIs, and delegates
+ * handling for all other content to the platform.
+ */
+public class MyReceiver implements OnReceiveContentListener {
+ public static final String[] SUPPORTED_MIME_TYPES = new String[]{"image/*"};
+
+ @Nullable
+ @Override
+ public ContentInfoCompat onReceiveContent(@NonNull View view,
+ @NonNull ContentInfoCompat contentInfo) {
+ Pair<ContentInfoCompat, ContentInfoCompat> split = contentInfo.partition(
+ item -> item.getUri() != null);
+ ContentInfoCompat uriContent = split.first;
+ ContentInfoCompat remaining = split.second;
+ if (uriContent != null) {
+ ClipData clip = uriContent.getClip();
+ for (int i = 0; i < clip.getItemCount(); i++) {
+ receive(view, clip.getItemAt(i).getUri());
+ }
+ }
+ return remaining;
+ }
+
+ private static void receive(@NonNull View view, @NonNull Uri contentUri) {
+ final Context applicationContext = view.getContext().getApplicationContext();
+ MyExecutors.bg().execute(() -> {
+ ContentResolver contentResolver = applicationContext.getContentResolver();
+ String mimeType = contentResolver.getType(contentUri);
+ long lengthBytes;
+ try {
+ AssetFileDescriptor fd = contentResolver.openAssetFileDescriptor(contentUri, "r");
+ lengthBytes = fd.getLength();
+ } catch (FileNotFoundException e) {
+ Log.e(Logcat.TAG, "Error opening content URI: " + contentUri, e);
+ return;
+ }
+ String msg = "Received " + mimeType + " (" + lengthBytes + " bytes): " + contentUri;
+ Log.i(Logcat.TAG, msg);
+ MyExecutors.main().post(() -> {
+ Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG).show();
+ });
+ });
+ }
+}
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/res/layout/activity_main.xml b/appcompat/integration-tests/receive-content-testapp/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..6a820f3
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/res/layout/activity_main.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/layout_root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity">
+
+ <androidx.appcompat.widget.AppCompatEditText
+ android:id="@+id/text_input"
+ android:inputType="text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginVertical="2dp"
+ android:layout_marginHorizontal="8dp"
+ android:gravity="top"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ android:text="@string/text_input_default_text" />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/res/values/strings.xml b/appcompat/integration-tests/receive-content-testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..bb9fdb58
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="app_name">Receive Content AndroidX Demo</string>
+ <string name="text_input_default_text">Hello world</string>
+</resources>
diff --git a/appcompat/integration-tests/receive-content-testapp/src/main/res/values/styles.xml b/appcompat/integration-tests/receive-content-testapp/src/main/res/values/styles.xml
new file mode 100644
index 0000000..a948e6f
--- /dev/null
+++ b/appcompat/integration-tests/receive-content-testapp/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <style name="AppTheme" parent="Theme.AppCompat.Light">
+ </style>
+</resources>
diff --git a/benchmark/macro/src/androidTest/java/androidx/benchmark/macro/test/StartupTimingMetricTest.kt b/benchmark/macro/src/androidTest/java/androidx/benchmark/macro/test/StartupTimingMetricTest.kt
index 2970e98..6bbf570 100644
--- a/benchmark/macro/src/androidTest/java/androidx/benchmark/macro/test/StartupTimingMetricTest.kt
+++ b/benchmark/macro/src/androidTest/java/androidx/benchmark/macro/test/StartupTimingMetricTest.kt
@@ -26,6 +26,7 @@
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeFalse
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,9 +36,7 @@
@LargeTest
@Test
fun noResults() {
- if (Build.SUPPORTED_64_BIT_ABIS.isEmpty()) {
- return
- }
+ assumeFalse(Build.SUPPORTED_64_BIT_ABIS.isEmpty())
val packageName = "fake.package.fiction.nostartups"
val metrics = measureStartup(packageName) {
@@ -49,9 +48,7 @@
@LargeTest
@Test
fun validateStartup() {
- if (Build.SUPPORTED_64_BIT_ABIS.isEmpty()) {
- return
- }
+ assumeFalse(Build.SUPPORTED_64_BIT_ABIS.isEmpty())
val packageName = "androidx.benchmark.integration.macrobenchmark.target"
val scope = MacrobenchmarkScope(packageName = packageName, launchWithClearTask = true)
@@ -80,13 +77,8 @@
metrics = listOf(metric)
)
metric.configure(config)
- return wrapper.captureTrace(packageName, iteration = 1) { tracePath ->
- try {
- metric.start()
- measureBlock()
- metric.getMetrics(packageName, tracePath)
- } finally {
- metric.stop()
- }
- }
+ wrapper.start()
+ measureBlock()
+ val tracePath = wrapper.stop(packageName, 1)!!
+ return metric.getMetrics(packageName, tracePath)
}
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index de7defa..1424c88 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -141,23 +141,24 @@
val metricResults = List(config.iterations) { iteration ->
setupBlock(scope, isFirstRun)
isFirstRun = false
- perfettoCollector.captureTrace(uniqueName, iteration) { tracePath ->
- try {
- config.metrics.forEach {
- it.start()
- }
- measureBlock(scope)
- } finally {
- config.metrics.forEach {
- it.stop()
- }
+ perfettoCollector.start()
+
+ try {
+ config.metrics.forEach {
+ it.start()
}
- config.metrics
- // capture list of Map<String,Long> per metric
- .map { it.getMetrics(config.packageName, tracePath) }
- // merge into one map
- .reduce { sum, element -> sum + element }
+ measureBlock(scope)
+ } finally {
+ config.metrics.forEach {
+ it.stop()
+ }
}
+ val tracePath = perfettoCollector.stop(uniqueName, iteration)
+ config.metrics
+ // capture list of Map<String,Long> per metric
+ .map { it.getMetrics(config.packageName, tracePath!!) }
+ // merge into one map
+ .reduce { sum, element -> sum + element }
}.mergeToMetricResults()
InstrumentationResults.instrumentationReport {
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt
index b093ea1..6892aee 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -147,10 +147,10 @@
val instrumentation = InstrumentationRegistry.getInstrumentation()
device = instrumentation.device()
parser = PerfettoTraceParser()
+ parser.copyTraceProcessorShell()
}
override fun start() {
- parser.copyTraceProcessorShell()
}
override fun stop() {
@@ -171,7 +171,7 @@
}
companion object {
- private const val TAG = "PerfettoMetric"
+ private const val TAG = "StartupTimingMetric"
private const val METRICS = "android_startup"
}
}
diff --git a/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt b/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
index 361ffac..35a28e5 100644
--- a/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
+++ b/benchmark/macro/src/main/java/androidx/benchmark/macro/PerfettoCaptureWrapper.kt
@@ -19,7 +19,6 @@
import android.os.Build
import android.util.Log
import androidx.benchmark.perfetto.PerfettoCapture
-import androidx.benchmark.perfetto.PerfettoHelper
import androidx.benchmark.perfetto.destinationPath
import androidx.benchmark.perfetto.reportAdditionalFileToCopy
@@ -34,20 +33,7 @@
}
}
- fun <T> captureTrace(
- benchmarkName: String,
- iteration: Int,
- block: (String) -> T
- ): T {
- try {
- start()
- return block(PerfettoHelper.getPerfettoTmpOutputFilePath())
- } finally {
- stop(benchmarkName, iteration)
- }
- }
-
- private fun start(): Boolean {
+ fun start(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Log.d(TAG, "Recording perfetto trace")
capture?.start()
@@ -55,7 +41,7 @@
return true
}
- private fun stop(benchmarkName: String, iteration: Int): Boolean {
+ fun stop(benchmarkName: String, iteration: Int): String? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val iterString = iteration.toString().padStart(3, '0')
// NOTE: macrobench still using legacy .trace name until
@@ -64,7 +50,8 @@
val destination = destinationPath(traceName).absolutePath
capture?.stop(destination)
reportAdditionalFileToCopy("perfetto_trace_$iterString", destination)
+ return destination
}
- return true
+ return null
}
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 58a9c20..face0e9 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -106,7 +106,7 @@
const val REACTIVE_STREAMS = "org.reactivestreams:reactive-streams:1.0.0"
const val RX_JAVA = "io.reactivex.rxjava2:rxjava:2.2.9"
const val RX_JAVA3 = "io.reactivex.rxjava3:rxjava:3.0.0"
-val SKIKO_VERSION = System.getenv("SKIKO_VERSION") ?: "0.1.21"
+val SKIKO_VERSION = System.getenv("SKIKO_VERSION") ?: "0.2.4"
val SKIKO = "org.jetbrains.skiko:skiko-jvm:$SKIKO_VERSION"
val SKIKO_LINUX_X64 = "org.jetbrains.skiko:skiko-jvm-runtime-linux-x64:$SKIKO_VERSION"
val SKIKO_MACOS_X64 = "org.jetbrains.skiko:skiko-jvm-runtime-macos-x64:$SKIKO_VERSION"
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplForceOpenCameraTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplForceOpenCameraTest.kt
new file mode 100644
index 0000000..d30d2f2
--- /dev/null
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplForceOpenCameraTest.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.internal
+
+import android.content.Context
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraManager
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import androidx.camera.camera2.internal.compat.CameraManagerCompat
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.Logger
+import androidx.camera.core.impl.CameraInternal.State
+import androidx.camera.core.impl.CameraStateRegistry
+import androidx.camera.core.impl.Observable.Observer
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.testing.CameraUtil
+import androidx.core.os.HandlerCompat
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.AfterClass
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+import org.junit.runner.RunWith
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+
+/**
+ * Tests [Camera2CameraImpl]'s force opening camera behavior.
+ *
+ * The test opens a camera with Camera2 (using [CameraDevice]), then attempts to open the same
+ * camera with CameraX (using [Camera2CameraImpl]).
+ *
+ * Camera opening behavior is different in API levels 21/22 compared to API levels 23 and above.
+ * In API levels 21 and 22, a second camera client cannot open a camera until the first client
+ * closes it, whereas in later API levels, the camera service steals the camera away from a
+ * client when another one with the same or a higher priority attempts to open it.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+public class Camera2CameraImplForceOpenCameraTest {
+
+ @get:Rule
+ public val mCameraRule: TestRule = CameraUtil.grantCameraPermissionAndPreTest()
+
+ private lateinit var mCameraId: String
+ private lateinit var mCamera2Camera: CameraDevice
+ private val mCameraXCameraToStateObserver = mutableMapOf<Camera2CameraImpl, Observer<State>>()
+
+ @Before
+ public fun getCameraId() {
+ val cameraId = CameraUtil.getCameraIdWithLensFacing(CameraSelector.LENS_FACING_BACK)
+ assumeFalse("Device doesn't have a back facing camera", cameraId == null)
+ mCameraId = cameraId!!
+ }
+
+ @After
+ public fun releaseCameraResources() {
+ if (::mCamera2Camera.isInitialized) {
+ mCamera2Camera.close()
+ }
+ for (entry in mCameraXCameraToStateObserver) {
+ releaseCameraXCameraResource(entry)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+ @Test
+ public fun openCameraImmediately_ifCameraCanBeStolen() {
+ // Open the camera with Camera2
+ val camera2CameraOpen = openCamera_camera2(mCameraId)
+ camera2CameraOpen.await()
+
+ // Open the camera with CameraX, this steals it away from Camera2
+ val cameraXCameraOpen = openCamera_cameraX(mCameraId)
+ cameraXCameraOpen.await()
+ }
+
+ @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.LOLLIPOP_MR1)
+ @Test
+ public fun openCameraWhenAvailable_ifCameraCannotBeStolen() {
+ // Open the camera with Camera2
+ val camera2CameraOpen = openCamera_camera2(mCameraId)
+ camera2CameraOpen.await()
+
+ // Attempt to open the camera with CameraX, this will fail
+ val cameraXCameraOpen = openCamera_cameraX(mCameraId)
+ assertThat(cameraXCameraOpen.timesOutWhileWaiting()).isTrue()
+
+ // Close the camera with Camera2, and wait for it to be opened with CameraX
+ mCamera2Camera.close()
+ cameraXCameraOpen.await()
+ }
+
+ @Test
+ public fun openCameraWhenAvailable_ifMaxAllowedOpenedCamerasReached() {
+ // Open the camera with CameraX
+ val cameraOpen1 = openCamera_cameraX(mCameraId)
+ cameraOpen1.await()
+
+ // Open the camera again with CameraX
+ val cameraOpen2 = openCamera_cameraX(mCameraId)
+ assertThat(cameraOpen2.timesOutWhileWaiting()).isTrue()
+
+ // Close the first camera instance, and wait for it to be opened with the second instance
+ releaseCameraXCameraResource(mCameraXCameraToStateObserver.entries.first())
+ cameraOpen2.await()
+ }
+
+ private fun openCamera_camera2(cameraId: String): Semaphore {
+ val context = ApplicationProvider.getApplicationContext<Context>()
+ val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
+ val cameraOpenSemaphore = Semaphore(0)
+ cameraManager.openCamera(
+ cameraId,
+ object : CameraDevice.StateCallback() {
+ override fun onOpened(camera: CameraDevice) {
+ Logger.d(TAG, "Camera2: Camera open")
+ mCamera2Camera = camera
+ cameraOpenSemaphore.release()
+ }
+
+ override fun onDisconnected(camera: CameraDevice) {
+ Logger.d(TAG, "Camera2: Camera disconnected")
+ mCamera2Camera = camera
+ }
+
+ override fun onError(camera: CameraDevice, error: Int) {
+ Logger.d(TAG, "Camera2: Camera error $error")
+ mCamera2Camera = camera
+ }
+ },
+ sCameraHandler
+ )
+ return cameraOpenSemaphore
+ }
+
+ private fun openCamera_cameraX(cameraId: String): Semaphore {
+ // Build camera manager wrapper
+ val context = ApplicationProvider.getApplicationContext<Context>()
+ val cameraManagerCompat = CameraManagerCompat.from(context)
+
+ // Build camera info from cameraId
+ val camera2CameraInfo = Camera2CameraInfoImpl(
+ cameraId,
+ cameraManagerCompat.getCameraCharacteristicsCompat(cameraId)
+ )
+
+ // Initialize camera instance
+ val camera = Camera2CameraImpl(
+ cameraManagerCompat,
+ cameraId,
+ camera2CameraInfo,
+ mCameraRegistry,
+ sCameraExecutor,
+ sCameraHandler
+ )
+
+ // Open the camera
+ camera.open()
+ val cameraOpenSemaphore = Semaphore(0)
+ val stateObserver = object : Observer<State> {
+ override fun onNewData(value: State?) {
+ if (value == State.OPEN) {
+ Logger.d(TAG, "CameraX: Camera open")
+ cameraOpenSemaphore.release()
+ }
+ }
+
+ override fun onError(throwable: Throwable) {
+ Logger.e(TAG, "CameraX: Camera error $throwable")
+ }
+ }
+ camera.cameraState.addObserver(sCameraExecutor, stateObserver)
+ mCameraXCameraToStateObserver[camera] = stateObserver
+ return cameraOpenSemaphore
+ }
+
+ private fun releaseCameraXCameraResource(
+ entry: MutableMap.MutableEntry<Camera2CameraImpl, Observer<State>>
+ ) {
+ entry.key.cameraState.removeObserver(entry.value)
+ entry.key.release().get()
+ }
+
+ private fun Semaphore.await() {
+ assertThat(tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ }
+
+ private fun Semaphore.timesOutWhileWaiting(): Boolean {
+ val acquired = tryAcquire(5, TimeUnit.SECONDS)
+ return !acquired
+ }
+
+ public companion object {
+ private const val TAG = "ForceOpenCameraTest"
+
+ private lateinit var sCameraHandlerThread: HandlerThread
+ private lateinit var sCameraHandler: Handler
+ private lateinit var sCameraExecutor: ExecutorService
+ private lateinit var mCameraRegistry: CameraStateRegistry
+
+ @BeforeClass
+ @JvmStatic
+ public fun classSetup() {
+ sCameraHandlerThread = HandlerThread("cameraThread")
+ sCameraHandlerThread.start()
+ sCameraHandler = HandlerCompat.createAsync(sCameraHandlerThread.looper)
+ sCameraExecutor = CameraXExecutors.newHandlerExecutor(sCameraHandler)
+ mCameraRegistry = CameraStateRegistry(1)
+ }
+
+ @AfterClass
+ @JvmStatic
+ public fun classTeardown() {
+ sCameraHandlerThread.quitSafely()
+ }
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 67e08ae..d476693 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -233,7 +233,7 @@
private void openInternal() {
switch (mState) {
case INITIALIZED:
- openCameraDevice(/*fromScheduledCameraReopen=*/false);
+ tryForceOpenCameraDevice();
break;
case CLOSING:
setState(InternalState.REOPENING);
@@ -656,7 +656,7 @@
}
}
- // Attempts to make use attach if they are not already attached.
+ /** Attempts to attach use cases if they are not already attached. */
@ExecutedBy("mExecutor")
private void tryAttachUseCases(@NonNull Collection<UseCase> toAdd) {
final boolean attachUseCaseFromEmpty =
@@ -875,28 +875,64 @@
return mCameraInfoInternal;
}
- /** Opens the camera device */
- // TODO(b/124268878): Handle SecurityException and require permission in manifest.
- @SuppressLint("MissingPermission")
+ /**
+ * Attempts to force open the camera device, which may result in stealing it from a lower
+ * priority client. This should only happen if another client doesn't close the camera when
+ * it should, e.g. when its process is moved to the background.
+ */
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@ExecutedBy("mExecutor")
- void openCameraDevice(boolean fromScheduledCameraReopen) {
+ void tryForceOpenCameraDevice() {
+ debugLog("Attempting to force open the camera.");
+ final boolean shouldTryOpenCamera = mCameraStateRegistry.tryOpenCamera(this);
+ if (!shouldTryOpenCamera) {
+ debugLog("No cameras available. Waiting for available camera before opening camera.");
+ setState(InternalState.PENDING_OPEN);
+ return;
+ }
+ openCameraDevice(false);
+ }
+
+ /**
+ * Attempts to open the camera device. Unlike {@link #tryForceOpenCameraDevice()}, this method
+ * does not steal the camera away from other clients.
+ *
+ * @param fromScheduledCameraReopen True if the attempt to open the camera originated from a
+ * {@linkplain StateCallback.ScheduledReopen scheduled
+ * reopen of the camera}. False otherwise.
+ */
+ @SuppressWarnings("WeakerAccess") /* synthetic accessor */
+ @ExecutedBy("mExecutor")
+ void tryOpenCameraDevice(boolean fromScheduledCameraReopen) {
+ debugLog("Attempting to open the camera.");
+ final boolean shouldTryOpenCamera =
+ mCameraAvailability.isCameraAvailable() && mCameraStateRegistry.tryOpenCamera(this);
+ if (!shouldTryOpenCamera) {
+ debugLog("No cameras available. Waiting for available camera before opening camera.");
+ setState(InternalState.PENDING_OPEN);
+ return;
+ }
+ openCameraDevice(fromScheduledCameraReopen);
+ }
+
+ /**
+ * Opens the camera device.
+ *
+ * @param fromScheduledCameraReopen True if the attempt to open the camera originated from a
+ * {@linkplain StateCallback.ScheduledReopen scheduled
+ * reopen of the camera}. False otherwise.
+ */
+ // TODO(b/124268878): Handle SecurityException and require permission in manifest.
+ @SuppressLint("MissingPermission")
+ @ExecutedBy("mExecutor")
+ private void openCameraDevice(boolean fromScheduledCameraReopen) {
if (!fromScheduledCameraReopen) {
mStateCallback.resetReopenMonitor();
}
mStateCallback.cancelScheduledReopen();
- // Check that we have an available camera to open here before attempting
- // to open the camera again.
- if (!mCameraAvailability.isCameraAvailable() || !mCameraStateRegistry.tryOpenCamera(this)) {
- debugLog("No cameras available. Waiting for available camera before opening camera.");
- setState(InternalState.PENDING_OPEN);
- return;
- } else {
- setState(InternalState.OPENING);
- }
-
debugLog("Opening camera.");
+ setState(InternalState.OPENING);
try {
mCameraManager.openCamera(mCameraInfoInternal.getCameraId(), mExecutor,
@@ -1346,7 +1382,7 @@
mCameraDeviceError));
scheduleCameraReopen();
} else {
- openCameraDevice(/*fromScheduledCameraReopen=*/false);
+ tryOpenCameraDevice(/*fromScheduledCameraReopen=*/false);
}
break;
default:
@@ -1524,7 +1560,7 @@
// this is still the scheduled reopen.
if (!mCancelled) {
Preconditions.checkState(mState == InternalState.REOPENING);
- openCameraDevice(/*fromScheduledCameraReopen=*/true);
+ tryOpenCameraDevice(/*fromScheduledCameraReopen=*/true);
}
});
}
@@ -1608,7 +1644,7 @@
mCameraAvailable = true;
if (mState == InternalState.PENDING_OPEN) {
- openCameraDevice(/*fromScheduledCameraReopen=*/false);
+ tryOpenCameraDevice(/*fromScheduledCameraReopen=*/false);
}
}
@@ -1627,7 +1663,7 @@
@ExecutedBy("mExecutor")
public void onOpenAvailable() {
if (mState == InternalState.PENDING_OPEN) {
- openCameraDevice(/*fromScheduledCameraReopen=*/false);
+ tryForceOpenCameraDevice();
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
index 2d630a2..9f0cacf 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
@@ -266,7 +266,7 @@
// Do not use FutureChain to chain the initFuture, because FutureChain.transformAsync()
// will not propagate if the input initFuture is failed. We want to always
// shutdown the CameraX instance to ensure that resources are freed.
- sShutdownFuture = CallbackToFutureAdapter.getFuture(
+ sShutdownFuture = Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(
completer -> {
synchronized (INSTANCE_LOCK) {
// Wait initialize complete
@@ -276,7 +276,7 @@
}, CameraXExecutors.directExecutor());
return "CameraX shutdown";
}
- });
+ }));
return sShutdownFuture;
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraStateRegistry.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraStateRegistry.java
index e8cfe7c..0f3e919 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraStateRegistry.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraStateRegistry.java
@@ -37,8 +37,8 @@
* A registry that tracks the state of cameras.
*
* <p>The registry tracks internally how many cameras are open and how many are available to open.
- * Cameras that are in a PENDING_OPEN can be be notified when there is a slot available to
- * open a camera.
+ * Cameras that are in a {@link CameraInternal.State#PENDING_OPEN} state can be notified when
+ * there is a slot available to open a camera.
*/
public final class CameraStateRegistry {
private static final String TAG = "CameraStateRegistry";
@@ -56,7 +56,7 @@
/**
* Creates a new registry with a limit of {@code maxAllowedOpenCameras} allowed to be opened.
*
- * @param maxAllowedOpenedCameras The limit of number of simultaneous open cameras.
+ * @param maxAllowedOpenedCameras The limit for number of simultaneous open cameras.
*/
public CameraStateRegistry(int maxAllowedOpenedCameras) {
mMaxAllowedOpenedCameras = maxAllowedOpenedCameras;
@@ -74,7 +74,7 @@
* <p>Before attempting to open a camera, {@link #tryOpenCamera(Camera)} must be called and
* callers should only continue to open the camera if it returns {@code true}.
*
- * <p>Cameras will be automatically unregistered when the are marked as being in a
+ * <p>Cameras will be automatically unregistered when they are marked as being in a
* {@link CameraInternal.State#RELEASED} state.
*
* @param camera The camera to register.
@@ -203,14 +203,14 @@
@NonNull CameraInternal.State state) {
CameraInternal.State previousState = Preconditions.checkNotNull(mCameraStates.get(camera),
"Cannot update state of camera which has not yet been registered. Register with "
- + "CameraAvailabilityRegistry.registerCamera()").setState(state);
+ + "CameraStateRegistry.registerCamera()").setState(state);
if (state == CameraInternal.State.OPENING) {
// A camera should only enter an OPENING state if it is already in an open state or
- // it has been allowed to by tryOpen().
+ // it has been allowed to by tryOpenCamera().
Preconditions.checkState(isOpen(state) || previousState == CameraInternal.State.OPENING,
"Cannot mark camera as opening until camera was successful at calling "
- + "CameraAvailabilityRegistry.tryOpen()");
+ + "CameraStateRegistry.tryOpenCamera()");
}
// Only update the available camera count if the camera state has changed.
@@ -235,9 +235,9 @@
mDebugString.append(
"-------------------------------------------------------------------\n");
}
- // Count the number of cameras that are not in a closed state state. Closed states are
+ // Count the number of cameras that are not in a closed state. Closed states are
// considered to be CLOSED, PENDING_OPEN or OPENING, since we can't guarantee a camera
- // has actually be open in these states. All cameras that are in a CLOSING or RELEASING
+ // has actually been open in these states. All cameras that are in a CLOSING or RELEASING
// state may have previously been open, so we will count them as open.
int openCount = 0;
for (Map.Entry<Camera, CameraRegistration> entry : mCameraStates.entrySet()) {
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 4d1fad26..7aab12a 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -231,7 +231,6 @@
field public static final androidx.car.app.model.CarIcon BACK;
field public static final androidx.car.app.model.CarIcon ERROR;
field public static final int TYPE_ALERT = 4; // 0x4
- field @Deprecated public static final int TYPE_APP = 5; // 0x5
field public static final int TYPE_APP_ICON = 5; // 0x5
field public static final int TYPE_BACK = 3; // 0x3
field public static final int TYPE_CUSTOM = 1; // 0x1
@@ -357,7 +356,6 @@
}
public final class ItemList {
- method @Deprecated public java.util.List<androidx.car.app.model.Item!> getItemList();
method public java.util.List<androidx.car.app.model.Item!> getItems();
method public androidx.car.app.model.CarText? getNoItemsMessage();
method public androidx.car.app.model.OnItemVisibilityChangedDelegate? getOnItemVisibilityChangedDelegate();
@@ -386,7 +384,6 @@
public final class ListTemplate implements androidx.car.app.model.Template {
method public androidx.car.app.model.ActionStrip? getActionStrip();
method public androidx.car.app.model.Action? getHeaderAction();
- method @Deprecated public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionLists();
method public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionedLists();
method public androidx.car.app.model.ItemList? getSingleList();
method public androidx.car.app.model.CarText? getTitle();
@@ -395,7 +392,6 @@
public static final class ListTemplate.Builder {
ctor public ListTemplate.Builder();
- method @Deprecated public androidx.car.app.model.ListTemplate.Builder addList(androidx.car.app.model.ItemList, CharSequence);
method public androidx.car.app.model.ListTemplate.Builder addSectionedList(androidx.car.app.model.SectionedItemList);
method public androidx.car.app.model.ListTemplate build();
method public androidx.car.app.model.ListTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
@@ -460,7 +456,6 @@
public final class Pane {
method public java.util.List<androidx.car.app.model.Action!> getActions();
- method @Deprecated public java.util.List<androidx.car.app.model.Row!> getRowList();
method public java.util.List<androidx.car.app.model.Row!> getRows();
method public boolean isLoading();
}
@@ -607,7 +602,6 @@
}
public final class SectionedItemList {
- method @Deprecated public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, androidx.car.app.model.CarText);
method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, CharSequence);
method public androidx.car.app.model.CarText? getHeader();
method public androidx.car.app.model.ItemList? getItemList();
@@ -618,7 +612,6 @@
public final class TemplateInfo {
ctor public TemplateInfo(Class<? extends androidx.car.app.model.Template>, String);
- ctor @Deprecated public TemplateInfo(androidx.car.app.model.Template, String);
method public Class<? extends androidx.car.app.model.Template> getTemplateClass();
method public String getTemplateId();
}
@@ -751,14 +744,12 @@
field public static final int TYPE_ON_RAMP_SLIGHT_RIGHT = 14; // 0xe
field public static final int TYPE_ON_RAMP_U_TURN_LEFT = 19; // 0x13
field public static final int TYPE_ON_RAMP_U_TURN_RIGHT = 20; // 0x14
- field @Deprecated public static final int TYPE_ROUNDABOUT_ENTER = 30; // 0x1e
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW = 34; // 0x22
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE = 35; // 0x23
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW = 32; // 0x20
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE = 33; // 0x21
field public static final int TYPE_ROUNDABOUT_ENTER_CCW = 45; // 0x2d
field public static final int TYPE_ROUNDABOUT_ENTER_CW = 43; // 0x2b
- field @Deprecated public static final int TYPE_ROUNDABOUT_EXIT = 31; // 0x1f
field public static final int TYPE_ROUNDABOUT_EXIT_CCW = 46; // 0x2e
field public static final int TYPE_ROUNDABOUT_EXIT_CW = 44; // 0x2c
field public static final int TYPE_STRAIGHT = 36; // 0x24
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 4d1fad26..7aab12a 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -231,7 +231,6 @@
field public static final androidx.car.app.model.CarIcon BACK;
field public static final androidx.car.app.model.CarIcon ERROR;
field public static final int TYPE_ALERT = 4; // 0x4
- field @Deprecated public static final int TYPE_APP = 5; // 0x5
field public static final int TYPE_APP_ICON = 5; // 0x5
field public static final int TYPE_BACK = 3; // 0x3
field public static final int TYPE_CUSTOM = 1; // 0x1
@@ -357,7 +356,6 @@
}
public final class ItemList {
- method @Deprecated public java.util.List<androidx.car.app.model.Item!> getItemList();
method public java.util.List<androidx.car.app.model.Item!> getItems();
method public androidx.car.app.model.CarText? getNoItemsMessage();
method public androidx.car.app.model.OnItemVisibilityChangedDelegate? getOnItemVisibilityChangedDelegate();
@@ -386,7 +384,6 @@
public final class ListTemplate implements androidx.car.app.model.Template {
method public androidx.car.app.model.ActionStrip? getActionStrip();
method public androidx.car.app.model.Action? getHeaderAction();
- method @Deprecated public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionLists();
method public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionedLists();
method public androidx.car.app.model.ItemList? getSingleList();
method public androidx.car.app.model.CarText? getTitle();
@@ -395,7 +392,6 @@
public static final class ListTemplate.Builder {
ctor public ListTemplate.Builder();
- method @Deprecated public androidx.car.app.model.ListTemplate.Builder addList(androidx.car.app.model.ItemList, CharSequence);
method public androidx.car.app.model.ListTemplate.Builder addSectionedList(androidx.car.app.model.SectionedItemList);
method public androidx.car.app.model.ListTemplate build();
method public androidx.car.app.model.ListTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
@@ -460,7 +456,6 @@
public final class Pane {
method public java.util.List<androidx.car.app.model.Action!> getActions();
- method @Deprecated public java.util.List<androidx.car.app.model.Row!> getRowList();
method public java.util.List<androidx.car.app.model.Row!> getRows();
method public boolean isLoading();
}
@@ -607,7 +602,6 @@
}
public final class SectionedItemList {
- method @Deprecated public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, androidx.car.app.model.CarText);
method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, CharSequence);
method public androidx.car.app.model.CarText? getHeader();
method public androidx.car.app.model.ItemList? getItemList();
@@ -618,7 +612,6 @@
public final class TemplateInfo {
ctor public TemplateInfo(Class<? extends androidx.car.app.model.Template>, String);
- ctor @Deprecated public TemplateInfo(androidx.car.app.model.Template, String);
method public Class<? extends androidx.car.app.model.Template> getTemplateClass();
method public String getTemplateId();
}
@@ -751,14 +744,12 @@
field public static final int TYPE_ON_RAMP_SLIGHT_RIGHT = 14; // 0xe
field public static final int TYPE_ON_RAMP_U_TURN_LEFT = 19; // 0x13
field public static final int TYPE_ON_RAMP_U_TURN_RIGHT = 20; // 0x14
- field @Deprecated public static final int TYPE_ROUNDABOUT_ENTER = 30; // 0x1e
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW = 34; // 0x22
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE = 35; // 0x23
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW = 32; // 0x20
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE = 33; // 0x21
field public static final int TYPE_ROUNDABOUT_ENTER_CCW = 45; // 0x2d
field public static final int TYPE_ROUNDABOUT_ENTER_CW = 43; // 0x2b
- field @Deprecated public static final int TYPE_ROUNDABOUT_EXIT = 31; // 0x1f
field public static final int TYPE_ROUNDABOUT_EXIT_CCW = 46; // 0x2e
field public static final int TYPE_ROUNDABOUT_EXIT_CW = 44; // 0x2c
field public static final int TYPE_STRAIGHT = 36; // 0x24
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 4d1fad26..7aab12a 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -231,7 +231,6 @@
field public static final androidx.car.app.model.CarIcon BACK;
field public static final androidx.car.app.model.CarIcon ERROR;
field public static final int TYPE_ALERT = 4; // 0x4
- field @Deprecated public static final int TYPE_APP = 5; // 0x5
field public static final int TYPE_APP_ICON = 5; // 0x5
field public static final int TYPE_BACK = 3; // 0x3
field public static final int TYPE_CUSTOM = 1; // 0x1
@@ -357,7 +356,6 @@
}
public final class ItemList {
- method @Deprecated public java.util.List<androidx.car.app.model.Item!> getItemList();
method public java.util.List<androidx.car.app.model.Item!> getItems();
method public androidx.car.app.model.CarText? getNoItemsMessage();
method public androidx.car.app.model.OnItemVisibilityChangedDelegate? getOnItemVisibilityChangedDelegate();
@@ -386,7 +384,6 @@
public final class ListTemplate implements androidx.car.app.model.Template {
method public androidx.car.app.model.ActionStrip? getActionStrip();
method public androidx.car.app.model.Action? getHeaderAction();
- method @Deprecated public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionLists();
method public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionedLists();
method public androidx.car.app.model.ItemList? getSingleList();
method public androidx.car.app.model.CarText? getTitle();
@@ -395,7 +392,6 @@
public static final class ListTemplate.Builder {
ctor public ListTemplate.Builder();
- method @Deprecated public androidx.car.app.model.ListTemplate.Builder addList(androidx.car.app.model.ItemList, CharSequence);
method public androidx.car.app.model.ListTemplate.Builder addSectionedList(androidx.car.app.model.SectionedItemList);
method public androidx.car.app.model.ListTemplate build();
method public androidx.car.app.model.ListTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
@@ -460,7 +456,6 @@
public final class Pane {
method public java.util.List<androidx.car.app.model.Action!> getActions();
- method @Deprecated public java.util.List<androidx.car.app.model.Row!> getRowList();
method public java.util.List<androidx.car.app.model.Row!> getRows();
method public boolean isLoading();
}
@@ -607,7 +602,6 @@
}
public final class SectionedItemList {
- method @Deprecated public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, androidx.car.app.model.CarText);
method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, CharSequence);
method public androidx.car.app.model.CarText? getHeader();
method public androidx.car.app.model.ItemList? getItemList();
@@ -618,7 +612,6 @@
public final class TemplateInfo {
ctor public TemplateInfo(Class<? extends androidx.car.app.model.Template>, String);
- ctor @Deprecated public TemplateInfo(androidx.car.app.model.Template, String);
method public Class<? extends androidx.car.app.model.Template> getTemplateClass();
method public String getTemplateId();
}
@@ -751,14 +744,12 @@
field public static final int TYPE_ON_RAMP_SLIGHT_RIGHT = 14; // 0xe
field public static final int TYPE_ON_RAMP_U_TURN_LEFT = 19; // 0x13
field public static final int TYPE_ON_RAMP_U_TURN_RIGHT = 20; // 0x14
- field @Deprecated public static final int TYPE_ROUNDABOUT_ENTER = 30; // 0x1e
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW = 34; // 0x22
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE = 35; // 0x23
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW = 32; // 0x20
field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE = 33; // 0x21
field public static final int TYPE_ROUNDABOUT_ENTER_CCW = 45; // 0x2d
field public static final int TYPE_ROUNDABOUT_ENTER_CW = 43; // 0x2b
- field @Deprecated public static final int TYPE_ROUNDABOUT_EXIT = 31; // 0x1f
field public static final int TYPE_ROUNDABOUT_EXIT_CCW = 46; // 0x2e
field public static final int TYPE_ROUNDABOUT_EXIT_CW = 44; // 0x2c
field public static final int TYPE_STRAIGHT = 36; // 0x24
diff --git a/car/app/app/src/main/java/androidx/car/app/AppManager.java b/car/app/app/src/main/java/androidx/car/app/AppManager.java
index 169b87a..bc7163e 100644
--- a/car/app/app/src/main/java/androidx/car/app/AppManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/AppManager.java
@@ -88,7 +88,6 @@
*
* @param text the text to show
* @param duration how long to display the message
- *
* @throws HostException if the remote call fails
*/
public void showToast(@NonNull CharSequence text, int duration) {
diff --git a/car/app/app/src/main/java/androidx/car/app/CarContext.java b/car/app/app/src/main/java/androidx/car/app/CarContext.java
index ec1e87a..2596a6b 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarContext.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarContext.java
@@ -157,9 +157,7 @@
* @param name The name of the car service requested. This should be one of
* {@link #APP_SERVICE},
* {@link #NAVIGATION_SERVICE} or {@link #SCREEN_SERVICE}.
- *
* @return The car service instance
- *
* @throws IllegalArgumentException if {@code name} does not refer to a valid car service
* @throws NullPointerException if {@code name} is {@code null}
*/
@@ -187,9 +185,7 @@
* ScreenManager}.
*
* @param serviceClass the class of the requested service
- *
* @return The car service instance
- *
* @throws IllegalArgumentException if {@code serviceClass} is not the class of a supported car
* service
* @throws NullPointerException if {@code serviceClass} is {@code null}
@@ -203,13 +199,10 @@
* Gets the name of the car service that is represented by the specified class.
*
* @param serviceClass the class of the requested service
- *
* @return the car service name to use with {@link #getCarService(String)}
- *
* @throws IllegalArgumentException if {@code serviceClass} is not the class of a supported car
* service
* @throws NullPointerException if {@code serviceClass} is {@code null}
- *
* @see #getCarService
*/
@NonNull
@@ -254,7 +247,6 @@
* </dl>
*
* @param intent the {@link Intent} to send to the target application
- *
* @throws SecurityException if the app attempts to start a different app explicitly or
* does not have permissions for the requested action
* @throws InvalidParameterException if {@code intent} does not meet the criteria defined
@@ -282,7 +274,6 @@
* @param appIntent the {@link Intent} to use for starting the car app. See {@link
* #startCarApp(Intent)} for the documentation on valid
* {@link Intent}s
- *
* @throws InvalidParameterException if {@code notificationIntent} is not an {@link Intent}
* received from a broadcast, due to an action taken by the
* user in the car
@@ -477,7 +468,6 @@
* @return a value between {@link AppInfo#getMinCarAppApiLevel()} and
* {@link AppInfo#getLatestCarAppApiLevel()}. In case of incompatibility, the host will
* disconnect from the service before completing the handshake
- *
* @throws IllegalStateException if invoked before the connection handshake with the host has
* been completed (for example, before
* {@link Session#onCreateScreen(Intent)})
diff --git a/car/app/app/src/main/java/androidx/car/app/CarToast.java b/car/app/app/src/main/java/androidx/car/app/CarToast.java
index c852365..645a212 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarToast.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarToast.java
@@ -76,7 +76,6 @@
* text will be set to empty
* @param duration how long to display the message. Either {@link #LENGTH_SHORT} or {@link
* #LENGTH_LONG}
- *
* @throws NullPointerException if {@code carContext} is {@code null}
*/
@NonNull
@@ -94,7 +93,6 @@
* @param text the text to show
* @param duration how long to display the message. Either {@link #LENGTH_SHORT} or {@link
* #LENGTH_LONG}
- *
* @throws NullPointerException if either the {@code carContext} or the {@code text} are {@code
* null}
*/
diff --git a/car/app/app/src/main/java/androidx/car/app/HostDispatcher.java b/car/app/app/src/main/java/androidx/car/app/HostDispatcher.java
index 890b0c8..98fb4a0 100644
--- a/car/app/app/src/main/java/androidx/car/app/HostDispatcher.java
+++ b/car/app/app/src/main/java/androidx/car/app/HostDispatcher.java
@@ -55,7 +55,6 @@
* @param hostType the service to dispatch to
* @param call the request to dispatch
* @param callName the name of the call for logging purposes
- *
* @throws SecurityException if the host has thrown it
* @throws HostException if the host throws any exception other than
* {@link SecurityException}
@@ -63,7 +62,6 @@
@Nullable
@SuppressWarnings({"unchecked", "cast.unsafe"}) // Cannot check if instanceof ServiceT
@SuppressLint("LambdaLast")
- // TODO(rampara): Change method signature to change parameter order.
public <ServiceT, ReturnT> ReturnT dispatch(
@CarServiceType @NonNull String hostType, @NonNull HostCall<ServiceT, ReturnT> call,
@NonNull String callName) {
diff --git a/car/app/app/src/main/java/androidx/car/app/Screen.java b/car/app/app/src/main/java/androidx/car/app/Screen.java
index ff3318f..9dfa10e 100644
--- a/car/app/app/src/main/java/androidx/car/app/Screen.java
+++ b/car/app/app/src/main/java/androidx/car/app/Screen.java
@@ -217,8 +217,6 @@
return mCarContext.getCarService(ScreenManager.class);
}
- // TODO(rampara): Replace code tags with link on submission of notification module
-
/**
* Returns the {@link Template} to present in the car screen.
*
@@ -296,7 +294,7 @@
* an app to begin a new task flow from notifications, and it holds true even if an app is
* already bound and in the foreground.
*
- * <p>See {@code androidx.car.app.notification.CarAppExtender} for details on notifications.
+ * <p>See {@link androidx.car.app.notification.CarAppExtender} for details on notifications.
*/
@NonNull
public abstract Template onGetTemplate();
diff --git a/car/app/app/src/main/java/androidx/car/app/ScreenManager.java b/car/app/app/src/main/java/androidx/car/app/ScreenManager.java
index 2ccb296..35f7cfe 100644
--- a/car/app/app/src/main/java/androidx/car/app/ScreenManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/ScreenManager.java
@@ -91,7 +91,6 @@
* @param onScreenResultListener the listener that will be executed with the result pushed by
* the {@code screen} through {@link Screen#setResult}. This
* callback will be executed on the main thread
- *
* @throws NullPointerException if either the {@code screen} or the {@code
* onScreenResultCallback} are {@code null}
* @throws IllegalStateException if the current thread is not the main thread
@@ -126,7 +125,6 @@
*
* @throws NullPointerException if {@code marker} is {@code null}
* @throws IllegalStateException if the current thread is not the main thread
- *
* @see Screen#setMarker
*/
public void popTo(@NonNull String marker) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Action.java b/car/app/app/src/main/java/androidx/car/app/model/Action.java
index 4bcb33f..84dd4f6 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Action.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Action.java
@@ -65,7 +65,6 @@
*
* @hide
*/
- // TODO(shiufai): investigate how to expose IntDefs if needed.
@RestrictTo(LIBRARY)
@IntDef(
value = {
@@ -135,8 +134,10 @@
private final int mType;
/**
- * Returns the title displayed in the action, or {@code null} if the action does not have a
+ * Returns the title displayed in the action or {@code null} if the action does not have a
* title.
+ *
+ * @see Builder#setTitle(CharSequence)
*/
@Nullable
public CarText getTitle() {
@@ -144,8 +145,10 @@
}
/**
- * Returns the {@link CarIcon} to displayed in the action, or {@code null} if the action does
+ * Returns the {@link CarIcon} to display in the action or {@code null} if the action does
* not have an icon.
+ *
+ * @see Builder#setIcon(CarIcon)
*/
@Nullable
public CarIcon getIcon() {
@@ -154,6 +157,8 @@
/**
* Returns the {@link CarColor} used for the background color of the action.
+ *
+ * @see Builder#setBackgroundColor(CarColor)
*/
@Nullable
public CarColor getBackgroundColor() {
@@ -339,7 +344,6 @@
*
* @param backgroundColor the {@link CarColor} to set as background. Use {@link
* CarColor#DEFAULT} to let the host pick a default
- *
* @throws IllegalArgumentException if {@code backgroundColor} is not a standard color
* @throws NullPointerException if {@code backgroundColor} is {@code null}
*/
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java b/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java
index 5f853ae..b28bc58 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ActionStrip.java
@@ -46,7 +46,9 @@
private final List<Action> mActions;
/**
- * Returns the list of {@link Action}'s.
+ * Returns the list of {@link Action}s in the strip.
+ *
+ * @see Builder#addAction(Action)
*/
@NonNull
public List<Action> getActions() {
@@ -54,7 +56,7 @@
}
/**
- * Returns the first {@link Action} associated with the input {@code actionType}, or {@code
+ * Returns the first {@link Action} associated with the input {@code actionType} or {@code
* null} if no matching {@link Action} is found.
*/
@Nullable
@@ -114,8 +116,8 @@
*
* @throws IllegalArgumentException if the background color of the action is specified,
* or if {@code action} is a standard action and an
- * action of the same type has already been added.
- * @throws NullPointerException if {@code action} is {@code null}.
+ * action of the same type has already been added
+ * @throws NullPointerException if {@code action} is {@code null}
*/
@NonNull
public Builder addAction(@NonNull Action action) {
@@ -137,7 +139,7 @@
/**
* Constructs the {@link ActionStrip} defined by this builder.
*
- * @throws IllegalStateException if the action strip is empty.
+ * @throws IllegalStateException if the action strip is empty
*/
@NonNull
public ActionStrip build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarColor.java b/car/app/app/src/main/java/androidx/car/app/model/CarColor.java
index b452602..06495ea 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarColor.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarColor.java
@@ -29,8 +29,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-// TODO(shiufai): Add link to color guidelines.
-
/**
* Represents a color to be used in a car app.
*
@@ -93,7 +91,6 @@
*
* @hide
*/
- // TODO(shiufai): investigate how to expose IntDefs if needed.
@IntDef(
value = {
TYPE_CUSTOM,
@@ -176,15 +173,14 @@
* Indicates that a default color should be used.
*
* <p>This can be used for example to tell the host that the app has no preference for the
- * tint of
- * an icon, and it should use whatever default it finds appropriate.
+ * tint of an icon, and it should use whatever default it finds appropriate.
*/
@NonNull
public static final CarColor DEFAULT = create(TYPE_DEFAULT);
/**
* Indicates that the app primary color and its dark version should be used, as declared in the
- * app manifest through the <code>carColorPrimary</code> and <code>carColorPrimaryDark</code>
+ * app manifest through the {@code carColorPrimary} and {@code carColorPrimaryDark}
* theme attributes.
*/
@NonNull
@@ -192,10 +188,8 @@
/**
* Indicates that the app secondary color and its dark version should be used, as declared in
- * the
- * app manifest through the <code>carColorSecondary</code> and
- * <code>carColorSecondaryDark</code>
- * theme attributes.
+ * the app manifest through the <code>carColorSecondary</code> and {@code
+ * carColorSecondaryDark} theme attributes.
*/
@NonNull
public static final CarColor SECONDARY = create(TYPE_SECONDARY);
@@ -241,16 +235,25 @@
return new CarColor(TYPE_CUSTOM, color, colorDark);
}
+ /** Returns the type of color for this instance. */
@CarColorType
public int getType() {
return mType;
}
+ /**
+ * Returns a packed color int for the light variant of the color, used when the type
+ * is {@link #TYPE_CUSTOM}.
+ */
@ColorInt
public int getColor() {
return mColor;
}
+ /**
+ * Returns a packed color int for the dark variant of the color, used when the type
+ * is {@link #TYPE_CUSTOM}.
+ */
@ColorInt
public int getColorDark() {
return mColorDark;
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
index 3df13af..6741860 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarIcon.java
@@ -104,7 +104,6 @@
*
* @hide
*/
- // TODO(shiufai): investigate how to expose IntDefs if needed.
@RestrictTo(LIBRARY)
@SuppressLint("UniqueConstants") // TYPE_APP will be removed in a follow-up change.
@IntDef(
@@ -112,7 +111,6 @@
TYPE_CUSTOM,
TYPE_BACK,
TYPE_ALERT,
- TYPE_APP,
TYPE_APP_ICON,
TYPE_ERROR,
})
@@ -143,16 +141,6 @@
* The app's icon.
*
* @see #APP_ICON
- * @deprecated use {@link #TYPE_APP_ICON} instead.
- */
- // TODO(b/176937077): remove after the host references TYPE_APP_ICON.
- @Deprecated
- public static final int TYPE_APP = 5;
-
- /**
- * The app's icon.
- *
- * @see #APP_ICON
*/
public static final int TYPE_APP_ICON = 5;
@@ -170,12 +158,21 @@
@NonNull
public static final CarIcon APP_ICON = CarIcon.forStandardType(TYPE_APP_ICON);
+ /**
+ * An icon representing a "back" action.
+ */
@NonNull
public static final CarIcon BACK = CarIcon.forStandardType(TYPE_BACK);
+ /**
+ * An icon representing an alert.
+ */
@NonNull
public static final CarIcon ALERT = CarIcon.forStandardType(TYPE_ALERT);
+ /**
+ * An icon representing an error.
+ */
@NonNull
public static final CarIcon ERROR = CarIcon.forStandardType(TYPE_ERROR);
@@ -189,16 +186,30 @@
@Nullable
private final CarColor mTint;
+ /**
+ * Returns the {@link IconCompat} instance backing by this car icon or {@code null} if one
+ * isn't set.
+ *
+ * @see Builder#Builder(IconCompat)
+ */
@Nullable
public IconCompat getIcon() {
return mIcon;
}
+ /**
+ * Returns the tint of the icon or {@code null} if not set.
+ *
+ * @see Builder#setTint(CarColor)
+ */
@Nullable
public CarColor getTint() {
return mTint;
}
+ /**
+ * Returns the type of car icon for this instance.
+ */
@CarIconType
public int getType() {
return mType;
@@ -260,7 +271,7 @@
return false;
}
- // TODO(shiufai): Decide how/if we will diff bitmap type IconCompat
+ // TODO(b/146175636): Decide how/if we will diff bitmap type IconCompat
if (type == TYPE_RESOURCE) {
return Objects.equals(mIcon.getResPackage(), other.getResPackage())
&& mIcon.getResId() == other.getResId();
@@ -325,7 +336,6 @@
/**
* Sets the tint of the icon to the given {@link CarColor}.
*
- *
* <p>This tint overrides the tint set through {@link IconCompat#setTint(int)} in the
* backing {@link IconCompat} with a {@link CarColor} tint.The tint set through {@link
* IconCompat#setTint(int)} is not guaranteed to be applied if the {@link CarIcon} tint
@@ -335,8 +345,7 @@
*
* <p>By default, no tint is set unless one is specified with this method.
*
- * @throws NullPointerException if {@code tin} is {@code null}.
- *
+ * @throws NullPointerException if {@code tin} is {@code null}
* @see CarColor
* @see android.graphics.drawable.Drawable#setTintMode(Mode)
*/
@@ -364,9 +373,8 @@
* <li>{@link IconCompat#TYPE_URI}
* </ul>
*
- * <p>{@link IconCompat#TYPE_URI} is only supported in templates that explicitly allow it
- * . In
- * those cases, the appropriate APIs will be documented to indicate this.
+ * <p>{@link IconCompat#TYPE_URI} is only supported in templates that explicitly allow it.
+ * In those cases, the appropriate APIs will be documented to indicate this.
*
* <p>For {@link IconCompat#TYPE_URI}, the URI's scheme must be {@link
* ContentResolver#SCHEME_CONTENT}.
@@ -374,8 +382,8 @@
* <p>If the icon image is loaded from URI, it may be cached on the host side. Changing the
* contents of the URI will result in the host showing a stale image.
*
- * @throws IllegalArgumentException if {@code icon}'s URI scheme is not supported.
- * @throws NullPointerException if {@code icon} is {@code null}.
+ * @throws IllegalArgumentException if {@code icon}'s URI scheme is not supported
+ * @throws NullPointerException if {@code icon} is {@code null}
*/
public Builder(@NonNull IconCompat icon) {
CarIconConstraints.UNCONSTRAINED.checkSupportedIcon(requireNonNull(icon));
@@ -388,7 +396,7 @@
* Returns a {@link Builder} instance configured with the same data as the given
* {@link CarIcon} instance.
*
- * @throws NullPointerException if {@code icon} is {@code null}.
+ * @throws NullPointerException if {@code icon} is {@code null}
*/
public Builder(@NonNull CarIcon carIcon) {
requireNonNull(carIcon);
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarIconSpan.java b/car/app/app/src/main/java/androidx/car/app/model/CarIconSpan.java
index 1c91d363..ad83c97 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarIconSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarIconSpan.java
@@ -71,7 +71,6 @@
ALIGN_BASELINE,
})
@Retention(RetentionPolicy.SOURCE)
- // TODO(shiufai): investigate how to expose IntDefs if needed.
@RestrictTo(LIBRARY)
public @interface Alignment {
}
@@ -108,7 +107,7 @@
* Creates a {@link CarIconSpan} from a {@link CarIcon} with a default alignment of {@link
* #ALIGN_BASELINE}.
*
- * @throws NullPointerException if {@code icon} is {@code null}.
+ * @throws NullPointerException if {@code icon} is {@code null}
* @see #create(CarIcon, int)
*/
@NonNull
@@ -118,15 +117,16 @@
/**
* Creates a {@link CarIconSpan} from a {@link CarIcon}, specifying the alignment of the icon
- * with
- * respect to its surrounding text.
+ * with respect to its surrounding text.
*
- * @param icon the {@link CarIcon} to replace the text with.
+ * @param icon the {@link CarIcon} to replace the text with
* @param alignment the alignment of the {@link CarIcon} relative to the text. This should be
* one of {@link #ALIGN_BASELINE}, {@link #ALIGN_BOTTOM} or
- * {@link #ALIGN_CENTER}.
- * @throws NullPointerException if {@code icon} is {@code null}.
- * @throws IllegalArgumentException if {@code alignment} is not a valid value.
+ * {@link #ALIGN_CENTER}
+ *
+ * @throws NullPointerException if {@code icon} is {@code null}
+ * @throws IllegalArgumentException if {@code alignment} is not a valid value
+ *
* @see #ALIGN_BASELINE
* @see #ALIGN_BOTTOM
* @see #ALIGN_CENTER
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarLocation.java b/car/app/app/src/main/java/androidx/car/app/model/CarLocation.java
index 674dd18..53a7200 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarLocation.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarLocation.java
@@ -42,7 +42,7 @@
* Returns a new instance of a {@link CarLocation} with the same latitude and longitude
* contained in the given {@link Location}.
*
- * @throws NullPointerException if {@code location} is {@code null}.
+ * @throws NullPointerException if {@code location} is {@code null}
*/
@NonNull
public static CarLocation create(@NonNull Location location) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/CarText.java b/car/app/app/src/main/java/androidx/car/app/model/CarText.java
index 7762286..22e5c1d 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/CarText.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/CarText.java
@@ -58,6 +58,7 @@
return new CarText(text);
}
+ /** Returns whether the text string is empty. */
public boolean isEmpty() {
return mText.isEmpty();
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java b/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java
index b62f7b9..11bb852 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java
@@ -34,6 +34,7 @@
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.TextStyle;
+import java.util.Date;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
@@ -85,18 +86,11 @@
@Override
@NonNull
- @RequiresApi(26)
- // TODO(shiufai): consider removing the @RequiresApi annotation for a toString method.
- @SuppressLint("UnsafeNewApiCall")
public String toString() {
- return "[local: "
- + LocalDateTime.ofEpochSecond(
- mTimeSinceEpochMillis / 1000,
- /* nanoOfSecond= */ 0,
- ZoneOffset.ofTotalSeconds(mZoneOffsetSeconds))
- + ", zone: "
- + mZoneShortName
- + "]";
+ return "[time since epoch (ms): " + mTimeSinceEpochMillis
+ + "( " + new Date(mTimeSinceEpochMillis) + ") "
+ + " zone offset (s): " + mZoneOffsetSeconds
+ + ", zone: " + mZoneShortName + "]";
}
@Override
@@ -123,19 +117,19 @@
* Returns an instance of a {@link DateTimeWithZone}.
*
* @param timeSinceEpochMillis The number of milliseconds from the epoch of
- * 1970-01-01T00:00:00Z.
+ * 1970-01-01T00:00:00Z
* @param zoneOffsetSeconds The offset of the time zone from UTC at the date specified by
* {@code timeInUtcMillis}. This offset must be in the range
* {@code -18:00} to {@code +18:00}, which corresponds to -64800
- * to +64800.
+ * to +64800
* @param zoneShortName The abbreviated name of the time zone, for example, "PST" for
* Pacific Standard Time. This string may be used to display to
* the user along with the date when needed, for example, if this
- * time zone is different than the current system time zone.
+ * time zone is different than the current system time zone
* @throws IllegalArgumentException if {@code timeSinceEpochMillis} is a negative value, if
* {@code zoneOffsetSeconds} is not within the required range,
- * or if {@code zoneShortName} is empty.
- * @throws NullPointerException if {@code zoneShortName} is {@code null}.
+ * or if {@code zoneShortName} is empty
+ * @throws NullPointerException if {@code zoneShortName} is {@code null}
*/
@NonNull
public static DateTimeWithZone create(
@@ -158,14 +152,14 @@
* Returns an instance of a {@link DateTimeWithZone}.
*
* @param timeSinceEpochMillis The number of milliseconds from the epoch of
- * 1970-01-01T00:00:00Z.
+ * 1970-01-01T00:00:00Z
* @param timeZone The time zone at the date specified by {@code timeInUtcMillis}.
* The abbreviated name of this time zone, formatted using the
* default locale, may be displayed to the user when needed, for
* example, if this time zone is different than the current
- * system time zone.
- * @throws IllegalArgumentException if {@code timeSinceEpochMillis} is a negative value.
- * @throws NullPointerException if {@code timeZone} is {@code null}.
+ * system time zone
+ * @throws IllegalArgumentException if {@code timeSinceEpochMillis} is a negative value
+ * @throws NullPointerException if {@code timeZone} is {@code null}
*/
@NonNull
public static DateTimeWithZone create(long timeSinceEpochMillis, @NonNull TimeZone timeZone) {
@@ -186,8 +180,8 @@
* @param zonedDateTime The time with a time zone. The abbreviated name of this time zone,
* formatted using the default locale, may be displayed to the user when
* needed, for example, if this time zone is different than the current
- * system time zone.
- * @throws NullPointerException if {@code zonedDateTime} is {@code null}.
+ * system time zone
+ * @throws NullPointerException if {@code zonedDateTime} is {@code null}
*/
// TODO(shiufai): revisit wrapping this method in a container class (e.g. Api26Impl).
@SuppressLint("UnsafeNewApiCall")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Distance.java b/car/app/app/src/main/java/androidx/car/app/model/Distance.java
index 2f1ea2d..7df2b6b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Distance.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Distance.java
@@ -46,7 +46,6 @@
UNIT_MILES_P1
})
@Retention(RetentionPolicy.SOURCE)
- // TODO(shiufai): investigate how to expose IntDefs if needed.
@RestrictTo(LIBRARY)
public @interface Unit {
}
@@ -114,11 +113,12 @@
* "1.5 km", "1.0 km", and so on.
*
* @param displayDistance the distance to display, in the units specified in {@code
- * displayUnit}. See {@link #getDisplayDistance()}.
+ * displayUnit}. See {@link #getDisplayDistance()}
* @param displayUnit the unit of distance to use when displaying the value in {@code
* displayUnit}. This should be one of the {@code UNIT_*} static
- * constants defined in this class. See {@link #getDisplayUnit()}.
- * @throws IllegalArgumentException if {@code displayDistance} is negative.
+ * constants defined in this class. See {@link #getDisplayUnit()}
+ *
+ * @throws IllegalArgumentException if {@code displayDistance} is negative
*/
@NonNull
public static Distance create(double displayDistance, @Unit int displayUnit) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/DistanceSpan.java b/car/app/app/src/main/java/androidx/car/app/model/DistanceSpan.java
index a931389..1d01ee08 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/DistanceSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/DistanceSpan.java
@@ -68,14 +68,10 @@
return new DistanceSpan(requireNonNull(distance));
}
- private DistanceSpan(Distance distance) {
- this.mDistance = distance;
- }
-
- private DistanceSpan() {
- mDistance = null;
- }
-
+ /**
+ * Returns the {@link Distance} instance associated with this span or {@code null} if not
+ * set.
+ */
@Nullable
public Distance getDistance() {
return mDistance;
@@ -104,4 +100,12 @@
return Objects.equals(mDistance, otherSpan.mDistance);
}
+
+ private DistanceSpan(Distance distance) {
+ this.mDistance = distance;
+ }
+
+ private DistanceSpan() {
+ mDistance = null;
+ }
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java b/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java
index 83000b7..a015208 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java
@@ -71,14 +71,7 @@
return new DurationSpan(requireNonNull(duration).getSeconds());
}
- private DurationSpan(long durationSeconds) {
- this.mDurationSeconds = durationSeconds;
- }
-
- private DurationSpan() {
- mDurationSeconds = 0;
- }
-
+ /** Returns the time duration associated with this span, in seconds. */
@SuppressLint("MethodNameUnits")
public long getDurationSeconds() {
return mDurationSeconds;
@@ -108,4 +101,12 @@
return mDurationSeconds == otherSpan.mDurationSeconds;
}
+
+ private DurationSpan(long durationSeconds) {
+ this.mDurationSeconds = durationSeconds;
+ }
+
+ private DurationSpan() {
+ mDurationSeconds = 0;
+ }
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java b/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java
index ca11229..4edd35b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ForegroundCarColorSpan.java
@@ -50,6 +50,7 @@
@Keep
private final CarColor mCarColor;
+ /** Returns the {@link CarColor} associated with this span or {@code null} if not set. */
@Nullable
public CarColor getColor() {
return mCarColor;
@@ -60,8 +61,8 @@
*
* <p>Custom colors created with {@link CarColor#createCustom} are not supported in text spans.
*
- * @throws IllegalArgumentException if {@code carColor} contains a custom color.
- * @throws NullPointerException if {@code carColor} is {@code null}.
+ * @throws IllegalArgumentException if {@code carColor} contains a custom color
+ * @throws NullPointerException if {@code carColor} is {@code null}
*/
@NonNull
public static ForegroundCarColorSpan create(@NonNull CarColor carColor) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/GridItem.java b/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
index b6552d3..e348fb3 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
@@ -44,7 +44,6 @@
*
* @hide
*/
- // TODO(shiufai): investigate how to expose IntDefs if needed.
@RestrictTo(LIBRARY)
@IntDef(value = {IMAGE_TYPE_ICON, IMAGE_TYPE_LARGE})
@Retention(RetentionPolicy.SOURCE)
@@ -55,8 +54,7 @@
* Represents an icon to be displayed in the grid item.
*
* <p>If necessary, icons will be scaled down to fit within a 64 x 64 dp bounding box,
- * preserving
- * their aspect ratios.
+ * preserving their aspect ratios.
*
* <p>A tint color is expected to be provided via {@link CarIcon.Builder#setTint}. Otherwise, a
* default tint color as determined by the host will be applied.
@@ -89,24 +87,41 @@
@Nullable
private final OnClickDelegate mOnClickDelegate;
- /** Returns whether the grid item is loading. */
+ /**
+ * Returns whether the grid item is in a loading state.
+ *
+ * @see Builder#setLoading(boolean)
+ */
public boolean isLoading() {
return mIsLoading;
}
- /** Returns the title of the grid item. */
+ /**
+ * Returns the title of the grid item or {@code null} if not set.
+ *
+ * @see Builder#setTitle(CharSequence)
+ */
@Nullable
public CarText getTitle() {
return mTitle;
}
- /** Returns the list of text below the title. */
+ /**
+ * Returns the text to display below the title or {@code null} if no text will be displayed
+ * below the title.
+ *
+ * @see Builder#setText(CharSequence)
+ */
@Nullable
public CarText getText() {
return mText;
}
- /** Returns the image of the grid item. */
+ /**
+ * Returns the image of the grid item or {@code null} if not set.
+ *
+ * @see Builder#setImage(CarIcon)
+ */
@Nullable
public CarIcon getImage() {
return mImage;
@@ -119,7 +134,7 @@
}
/**
- * Returns the {@link OnClickDelegate} to be called back when the grid item is clicked, or
+ * Returns the {@link OnClickDelegate} to be called back when the grid item is clicked or
* {@code null} if the grid item is non-clickable.
*/
@Nullable
@@ -216,8 +231,8 @@
*
* <p>Unless set with this method, the grid item will not have an title.
*
- * @throws NullPointerException if {@code title} is {@code null}.
- * @throws IllegalArgumentException if {@code title} is empty.
+ * @throws NullPointerException if {@code title} is {@code null}
+ * @throws IllegalArgumentException if {@code title} is empty
*/
@NonNull
public Builder setTitle(@NonNull CharSequence title) {
@@ -236,9 +251,9 @@
*
* <h2>Text Wrapping</h2>
*
- * This text is truncated at the end to fit in a single line below the title.
+ * This text is truncated at the end to fit in a single line below the title
*
- * @throws NullPointerException if {@code text} is {@code null}.
+ * @throws NullPointerException if {@code text} is {@code null}
*/
@NonNull
public Builder setText(@NonNull CharSequence text) {
@@ -249,7 +264,8 @@
/**
* Sets an image to show in the grid item with the default size {@link #IMAGE_TYPE_LARGE}.
*
- * @throws NullPointerException if {@code image} is {@code null}.
+ * @throws NullPointerException if {@code image} is {@code null}
+ *
* @see #setImage(CarIcon, int)
*/
@NonNull
@@ -274,9 +290,10 @@
* <p>See {@link CarIcon} for more details related to providing icon and image resources
* that work with different car screen pixel densities.
*
- * @param image the {@link CarIcon} to display.
- * @param imageType one of {@link #IMAGE_TYPE_ICON} or {@link #IMAGE_TYPE_LARGE}.
- * @throws NullPointerException if {@code image} is {@code null}.
+ * @param image the {@link CarIcon} to display
+ * @param imageType one of {@link #IMAGE_TYPE_ICON} or {@link #IMAGE_TYPE_LARGE}
+ *
+ * @throws NullPointerException if {@code image} is {@code null}
*/
@NonNull
public Builder setImage(@NonNull CarIcon image, @GridItemImageType int imageType) {
@@ -291,9 +308,9 @@
* {@code null} to make the grid item non-clickable.
*
* <p>Note that the listener relates to UI events and will be executed on the main thread
- * using {@link Looper#getMainLooper()}.
+ * using {@link Looper#getMainLooper()}
*
- * @throws NullPointerException if {@code onClickListener} is {@code null}.
+ * @throws NullPointerException if {@code onClickListener} is {@code null}
*/
@NonNull
@SuppressLint({"MissingGetterMatchingBuilder", "ExecutorRegistration"})
@@ -307,7 +324,7 @@
*
* @throws IllegalStateException if the grid item's title is not set, if the grid item's
* image is set when it is loading or vice versa, or if
- * the grid item is loading but the click listener is set.
+ * the grid item is loading but the click listener is set
*/
@NonNull
public GridItem build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
index 51dee65..e4dbc3e 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/GridTemplate.java
@@ -59,30 +59,57 @@
@Nullable
private final ActionStrip mActionStrip;
- public boolean isLoading() {
- return mIsLoading;
- }
-
+ /**
+ * Returns the title of the template or {@code null} if not set.
+ *
+ * @see Builder#setTitle(CharSequence)
+ */
@Nullable
public CarText getTitle() {
return mTitle;
}
+ /**
+ * Returns the {@link Action} that is set to be displayed in the header of the template, or
+ * {@code null} if not set.
+ *
+ * @see Builder#setHeaderAction(Action)
+ */
@Nullable
public Action getHeaderAction() {
return mHeaderAction;
}
- @Nullable
- public ItemList getSingleList() {
- return mSingleList;
- }
-
+ /**
+ * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+ *
+ * @see Builder#setActionStrip(ActionStrip)
+ */
@Nullable
public ActionStrip getActionStrip() {
return mActionStrip;
}
+ /**
+ * Returns whether the template is loading.
+ *
+ * @see Builder#setLoading(boolean)
+ */
+ public boolean isLoading() {
+ return mIsLoading;
+ }
+
+ /**
+ * Returns the {@link ItemList} instance that contains the grid items to display or {@code
+ * null} if not set.
+ *
+ * @see Builder#setSingleList(ItemList)
+ */
+ @Nullable
+ public ItemList getSingleList() {
+ return mSingleList;
+ }
+
@NonNull
@Override
public String toString() {
@@ -156,8 +183,7 @@
}
/**
- * Sets the {@link Action} that will be displayed in the header of the template, or
- * {@code null} to not display an action.
+ * Sets the {@link Action} that will be displayed in the header of the template.
*
* <p>Unless set with this method, the template will not have a header action.
*
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ItemList.java b/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
index 0bd9c1f..9721102 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
@@ -50,7 +50,7 @@
* <p>This event is called even if the selection did not change, for example, if the user
* selected an already selected item.
*
- * @param selectedIndex the index of the newly selected item.
+ * @param selectedIndex the index of the newly selected item
*/
void onSelected(int selectedIndex);
}
@@ -64,9 +64,9 @@
* first item in a list is visible, the start and end indices would be 0 and 1,
* respectively. If no items are visible, the indices will be set to -1.
*
- * @param startIndex the index of the first item that is visible.
+ * @param startIndex the index of the first item that is visible
* @param endIndex the index of the first item that is not visible after the visible
- * range.
+ * range
*/
void onItemVisibilityChanged(int startIndex, int endIndex);
}
@@ -85,21 +85,32 @@
@Nullable
private final CarText mNoItemsMessage;
- /** Returns the index of the selected item of the list. */
+ /**
+ * Returns the index of the selected item of the list.
+ *
+ * @see Builder#setSelectedIndex(int)
+ */
public int getSelectedIndex() {
return mSelectedIndex;
}
/**
* Returns the {@link OnSelectedDelegate} to be called when when an item is selected
- * by the user, or {@code null} is the list is non-selectable.
+ * by the user or {@code null} is the list is non-selectable.
+ *
+ * @see Builder#setOnSelectedListener(OnSelectedListener)
*/
@Nullable
public OnSelectedDelegate getOnSelectedDelegate() {
return mOnSelectedDelegate;
}
- /** Returns the text to be displayed if the list is empty. */
+ /**
+ * Returns the app-supplied text to be displayed if the list is empty or {@code null} if the
+ * default text will be used by the host.
+ *
+ * @see Builder#setNoItemsMessage(CharSequence)
+ */
@Nullable
public CarText getNoItemsMessage() {
return mNoItemsMessage;
@@ -107,7 +118,9 @@
/**
* Returns the {@link OnItemVisibilityChangedDelegate} to be called when the visible
- * items in the list changes.
+ * items in the list changes or {@code null} if one hasn't been set.
+ *
+ * @see Builder#setOnItemsVisibilityChangedListener(OnItemVisibilityChangedListener)
*/
@Nullable
public OnItemVisibilityChangedDelegate getOnItemVisibilityChangedDelegate() {
@@ -116,24 +129,14 @@
/**
* Returns the list of items in this {@link ItemList}.
+ *
+ * @see Builder#addItem(Item)
*/
@NonNull
public List<Item> getItems() {
return CollectionUtils.emptyIfNull(mItems);
}
- /**
- * Returns the list of items in this {@link ItemList}.
- *
- * @deprecated use {@link #getItems()} instead.
- */
- // TODO(b/177591128): remove after host(s) no longer reference this.
- @Deprecated
- @NonNull
- public List<Item> getItemList() {
- return CollectionUtils.emptyIfNull(mItems);
- }
-
@Override
@NonNull
public String toString() {
@@ -257,6 +260,7 @@
* radio button group, while others may highlight the selected item's background.
*
* @throws NullPointerException if {@code onSelectedListener} is {@code null}
+ *
* @see #setSelectedIndex(int)
*/
@NonNull
@@ -301,7 +305,7 @@
/**
* Adds an item to the list.
*
- * @throws NullPointerException if {@code item} is {@code null}.
+ * @throws NullPointerException if {@code item} is {@code null}
*/
@NonNull
public Builder addItem(@NonNull Item item) {
@@ -316,7 +320,7 @@
* the selected index is greater or equal to the size of the
* list, or if the list is selectable and any items have
* either one of their {@link OnClickListener} or
- * {@link Toggle} set.
+ * {@link Toggle} set
*/
@NonNull
public ItemList build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java
index 2adbe26..6e2e03a 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ListTemplate.java
@@ -22,8 +22,6 @@
import static java.util.Objects.requireNonNull;
-import android.annotation.SuppressLint;
-
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -72,45 +70,67 @@
@Nullable
private final ActionStrip mActionStrip;
- public boolean isLoading() {
- return mIsLoading;
- }
-
+ /**
+ * Returns the title of the template or {@code null} if not set.
+ *
+ * @see Builder#setTitle(CharSequence)
+ */
@Nullable
public CarText getTitle() {
return mTitle;
}
+ /**
+ * Returns the {@link Action} that is set to be displayed in the header of the template, or
+ * {@code null} if not set.
+ *
+ * @see Builder#setHeaderAction(Action)
+ */
@Nullable
public Action getHeaderAction() {
return mHeaderAction;
}
+ /**
+ * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+ *
+ * @see Builder#setActionStrip(ActionStrip)
+ */
+ @Nullable
+ public ActionStrip getActionStrip() {
+ return mActionStrip;
+ }
+
+ /**
+ * Returns whether the template is loading.
+ *
+ * @see Builder#setLoading(boolean)
+ */
+ public boolean isLoading() {
+ return mIsLoading;
+ }
+
+ /**
+ * Returns the {@link ItemList} instance containing the list of items to display or {@code
+ * null} if one hasn't been set.
+ *
+ * @see Builder#setSingleList(ItemList)
+ */
@Nullable
public ItemList getSingleList() {
return mSingleList;
}
/**
- * @deprecated use {@link #getSectionedLists()} instead.
+ * Returns the list of {@link SectionedItemList} instances to be displayed in the template.
+ *
+ * @see Builder#addSectionedList(SectionedItemList)
*/
- // TODO(b/177591128): remove after host(s) no longer reference this.
- @Deprecated
- @NonNull
- public List<SectionedItemList> getSectionLists() {
- return CollectionUtils.emptyIfNull(mSectionedLists);
- }
-
@NonNull
public List<SectionedItemList> getSectionedLists() {
return CollectionUtils.emptyIfNull(mSectionedLists);
}
- @Nullable
- public ActionStrip getActionStrip() {
- return mActionStrip;
- }
-
@NonNull
@Override
public String toString() {
@@ -183,7 +203,7 @@
* to the host once the data is ready.
*
* <p>If set to {@code false}, the UI will display the contents of the {@link ItemList}
- * instance(s) added via {@link #setSingleList} or {@link #addList}.
+ * instance(s) added via {@link #setSingleList} or {@link #addSectionedList}.
*/
@NonNull
public Builder setLoading(boolean isLoading) {
@@ -230,12 +250,10 @@
/**
* Sets a single {@link ItemList} to show in the template.
*
- * <p>Note that this list cannot be mixed with others added via {@link #addList}. If
- * multiple lists were previously added, they will be cleared.
+ * <p>Note that this list cannot be mixed with others added via {@link #addSectionedList}
+ * . If multiple lists were previously added, they will be cleared.
*
- * @throws NullPointerException if {@code list} is null.
- *
- * @see #addList(ItemList, CharSequence)
+ * @throws NullPointerException if {@code list} is null
*/
@NonNull
public Builder setSingleList(@NonNull ItemList list) {
@@ -246,34 +264,6 @@
}
/**
- * Adds an {@link ItemList} to display in the template.
- *
- * <p>Use this method to add multiple {@link ItemList}s to the template. Each
- * {@link ItemList} will be grouped under the given {@code header}. These lists cannot be
- * mixed with an {@link ItemList} added via {@link #setSingleList}. If a single list was
- * previously added, it will be cleared.
- *
- * <p>If the added {@link ItemList} contains a {@link ItemList.OnSelectedListener}, then it
- * cannot be added alongside other {@link ItemList}(s).
- *
- * @throws NullPointerException if {@code list} or {@code header} is {@code null}.
- * @throws IllegalArgumentException if {@code list} is empty, if {@code list}'s {@link
- * ItemList.OnItemVisibilityChangedListener} is set, if
- * {@code header} is empty, or if a selectable list is
- * added alongside other lists.
- *
- * @deprecated use {@link #addSectionedList} instead.
- */
- // TODO(b/177591128): remove after host(s) no longer reference this.
- @Deprecated
- @NonNull
- // TODO(shiufai): consider rename to match getter's name.
- @SuppressLint("MissingGetterMatchingBuilder")
- public Builder addList(@NonNull ItemList list, @NonNull CharSequence header) {
- return addSectionedList(SectionedItemList.create(list, header));
- }
-
- /**
* Adds an {@link SectionedItemList} to display in the template.
*
* <p>Use this method to add multiple lists to the template. Each
@@ -285,11 +275,11 @@
* {@link ItemList.OnSelectedListener}, then it cannot be added alongside other
* {@link SectionedItemList}(s).
*
- * @throws NullPointerException if {@code list} or {@code header} is {@code null}.
+ * @throws NullPointerException if {@code list} or {@code header} is {@code null}
* @throws IllegalArgumentException if {@code list} is empty, if {@code list}'s {@link
* ItemList.OnItemVisibilityChangedListener} is set, if
* {@code header} is empty, or if a selectable list is
- * added alongside other lists.
+ * added alongside other lists
*/
@NonNull
public Builder addSectionedList(@NonNull SectionedItemList list) {
@@ -320,7 +310,7 @@
}
/**
- * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
+ * Sets the {@link ActionStrip} for this template or {@code null} to not display an {@link
* ActionStrip}.
*
* <p>Unless set with this method, the template will not have an action strip.
@@ -331,7 +321,7 @@
* {@link Action}s, one of them can contain a title as set via
* {@link Action.Builder#setTitle}. Otherwise, only {@link Action}s with icons are allowed.
*
- * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements.
+ * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements
* @throws NullPointerException if {@code actionStrip} is {@code null}
*/
@NonNull
@@ -354,9 +344,9 @@
*
* @throws IllegalStateException if the template is in a loading state but there are
* lists added or vice versa, or if the template does
- * not have either a title or header {@link Action} set.
+ * not have either a title or header {@link Action} set
* @throws IllegalArgumentException if the added {@link ItemList}(s) do not meet the
- * template's requirements.
+ * template's requirements
*/
@NonNull
public ListTemplate build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
index bb1b0b2..6c0a53f 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/MessageTemplate.java
@@ -61,31 +61,63 @@
@Keep
private final List<Action> mActionList;
+ /**
+ * Returns the title of the template or {@code null} if not set.
+ *
+ * @see Builder#setTitle(CharSequence)
+ */
@Nullable
public CarText getTitle() {
return mTitle;
}
+ /**
+ * Returns the {@link Action} that is set to be displayed in the header of the template, or
+ * {@code null} if not set.
+ *
+ * @see Builder#setHeaderAction(Action)
+ */
@Nullable
public Action getHeaderAction() {
return mHeaderAction;
}
+ /**
+ * Returns the message to display in the template.
+ *
+ * @see Builder#Builder(CharSequence)
+ */
@Nullable
public CarText getMessage() {
return mMessage;
}
+ /**
+ * Returns a debug message to display in the template or {@code null} if not set.
+ *
+ * @see Builder#setDebugMessage(Throwable)
+ * @see Builder#setDebugMessage(String)
+ */
@Nullable
public CarText getDebugMessage() {
return mDebugMessage;
}
+ /**
+ * Returns the icon to display in the template or {@code null} if not set.
+ *
+ * @see Builder#setIcon(CarIcon)
+ */
@Nullable
public CarIcon getIcon() {
return mIcon;
}
+ /**
+ * Returns the list of actions to display in the template.
+ *
+ * @see Builder#addAction(Action)
+ */
@NonNull
public List<Action> getActions() {
return CollectionUtils.emptyIfNull(mActionList);
@@ -161,7 +193,7 @@
*
* <p>Unless set with this method, the template will not have a title.
*
- * @throws NullPointerException if {@code title} is null
+ * @throws NullPointerException if {@code title} is {@code null}
*/
@NonNull
public Builder setTitle(@NonNull CharSequence title) {
@@ -170,7 +202,7 @@
}
/**
- * Sets a {@link Throwable} for debugging purposes, or {@code null} to not show it.
+ * Sets a {@link Throwable} for debugging purposes.
*
* <p>The cause will be displayed along with the message set in
* {@link #setDebugMessage(String)}.
@@ -188,7 +220,7 @@
}
/**
- * Sets a debug message for debugging purposes, or {@code null} to not show a debug message.
+ * Sets a debug message for debugging purposes.
*
* <p>The debug message will be displayed along with the cause set in
* {@link #setDebugMessage}.
@@ -229,8 +261,7 @@
}
/**
- * Sets the {@link Action} that will be displayed in the header of the template, or
- * {@code null} to not display an action.
+ * Sets the {@link Action} that will be displayed in the header of the template.
*
* <p>Unless set with this method, the template will not have a header action.
*
@@ -256,7 +287,7 @@
*
* <p>Any actions above the maximum limit of 2 will be ignored.
*
- * @throws NullPointerException if {@code action} is {@code null}.
+ * @throws NullPointerException if {@code action} is {@code null}
*/
@NonNull
public Builder addAction(@NonNull Action action) {
@@ -275,7 +306,7 @@
* <p>Either a header {@link Action} or title must be set on the template.
*
* @throws IllegalStateException if the message is empty, or if the template does not have
- * either a title or header {@link Action} set.
+ * either a title or header {@link Action} set
*/
@NonNull
public MessageTemplate build() {
@@ -302,8 +333,9 @@
/**
* Returns a {@link Builder} instance.
*
- * @param message the text message to display in the template.
- * @throws NullPointerException if the {@code message} is {@code null}.
+ * @param message the text message to display in the template
+ *
+ * @throws NullPointerException if the {@code message} is {@code null}
*/
public Builder(@NonNull CharSequence message) {
this.mMessage = CarText.create(requireNonNull(message));
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Metadata.java b/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
index e5ff857..ec59bc7 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Metadata.java
@@ -33,6 +33,11 @@
@Nullable
private final Place mPlace;
+ /**
+ * Returns a {@link Place} instance set in the metadata.
+ *
+ * @see Builder#setPlace(Place)
+ */
@Nullable
public Place getPlace() {
return mPlace;
@@ -72,7 +77,7 @@
/**
* Sets a {@link Place} used for showing {@link Distance} and {@link PlaceMarker}
- * information, or {@code null} if no {@link Place} information is available.
+ * information.
*
* @throws NullPointerException if {@code place} is {@code null}
*/
@@ -97,7 +102,7 @@
/**
* Returns a new {@link Builder} with the data from the given {@link Metadata} instance.
*
- * @throws NullPointerException if {@code icon} is {@code null}.
+ * @throws NullPointerException if {@code icon} is {@code null}
*/
public Builder(@NonNull Metadata metadata) {
this.mPlace = requireNonNull(metadata).getPlace();
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ModelUtils.java b/car/app/app/src/main/java/androidx/car/app/model/ModelUtils.java
index 67bcd64..09c5203 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ModelUtils.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ModelUtils.java
@@ -39,7 +39,7 @@
*
* @throws IllegalArgumentException if the input list contains any non-Row instances, or if any
* non-browsable row does not have a {@link DistanceSpan}
- * instance.
+ * instance
*/
public static void validateAllNonBrowsableRowsHaveDistance(@NonNull List<Item> rows) {
int spanSetCount = 0;
@@ -72,7 +72,7 @@
*
* @throws IllegalArgumentException if the input list contains any non-Row instances, or if any
* non-browsable row does not have either a {@link
- * DurationSpan} or {@link DistanceSpan} instance.
+ * DurationSpan} or {@link DistanceSpan} instance
*/
public static void validateAllRowsHaveDistanceOrDuration(@NonNull List<Item> rows) {
for (Item rowObj : rows) {
@@ -94,7 +94,7 @@
* Checks whether all rows have only small-sized images if they are set.
*
* @throws IllegalArgumentException if the input list contains any non-Row instances, or if an
- * image set in any rows is using {@link Row#IMAGE_TYPE_LARGE}.
+ * image set in any rows is using {@link Row#IMAGE_TYPE_LARGE}
*/
public static void validateAllRowsHaveOnlySmallImages(@NonNull List<Item> rows) {
for (Item rowObj : rows) {
@@ -112,7 +112,7 @@
* Checks whether any rows have both a marker and an image.
*
* @throws IllegalArgumentException if the input list contains any non-Row instances, or if
- * both a marker and an image are set in a row.
+ * both a marker and an image are set in a row
*/
public static void validateNoRowsHaveBothMarkersAndImages(@NonNull List<Item> rows) {
for (Item rowObj : rows) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegate.java
index 078fc40..b63b535 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegate.java
@@ -28,9 +28,9 @@
/**
* Notifies that checked state has changed.
*
- * @param isChecked the updated checked state.
+ * @param isChecked the updated checked state
* @param callback the {@link OnDoneCallback} to trigger when the client finishes handling
- * the event.
+ * the event
*/
// This mirrors the AIDL class and is not supported to support an executor as an input.
@SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegate.java
index 41a64e8..7993740 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegate.java
@@ -34,7 +34,7 @@
* Notifies that a click happened.
*
* @param callback the {@link OnDoneCallback} to trigger when the client finishes handling
- * the event.
+ * the event
*/
// This mirrors the AIDL class and is not supported to support an executor as an input.
@SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegate.java
index 6b2e423..f3ef897 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegate.java
@@ -33,11 +33,11 @@
* respectively. If no items are visible, the indices will be set to -1.
*
* @param startIndex the index (inclusive) of the first visible element, or -1 if no items
- * are visible.
+ * are visible
* @param endIndex the index (exclusive) of the last visible element, or -1 if no items
- * are visible.
+ * are visible
* @param callback the {@link OnDoneCallback} to trigger when the client finishes handling
- * the event.
+ * the event
*/
// This mirrors the AIDL class and is not supported to support an executor as an input.
@SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegate.java
index 3d955ca..72ba285 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegate.java
@@ -31,9 +31,9 @@
* <p>This event is called even if the selection did not change, for example, if the user
* selected an already selected item.
*
- * @param selectedIndex the index of the selected item.
+ * @param selectedIndex the index of the selected item
* @param callback the {@link OnDoneCallback} to trigger when the client finishes handling
- * the event.
+ * the event
*/
// This mirrors the AIDL class and is not supported to support an executor as an input.
@SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Pane.java b/car/app/app/src/main/java/androidx/car/app/model/Pane.java
index 8b1bfd5..d1beb9a 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Pane.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Pane.java
@@ -41,6 +41,15 @@
private final boolean mIsLoading;
/**
+ * Returns whether the pane is in a loading state.
+ *
+ * @see Builder#setLoading(boolean)
+ */
+ public boolean isLoading() {
+ return mIsLoading;
+ }
+
+ /**
* Returns the list of {@link Action}s displayed alongside the {@link Row}s in this pane.
*/
@NonNull
@@ -56,25 +65,6 @@
return CollectionUtils.emptyIfNull(mRows);
}
- /**
- * Returns the list of {@link Row} objects that make up the {@link Pane}.
- *
- * @deprecated use {@link #getRows()} instead.
- */
- // TODO(b/177591128): remove after host(s) no longer reference this.
- @Deprecated
- @NonNull
- public List<Row> getRowList() {
- return CollectionUtils.emptyIfNull(mRows);
- }
-
- /**
- * Returns the {@code true} if the {@link Pane} is loading.*
- */
- public boolean isLoading() {
- return mIsLoading;
- }
-
@Override
@NonNull
public String toString() {
@@ -144,7 +134,7 @@
/**
* Adds a row to display in the list.
*
- * @throws NullPointerException if {@code row} is {@code null}.
+ * @throws NullPointerException if {@code row} is {@code null}
*/
@NonNull
public Builder addRow(@NonNull Row row) {
@@ -157,7 +147,7 @@
*
* <p>By default, no actions are displayed.
*
- * @throws NullPointerException if {@code action} is {@code null}.
+ * @throws NullPointerException if {@code action} is {@code null}
*/
@NonNull
public Builder addAction(@NonNull Action action) {
@@ -170,7 +160,7 @@
* Constructs the row list defined by this builder.
*
* @throws IllegalStateException if the pane is in loading state and also contains rows, or
- * vice versa.
+ * vice versa
*/
@NonNull
public Pane build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java
index 4d9de4e..a065cbf 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/PaneTemplate.java
@@ -59,26 +59,47 @@
@Nullable
private final ActionStrip mActionStrip;
+ /**
+ * Returns the title of the template or {@code null} if not set.
+ *
+ * @see Builder#setTitle(CharSequence)
+ */
@Nullable
public CarText getTitle() {
return mTitle;
}
+ /**
+ * Returns the {@link Action} that is set to be displayed in the header of the template, or
+ * {@code null} if not set.
+ *
+ * @see Builder#setHeaderAction(Action)
+ */
@Nullable
public Action getHeaderAction() {
return mHeaderAction;
}
- @Nullable
- public Pane getPane() {
- return mPane;
- }
-
+ /**
+ * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+ *
+ * @see Builder#setActionStrip(ActionStrip)
+ */
@Nullable
public ActionStrip getActionStrip() {
return mActionStrip;
}
+ /**
+ * Returns the {@link Pane} to display in the template or {@code null} if not set.
+ *
+ * @see Builder#Builder(Pane)
+ */
+ @Nullable
+ public Pane getPane() {
+ return mPane;
+ }
+
@NonNull
@Override
public String toString() {
@@ -136,7 +157,7 @@
*
* <p>Unless set with this method, the template will not have a title.
*
- * @throws NullPointerException if {@code title} is null
+ * @throws NullPointerException if {@code title} is {@code null}
*/
@NonNull
public Builder setTitle(@NonNull CharSequence title) {
@@ -145,8 +166,7 @@
}
/**
- * Sets the {@link Action} that will be displayed in the header of the template, or
- * {@code null} to not display an action.
+ * Sets the {@link Action} that will be displayed in the header of the template.
*
* <p>Unless set with this method, the template will not have a header action.
*
@@ -168,8 +188,7 @@
}
/**
- * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
- * ActionStrip}.
+ * Sets the {@link ActionStrip} for this template.
*
* <p>Unless set with this method, the template will not have an action strip.
*
@@ -179,7 +198,7 @@
* {@link Action}s, one of them can contain a title as set via
* {@link Action.Builder#setTitle}. Otherwise, only {@link Action}s with icons are allowed.
*
- * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements.
+ * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements
* @throws NullPointerException if {@code actionStrip} is {@code null}
*/
@NonNull
@@ -203,10 +222,9 @@
*
* <p>Either a header {@link Action} or title must be set on the template.
*
- * @throws IllegalArgumentException if the {@link Pane} does not meet the requirements.
+ * @throws IllegalArgumentException if the {@link Pane} does not meet the requirements
* @throws IllegalStateException if the template does not have either a title or header
- * {@link
- * Action} set.
+ * {@link Action} set
*/
@NonNull
public PaneTemplate build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ParkedOnlyOnClickListener.java b/car/app/app/src/main/java/androidx/car/app/model/ParkedOnlyOnClickListener.java
index 0b6c942..08d2e14 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ParkedOnlyOnClickListener.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ParkedOnlyOnClickListener.java
@@ -47,6 +47,10 @@
@Keep
private final OnClickListener mListener;
+ /**
+ * Triggers the {@link OnClickListener#onClick()} method in the listener wrapped by this
+ * object.
+ */
@Override
public void onClick() {
mListener.onClick();
@@ -58,7 +62,7 @@
* <p>Note that the listener relates to UI events and will be executed on the main thread
* using {@link Looper#getMainLooper()}.
*
- * @throws NullPointerException if {@code listener} is {@code null}.
+ * @throws NullPointerException if {@code listener} is {@code null}
*/
@NonNull
@SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Place.java b/car/app/app/src/main/java/androidx/car/app/model/Place.java
index 190ab06..a719a09 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Place.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Place.java
@@ -33,13 +33,22 @@
@Nullable
private final PlaceMarker mMarker;
+ /**
+ * Returns the {@link PlaceMarker} object associated with this place or {@code null} if one
+ * is not set.
+ *
+ * @see Builder#setMarker(PlaceMarker)
+ */
@Nullable
public PlaceMarker getMarker() {
return mMarker;
}
/**
- * @return the {@link CarLocation} set for this Place instance.
+ * Returns the {@link CarLocation} instance associated with this place or {@code null} if one
+ * is not set.
+ *
+ * @see Builder#Builder(CarLocation)
*/
@Nullable
public CarLocation getLocation() {
@@ -92,6 +101,7 @@
* Returns a builder instance for a {@link CarLocation}.
*
* @param location the geographical location associated with the place
+ *
* @throws NullPointerException if {@code location} is {@code null}
*/
public Builder(@NonNull CarLocation location) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java
index 8556dce..d3664b3c 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/PlaceListMapTemplate.java
@@ -74,30 +74,62 @@
return mShowCurrentLocation;
}
+ /**
+ * Returns the title of the template or {@code null} if not set.
+ *
+ * @see Builder#setTitle(CharSequence)
+ */
@Nullable
public CarText getTitle() {
return mTitle;
}
- public boolean isLoading() {
- return mIsLoading;
- }
-
- @Nullable
- public ItemList getItemList() {
- return mItemList;
- }
-
+ /**
+ * Returns the {@link Action} that is set to be displayed in the header of the template, or
+ * {@code null} if not set.
+ *
+ * @see Builder#setHeaderAction(Action)
+ */
@Nullable
public Action getHeaderAction() {
return mHeaderAction;
}
+ /**
+ * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+ *
+ * @see Builder#setActionStrip(ActionStrip)
+ */
@Nullable
public ActionStrip getActionStrip() {
return mActionStrip;
}
+ /**
+ * Returns whether the template is loading.
+ *
+ * @see Builder#setLoading(boolean)
+ */
+ public boolean isLoading() {
+ return mIsLoading;
+ }
+
+ /**
+ * Returns the {@link ItemList} instance with the list of items to display in the template,
+ * or {@code null} if not set.
+ *
+ * @see Builder#setItemList(ItemList)
+ */
+ @Nullable
+ public ItemList getItemList() {
+ return mItemList;
+ }
+
+ /**
+ * Returns the {@link Place} instance to display as an anchor in the map.
+ *
+ * @see Builder#setAnchor(Place)
+ */
@Nullable
public Place getAnchor() {
return mAnchor;
@@ -202,8 +234,7 @@
}
/**
- * Sets the {@link Action} that will be displayed in the header of the template, or
- * {@code null} to not display an action.
+ * Sets the {@link Action} that will be displayed in the header of the template.
*
* <p>Unless set with this method, the template will not have a header action.
*
@@ -229,7 +260,7 @@
*
* <p>Unless set with this method, the template will not have a title.
*
- * @throws NullPointerException if {@code title} is null
+ * @throws NullPointerException if {@code title} is {@code null}
*/
@NonNull
public Builder setTitle(@NonNull CharSequence title) {
@@ -278,8 +309,7 @@
}
/**
- * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
- * ActionStrip}.
+ * Sets the {@link ActionStrip} for this template.
*
* <p>Unless set with this method, the template will not have an action strip.
*
@@ -289,7 +319,7 @@
* {@link Action}s, one of them can contain a title as set via
* {@link Action.Builder#setTitle}. Otherwise, only {@link Action}s with icons are allowed.
*
- * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements.
+ * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements
* @throws NullPointerException if {@code actionStrip} is {@code null}
*/
@NonNull
@@ -329,9 +359,9 @@
* Either a header {@link Action} or title must be set on the template.
*
* @throws IllegalArgumentException if the template is in a loading state but the list is
- * set, or vice versa.
+ * set, or vice versa
* @throws IllegalStateException if the template does not have either a title or header
- * {@link Action} set.
+ * {@link Action} set
*/
@NonNull
public PlaceListMapTemplate build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java b/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java
index 20c5597..778c467 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/PlaceMarker.java
@@ -75,7 +75,7 @@
private final int mIconType;
/**
- * Returns the {@link CarIcon} associated with this marker.
+ * Returns the {@link CarIcon} associated with this marker or {@code null} if not set.
*/
@Nullable
public CarIcon getIcon() {
@@ -91,11 +91,11 @@
}
/**
- * If set, the text that should be rendered as the marker's content, {@code null} otherwise.
+ * Returns the text that should be rendered as the marker's content or {@code null} if one
+ * is not set.
*
* <p>Note that a {@link PlaceMarker} can only display either an icon or a text label. If
- * both are
- * set, then {@link #getIcon()} will take precedence.
+ * both are set, then {@link #getIcon()} will take precedence.
*/
@Nullable
public CarText getLabel() {
@@ -196,6 +196,7 @@
*
* @param icon the {@link CarIcon} to display inside the marker
* @param iconType one of {@link #TYPE_ICON} or {@link #TYPE_IMAGE}
+ *
* @throws NullPointerException if the {@code icon} is {@code null}
*/
@NonNull
@@ -215,7 +216,8 @@
*
* @param label the text to display inside of the marker. The string must have a maximum
* size of 3 characters. Set to {@code null} to let the host choose a
- * labelling scheme (for example, using a sequence of numbers).
+ * labelling scheme (for example, using a sequence of numbers)
+ *
* @throws NullPointerException if the {@code label} is {@code null}
*/
@NonNull
@@ -239,7 +241,7 @@
* <li>When the {@link PlaceMarker} is displayed on the map, the pin enclosing the icon or
* label will be painted using the given color.
* <li>When the {@link PlaceMarker} is displayed on the list, the color will be applied
- * if the content is a label. A label rendered inside a map's pin cannot be color
+ * if the content is a label. A label rendered inside a map's pin cannot be colored
* and will always use the default color as chosen by the host.
* </ul>
*
@@ -263,7 +265,7 @@
* Constructs the {@link PlaceMarker} defined by this builder.
*
* @throws IllegalStateException if the icon is of the type {@link #TYPE_IMAGE} and a a
- * color is set.
+ * color is set
*/
@NonNull
public PlaceMarker build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Row.java b/car/app/app/src/main/java/androidx/car/app/model/Row.java
index 648c994..3ed1bb2 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Row.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Row.java
@@ -51,7 +51,6 @@
*
* @hide
*/
- // TODO(shiufai): investigate how to expose IntDefs if needed.
@RestrictTo(LIBRARY)
@IntDef(value = {IMAGE_TYPE_SMALL, IMAGE_TYPE_ICON, IMAGE_TYPE_LARGE})
@Retention(RetentionPolicy.SOURCE)
@@ -78,8 +77,7 @@
* Represents a small image to be displayed in the row.
*
* <p>If necessary, icons will be scaled down to fit within a 44 x 44 dp bounding box,
- * preserving
- * their aspect ratios.
+ * preserving their aspect ratios.
*
* <p>A tint color is expected to be provided via {@link CarIcon.Builder#setTint}. Otherwise, a
* default tint color as determined by the host will be applied.
@@ -109,19 +107,33 @@
@RowImageType
private final int mRowImageType;
- /** Returns the title of the row. */
+ /**
+ * Returns the title of the row or {@code null} if not set.
+ *
+ * @see Builder#setTitle(CharSequence)
+ */
@Nullable
public CarText getTitle() {
return mTitle;
}
- /** Returns the list of text below the title. */
+ /**
+ * Returns the list of text below the title.
+ *
+ * @see Builder#addText(CharSequence)
+ */
@NonNull
public List<CarText> getTexts() {
return CollectionUtils.emptyIfNull(mTexts);
}
- /** Returns the image of the row. */
+ /**
+ * Returns the image to display in the row or {@code null} if the row does not contain an
+ * image.
+ *
+ * @see Builder#setImage(CarIcon)
+ * @see Builder#setImage(CarIcon, int)
+ */
@Nullable
public CarIcon getImage() {
return mImage;
@@ -136,6 +148,8 @@
/**
* Returns the {@link Toggle} in the row or {@code null} if the row does not contain a
* toggle.
+ *
+ * @see Builder#setToggle(Toggle)
*/
@Nullable
public Toggle getToggle() {
@@ -143,16 +157,18 @@
}
/**
- * Returns {@code true} if the row is browsable, {@code false} otherwise.
+ * Returns whether the row is browsable.
*
* <p>If a row is browsable, then no {@link Action} or {@link Toggle} can be added to it.
+ *
+ * @see Builder#isBrowsable()
*/
public boolean isBrowsable() {
return mIsBrowsable;
}
/**
- * Returns the {@link OnClickListener} to be called back when the row is clicked, or {@code
+ * Returns the {@link OnClickListener} to be called back when the row is clicked or {@code
* null} if the row is non-clickable.
*/
@Nullable
@@ -161,7 +177,8 @@
}
/**
- * Returns the {@link Metadata} associated with the row.
+ * Returns the {@link Metadata} associated with the row or {@code null} if there is no
+ * metadata associated with the row.
*/
@Nullable
public Metadata getMetadata() {
@@ -278,8 +295,8 @@
/**
* Sets the title of the row.
*
- * @throws NullPointerException if {@code title} is {@code null}.
- * @throws IllegalArgumentException if {@code title} is empty.
+ * @throws NullPointerException if {@code title} is {@code null}
+ * @throws IllegalArgumentException if {@code title} is empty
*/
@NonNull
public Builder setTitle(@NonNull CharSequence title) {
@@ -352,7 +369,8 @@
* of text
* </pre>
*
- * @throws NullPointerException if {@code text} is {@code null}.
+ * @throws NullPointerException if {@code text} is {@code null}
+ *
* @see ForegroundCarColorSpan
*/
@NonNull
@@ -365,6 +383,7 @@
* Sets an image to show in the row with the default size {@link #IMAGE_TYPE_SMALL}.
*
* @throws NullPointerException if {@code image} is {@code null}
+ *
* @see #setImage(CarIcon, int)
*/
@NonNull
@@ -389,9 +408,10 @@
* <p>See {@link CarIcon} for more details related to providing icon and image resources
* that work with different car screen pixel densities.
*
- * @param image the {@link CarIcon} to display, or {@code null} to not display one.
+ * @param image the {@link CarIcon} to display or {@code null} to not display one
* @param imageType one of {@link #IMAGE_TYPE_ICON}, {@link #IMAGE_TYPE_SMALL} or {@link
* #IMAGE_TYPE_LARGE}
+ *
* @throws NullPointerException if {@code image} is {@code null}
*/
@NonNull
@@ -428,8 +448,7 @@
}
/**
- * Sets the {@link OnClickListener} to be called back when the row is clicked, or {@code
- * null} to make the row non-clickable.
+ * Sets the {@link OnClickListener} to be called back when the row is clicked.
*
* <p>Note that the listener relates to UI events and will be executed on the main thread
* using {@link Looper#getMainLooper()}.
@@ -448,7 +467,7 @@
* Sets the {@link Metadata} associated with the row.
*
* @param metadata The metadata to set with the row. Pass {@link Metadata#EMPTY_METADATA}
- * to not associate any metadata with the row.
+ * to not associate any metadata with the row
*/
@NonNull
public Builder setMetadata(@NonNull Metadata metadata) {
@@ -463,7 +482,7 @@
* row and has a {@link Toggle}, if it is a browsable
* row but does not have a {@link OnClickListener}, or if
* it has both a {@link OnClickListener} and a {@link
- * Toggle}.
+ * Toggle}
*/
@NonNull
public Row build() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegate.java b/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegate.java
index f928dee..15d6a23 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegate.java
@@ -28,9 +28,9 @@
/**
* Notifies that the search text has changed.
*
- * @param searchText the up-to-date search text.
+ * @param searchText the up-to-date search text
* @param callback the {@link OnDoneCallback} to trigger when the client finishes handling
- * the event.
+ * the event
*/
// This mirrors the AIDL class and is not supported to support an executor as an input.
@SuppressLint("ExecutorRegistration")
@@ -39,9 +39,9 @@
/**
* Notifies that the user has submitted the search.
*
- * @param searchText the search text that was submitted.
+ * @param searchText the search text that was submitted
* @param callback the {@link OnDoneCallback} to trigger when the client finishes handling
- * the event.
+ * the event
*/
// This mirrors the AIDL class and is not supported to support an executor as an input.
@SuppressLint("ExecutorRegistration")
diff --git a/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java
index e25585f..30e01c3 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/SearchTemplate.java
@@ -53,7 +53,7 @@
* these updates is not guaranteed to be after every individual keystroke. The host may
* decide to wait for several keystrokes before sending a single update.
*
- * @param searchText the current search text that the user has typed.
+ * @param searchText the current search text that the user has typed
*/
void onSearchTextChanged(@NonNull String searchText);
@@ -61,7 +61,7 @@
* Notifies that the user has submitted the search and the given {@code searchText} is
* the final term.
*
- * @param searchText the search text that the user typed.
+ * @param searchText the search text that the user typed
*/
void onSearchSubmitted(@NonNull String searchText);
}
@@ -88,17 +88,21 @@
@Nullable
private final ActionStrip mActionStrip;
- public boolean isLoading() {
- return mIsLoading;
- }
-
+ /**
+ * Returns the {@link Action} that is set to be displayed in the header of the template, or
+ * {@code null} if not set.
+ *
+ * @see Builder#setHeaderAction(Action)
+ */
@Nullable
public Action getHeaderAction() {
return mHeaderAction;
}
/**
- * Returns the {@link ActionStrip} instance set in the template.
+ * Returns the {@link ActionStrip} for this template or {@code null} if not set.
+ *
+ * @see Builder#setActionStrip(ActionStrip)
*/
@Nullable
public ActionStrip getActionStrip() {
@@ -106,6 +110,15 @@
}
/**
+ * Returns whether the template is loading.
+ *
+ * @see Builder#setLoading(boolean)
+ */
+ public boolean isLoading() {
+ return mIsLoading;
+ }
+
+ /**
* Returns the optional initial search text.
*
* @see Builder#setInitialSearchText
@@ -126,7 +139,7 @@
}
/**
- * Returns the optional {@link ItemList} for search results.
+ * Returns the {@link ItemList} for search results or {@code null} if not set.
*
* @see Builder#getItemList
*/
@@ -136,7 +149,8 @@
}
/**
- * Returns the {@link SearchCallbackDelegate} for search callbacks.
+ * Returns the {@link SearchCallbackDelegate} for search callbacks or {@code null} if one is
+ * not set.
*/
@Nullable
public SearchCallbackDelegate getSearchCallbackDelegate() {
@@ -230,8 +244,7 @@
ActionStrip mActionStrip;
/**
- * Sets the {@link Action} that will be displayed in the header of the template, or
- * {@code null} to not display an action.
+ * Sets the {@link Action} that will be displayed in the header of the template.
*
* <p>Unless set with this method, the template will not have a header action.
*
@@ -253,8 +266,7 @@
}
/**
- * Sets the {@link ActionStrip} for this template, or {@code null} to not display an {@link
- * ActionStrip}.
+ * Sets the {@link ActionStrip} for this template.
*
* <p>Unless set with this method, the template will not have an action strip.
*
@@ -264,7 +276,7 @@
* {@link Action}s, one of them can contain a title as set via
* {@link Action.Builder#setTitle}. Otherwise, only {@link Action}s with icons are allowed.
*
- * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements.
+ * @throws IllegalArgumentException if {@code actionStrip} does not meet the requirements
* @throws NullPointerException if {@code actionStrip} is {@code null}
*/
@NonNull
@@ -276,6 +288,7 @@
/**
* Sets the initial search text to display in the search box.
+ *
* @throws NullPointerException if {@code initialSearchText} is {@code null}
*/
@NonNull
@@ -287,7 +300,7 @@
/**
* Sets the text hint to display in the search box when it is empty.
*
- * <p>The host will use a default search hint if one is not set with this method.
+ * <p>The host will use a default search hint if not set with this method.
*
* <p>This is not the actual search text, and will disappear if user types any value into
* the search.
@@ -358,7 +371,7 @@
* Constructs the {@link SearchTemplate} model.
*
* @throws IllegalArgumentException if the template is in a loading state but the list is
- * set.
+ * set
*/
@NonNull
public SearchTemplate build() {
@@ -377,7 +390,7 @@
* using {@link Looper#getMainLooper()}.
*
* @param callback the callback to be invoked for events such as when the user types new
- * text, or submits a search.
+ * text, or submits a search
*/
@SuppressLint("ExecutorRegistration")
public Builder(@NonNull SearchCallback callback) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/SectionedItemList.java b/car/app/app/src/main/java/androidx/car/app/model/SectionedItemList.java
index 19f3c32..419b56c 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/SectionedItemList.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/SectionedItemList.java
@@ -38,20 +38,6 @@
/**
* Creates an instance of a {@link SectionedItemList} with the given {@code itemList} and
* {@code sectionHeader}.
- *
- * @deprecated use {@link #create(ItemList, CharSequence)} ()} instead.
- */
- // TODO(b/177591128): remove after host(s) no longer reference this.
- @Deprecated
- @NonNull
- public static SectionedItemList create(
- @NonNull ItemList itemList, @NonNull CarText sectionHeader) {
- return new SectionedItemList(requireNonNull(itemList), requireNonNull(sectionHeader));
- }
-
- /**
- * Creates an instance of a {@link SectionedItemList} with the given {@code itemList} and
- * {@code sectionHeader}.
*/
@NonNull
public static SectionedItemList create(
@@ -60,13 +46,13 @@
CarText.create(requireNonNull(sectionHeader)));
}
- /** Returns the {@link ItemList} for the section. */
+ /** Returns the {@link ItemList} for the section or {@code null} if not set. */
@Nullable
public ItemList getItemList() {
return mItemList;
}
- /** Returns the title of the section. */
+ /** Returns the title of the section or {@code null} if not set */
@Nullable
public CarText getHeader() {
return mHeader;
@@ -102,7 +88,6 @@
this.mHeader = header;
}
- /** For serialization. */
private SectionedItemList() {
this.mItemList = null;
this.mHeader = null;
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TemplateInfo.java b/car/app/app/src/main/java/androidx/car/app/model/TemplateInfo.java
index 4d33ad2..148008c 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TemplateInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TemplateInfo.java
@@ -16,6 +16,8 @@
package androidx.car.app.model;
+import static java.util.Objects.requireNonNull;
+
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -30,15 +32,17 @@
*/
public final class TemplateInfo {
@Keep
+ @Nullable
private final Class<? extends Template> mTemplateClass;
@Keep
+ @Nullable
private final String mTemplateId;
/**
* Constructs the info for the given template information provided.
*
* @param templateClass the class of the template this info is for
- * @param templateId the unique id for the template
+ * @param templateId the unique id for the template
*/
public TemplateInfo(@NonNull Class<? extends Template> templateClass,
@NonNull String templateId) {
@@ -47,28 +51,27 @@
}
/**
- * @deprecated this constructor is deprecated.
+ * Returns the type of template this instance contains.
+ *
+ * @see TemplateInfo#TemplateInfo(Class, String)
*/
- // TODO(b/178104682): Remove this method.
- @Deprecated
- public TemplateInfo(@NonNull Template template, @NonNull String templateId) {
- this.mTemplateClass = template.getClass();
- this.mTemplateId = templateId;
- }
-
- private TemplateInfo() {
- this.mTemplateClass = null;
- this.mTemplateId = null;
- }
-
@NonNull
public Class<? extends Template> getTemplateClass() {
- return mTemplateClass;
+ // Intentionally kept as non-null because the library creates these classes internally after
+ // the app returns a non-null template, a null-value should not be expected here.
+ return requireNonNull(mTemplateClass);
}
+ /**
+ * Returns the ID of the template.
+ *
+ * @see TemplateInfo#TemplateInfo(Class, String)
+ */
@NonNull
public String getTemplateId() {
- return mTemplateId;
+ // Intentionally kept as non-null because the library creates these classes internally after
+ // the app returns a non-null template, a null-value should not be expected here.
+ return requireNonNull(mTemplateId);
}
@Override
@@ -89,4 +92,9 @@
return Objects.equals(mTemplateClass, otherInfo.mTemplateClass)
&& Objects.equals(mTemplateId, otherInfo.mTemplateId);
}
+
+ private TemplateInfo() {
+ this.mTemplateClass = null;
+ this.mTemplateId = null;
+ }
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TemplateWrapper.java b/car/app/app/src/main/java/androidx/car/app/model/TemplateWrapper.java
index d3c5d88..699708e 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TemplateWrapper.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TemplateWrapper.java
@@ -22,6 +22,7 @@
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.car.app.utils.CollectionUtils;
@@ -41,19 +42,18 @@
*/
public final class TemplateWrapper {
@Keep
+ @Nullable
private Template mTemplate;
@Keep
+ @Nullable
private String mId;
@Keep
private List<TemplateInfo> mTemplateInfoForScreenStack = new ArrayList<>();
- /** The current step in a task that the template is in. For internal, host-side use only. */
+ /** The current step in a task that the template is in. */
private int mCurrentTaskStep;
- /**
- * Whether the template wrapper is a refresh of the current template. For internal, host-side
- * use only.
- */
+ /** Whether the template wrapper is a refresh of the current template. */
private boolean mIsRefresh;
/**
@@ -80,14 +80,12 @@
* Creates a {@link TemplateWrapper} instance with the given {@link Template} and ID.
*
* <p>The ID is primarily used to inform the host that the given {@link Template} shares the
- * same
- * ID as a previously sent {@link Template}, even though their contents differ. In such
- * cases, the
- * host will reset the task step to where the previous {@link Template} was.
+ * same ID as a previously sent {@link Template}, even though their contents differ. In such
+ * cases, the host will reset the task step to where the previous {@link Template} was.
*
- * <p>For example, the client sends Template A (task step 1), then move forwards a screen and
- * sends Template B (task step 2). Now the client pops the screen and sends Template C. By
- * assigning the ID of Template A to Template C, the client library informs the host that it
+ * <p>For example, the client sends template A (task step 1), then move forwards a screen and
+ * sends template B (task step 2). Now the client pops the screen and sends template C. By
+ * assigning the ID of template A to template C, the client library informs the host that it
* is a back operation and the task step should be set to 1 again.
*/
@NonNull
@@ -98,13 +96,17 @@
/** Returns the wrapped {@link Template}. */
@NonNull
public Template getTemplate() {
- return mTemplate;
+ // Intentionally kept as non-null because the library creates these classes internally after
+ // the app returns a non-null template, a null-value should not be expected here.
+ return requireNonNull(mTemplate);
}
/** Returns the ID associated with the wrapped {@link Template}. */
@NonNull
public String getId() {
- return mId;
+ // Intentionally kept as non-null because the library creates these classes internally after
+ // the app returns a non-null template, a null-value should not be expected here.
+ return requireNonNull(mId);
}
/**
@@ -125,24 +127,19 @@
* screen stack managed by the screen manager.
*
* <p>The return values are in order, where position 0 is the top of the stack, and position
- * n is
- * the bottom of the stack given n screens on the stack.
+ * n is the bottom of the stack given n screens on the stack.
*/
@NonNull
public List<TemplateInfo> getTemplateInfosForScreenStack() {
return CollectionUtils.emptyIfNull(mTemplateInfoForScreenStack);
}
- /**
- * Retrieves the current task step that the template is in. For internal, host-side use only.
- */
+ /** Retrieves the current task step that the template is in. */
public int getCurrentTaskStep() {
return mCurrentTaskStep;
}
- /**
- * Sets the current task step that the template is in. For internal, host-side use only.
- */
+ /** Sets the current task step that the template is in. */
public void setCurrentTaskStep(int currentTaskStep) {
this.mCurrentTaskStep = currentTaskStep;
}
@@ -157,18 +154,12 @@
return mIsRefresh;
}
- /**
- * Updates the {@link Template} this {@link TemplateWrapper} instance wraps. For internal,
- * host-side use only.
- */
+ /** Updates the {@link Template} this {@link TemplateWrapper} instance wraps. */
public void setTemplate(@NonNull Template template) {
this.mTemplate = template;
}
- /**
- * Updates the ID associated with the wrapped {@link Template}. For internal, host-side use
- * only.
- */
+ /** Updates the ID associated with the wrapped {@link Template}. */
public void setId(@NonNull String id) {
this.mId = id;
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Toggle.java b/car/app/app/src/main/java/androidx/car/app/model/Toggle.java
index 2da80a6..42a82a1 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Toggle.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Toggle.java
@@ -46,7 +46,7 @@
/**
* Returns the {@link OnCheckedChangeDelegate} that is called when the checked state of
- * the {@link Toggle} is changed.
+ * the {@link Toggle} is changed or {@code null} if not set.
*/
@Nullable
public OnCheckedChangeDelegate getOnCheckedChangeDelegate() {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index 2a4d9f2..22be866 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -106,7 +106,7 @@
* @throws IllegalArgumentException if the actions has more actions than allowed, if it has
* more actions with custom titles than allowed, if the
* actions do not contain all required types, or if the
- * actions contain any disallowed types.
+ * actions contain any disallowed types
*/
public void validateOrThrow(@NonNull List<Action> actions) {
int maxAllowedActions = mMaxActions;
@@ -226,7 +226,7 @@
* Returns a new builder that contains the same data as the given {@link ActionsConstraints}
* instance.
*
- * @throws NullPointerException if {@code latLng} is {@code null}.
+ * @throws NullPointerException if {@code latLng} is {@code null}
*/
public Builder(@NonNull ActionsConstraints constraints) {
requireNonNull(constraints);
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java
index 258d8f3..ec6a2a9 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarColorConstraints.java
@@ -70,7 +70,7 @@
/**
* Returns {@code true} if the {@link CarColor} meets the constraints' requirement.
*
- * @throws IllegalArgumentException if the color type is not allowed.
+ * @throws IllegalArgumentException if the color type is not allowed
*/
public void validateOrThrow(@NonNull CarColor carColor) {
@CarColorType int type = carColor.getType();
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java
index dc504f8..6a7e754 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/CarIconConstraints.java
@@ -56,9 +56,8 @@
* Returns {@code true} if the {@link CarIcon} meets the constraints' requirement.
*
* @throws IllegalStateException if the custom icon does not have a backing
- * {@link IconCompat}
- * instance.
- * @throws IllegalArgumentException if the custom icon type is not allowed.
+ * {@link IconCompat} instance
+ * @throws IllegalArgumentException if the custom icon type is not allowed
*/
public void validateOrThrow(@Nullable CarIcon carIcon) {
if (carIcon == null || carIcon.getType() != CarIcon.TYPE_CUSTOM) {
@@ -76,7 +75,7 @@
/**
* Checks whether the given icon is supported.
*
- * @throws IllegalArgumentException if the given icon type is unsupported.
+ * @throws IllegalArgumentException if the given icon type is unsupported
*/
@NonNull
public IconCompat checkSupportedIcon(@NonNull IconCompat iconCompat) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
index 9c9fee9..e767e5b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowConstraints.java
@@ -112,7 +112,7 @@
/**
* Validates that the given row satisfies this {@link RowConstraints} instance.
*
- * @throws IllegalArgumentException if the constraints are not met.
+ * @throws IllegalArgumentException if the constraints are not met
*/
public void validateOrThrow(@NonNull Row row) {
if (!mIsOnClickListenerAllowed && row.getOnClickDelegate() != null) {
@@ -215,7 +215,7 @@
* Returns a new builder that contains the same data as the given {@link RowConstraints}
* instance.
*
- * @throws NullPointerException if {@code latLng} is {@code null}.
+ * @throws NullPointerException if {@code latLng} is {@code null}
*/
public Builder(@NonNull RowConstraints constraints) {
requireNonNull(constraints);
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
index ae2d5c3..9f3d120 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/RowListConstraints.java
@@ -107,7 +107,7 @@
* Validates that the {@link ItemList} satisfies this {@link RowListConstraints} instance.
*
* @throws IllegalArgumentException if the constraints are not met, or if the list contains
- * non-Row instances.
+ * non-row instances
*/
public void validateOrThrow(@NonNull ItemList itemList) {
if (itemList.getOnSelectedDelegate() != null && !mAllowSelectableLists) {
@@ -122,7 +122,7 @@
* {@link RowListConstraints} instance.
*
* @throws IllegalArgumentException if the constraints are not met or if the lists contain
- * any non-Row instances.
+ * any non-row instances
*/
public void validateOrThrow(@NonNull List<SectionedItemList> sections) {
List<Item> combinedLists = new ArrayList<>();
@@ -142,7 +142,7 @@
/**
* Validates that the {@link Pane} satisfies this {@link RowListConstraints} instance.
*
- * @throws IllegalArgumentException if the constraints are not met.
+ * @throws IllegalArgumentException if the constraints are not met
*/
public void validateOrThrow(@NonNull Pane pane) {
List<Action> actions = pane.getActions();
@@ -214,7 +214,7 @@
/**
* Return a a new builder for the given {@link RowListConstraints} instance.
*
- * @throws NullPointerException if {@code latLng} is {@code null}.
+ * @throws NullPointerException if {@code latLng} is {@code null}
*/
public Builder(@NonNull RowListConstraints constraints) {
requireNonNull(constraints);
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java b/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java
index c5c7624..763335c 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/NavigationManager.java
@@ -94,7 +94,6 @@
* displays the associated icon may be shown.
*
* @param trip destination, steps, and trip estimates to be sent to the host
- *
* @throws HostException if the call is invoked by an app that is not declared as
* a navigation app in the manifest
* @throws IllegalStateException if the call occurs when navigation is not started (see
@@ -134,7 +133,6 @@
* {@link #setNavigationManagerCallback(Executor, NavigationManagerCallback)}.
*
* @param callback the {@link NavigationManagerCallback} to use
- *
* @throws IllegalStateException if the current thread is not the main thread
*/
@SuppressLint("ExecutorRegistration")
@@ -150,7 +148,6 @@
*
* @param executor the executor which will be used for invoking the callback
* @param callback the {@link NavigationManagerCallback} to use
- *
* @throws IllegalStateException if the current thread is not the main thread
*/
@MainThread
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java
index 6ea3566..88cea7c 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Maneuver.java
@@ -34,7 +34,7 @@
import java.util.Objects;
/** Information about a maneuver that the driver will be required to perform. */
-// TODO(rasekh): Update when host(s) updates or a scheme for auto sync is established.
+// TODO(b/154671667): Update when host(s) updates or a scheme for auto sync is established.
public final class Maneuver {
/**
* Possible maneuver types.
@@ -72,8 +72,6 @@
TYPE_MERGE_LEFT,
TYPE_MERGE_RIGHT,
TYPE_MERGE_SIDE_UNSPECIFIED,
- TYPE_ROUNDABOUT_ENTER,
- TYPE_ROUNDABOUT_EXIT,
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW,
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE,
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW,
@@ -293,30 +291,6 @@
public static final int TYPE_MERGE_SIDE_UNSPECIFIED = 29;
/**
- * Roundabout entrance on which the current road ends.
- *
- * <p>For example, this is used to indicate "Enter the roundabout".
- *
- * @deprecated Use {@link #TYPE_ROUNDABOUT_ENTER_CW} or {@link #TYPE_ROUNDABOUT_ENTER_CCW}
- * instead.
- */
- @Deprecated
- @Type
- public static final int TYPE_ROUNDABOUT_ENTER = 30;
-
- /**
- * Used when leaving a roundabout when the step starts in it.
- *
- * <p>For example, this is used to indicate "Exit the roundabout".
- *
- * @deprecated Use {@link #TYPE_ROUNDABOUT_EXIT_CW} or {@link #TYPE_ROUNDABOUT_EXIT_CCW}
- * instead.
- */
- @Deprecated
- @Type
- public static final int TYPE_ROUNDABOUT_EXIT = 31;
-
- /**
* Enter a clockwise roundabout and take the Nth exit.
*
* <p>The exit number must be passed when created the maneuver.
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
index 729db82..0f431dd 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
@@ -33,8 +33,6 @@
import java.util.Objects;
-// TODO(rampara): Update code reference to CarAppExtender is javadoc to link
-
/**
* A template for showing navigation information.
*
@@ -51,7 +49,7 @@
* <p>The template itself does not expose a drawing surface. In order to draw on the canvas, use
* {@link androidx.car.app.AppManager#setSurfaceCallback(SurfaceCallback)}.
*
- * <p>See {@code androidx.car.app.notification.CarAppExtender} for how to show
+ * <p>See {@link androidx.car.app.notification.CarAppExtender} for how to show
* alerts with notifications. Frequent alert notifications distract the driver and are discouraged.
*
* <h4>Template Restrictions</h4>
@@ -89,6 +87,16 @@
private final ActionStrip mActionStrip;
/**
+ * Returns the {@link ActionStrip} for this template, or {@code null} if one isn't set.
+ *
+ * @see Builder#setActionStrip(ActionStrip)
+ */
+ @Nullable
+ public ActionStrip getActionStrip() {
+ return requireNonNull(mActionStrip);
+ }
+
+ /**
* Returns the navigation information displayed on the template, or {@code null} if there is no
* navigation information on top of the map.
*/
@@ -115,14 +123,6 @@
return mDestinationTravelEstimate;
}
- /**
- * Returns the {@link ActionStrip} with a list of the template-scoped actions for this template.
- */
- @Nullable
- public ActionStrip getActionStrip() {
- return mActionStrip;
- }
-
@NonNull
@Override
public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java
index 13879e1..fd7d6c1 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/PlaceListNavigationTemplate.java
@@ -81,30 +81,57 @@
@Nullable
private final ActionStrip mActionStrip;
+ /**
+ * Returns the title of the template, or {@code null} if one is not set.
+ *
+ * @see Builder#setTitle(CharSequence)
+ */
@Nullable
public CarText getTitle() {
return mTitle;
}
- public boolean isLoading() {
- return mIsLoading;
- }
-
- @Nullable
- public ItemList getItemList() {
- return mItemList;
- }
-
+ /**
+ * Returns the {@link Action} that is set to be displayed in the header of the template, or
+ * {@code null} if one is not set.
+ *
+ * @see Builder#setHeaderAction(Action)
+ */
@Nullable
public Action getHeaderAction() {
return mHeaderAction;
}
+ /**
+ * Returns the {@link ActionStrip} for this template, or {@code null} if one isn't set.
+ *
+ * @see Builder#setActionStrip(ActionStrip)
+ */
@Nullable
public ActionStrip getActionStrip() {
return mActionStrip;
}
+ /**
+ * Returns whether the template is loading.
+ *
+ * @see Builder#setLoading(boolean)
+ */
+ public boolean isLoading() {
+ return mIsLoading;
+ }
+
+ /**
+ * Returns the list of items to display alongside the map or {@code null} if the list is not
+ * set.
+ *
+ * @see Builder#setItemList(ItemList)
+ */
+ @Nullable
+ public ItemList getItemList() {
+ return mItemList;
+ }
+
@NonNull
@Override
public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java
index b0a80be..b97dccd 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutePreviewNavigationTemplate.java
@@ -92,13 +92,41 @@
private final ActionStrip mActionStrip;
/**
- * Returns the {@link CarText} that should be used as the title in the template.
+ * Returns the title of the template, or {@code null} if one is not set.
+ *
+ * @see Builder#setTitle(CharSequence)
*/
@Nullable
public CarText getTitle() {
return mTitle;
}
+ /**
+ * Returns the {@link Action} that is set to be displayed in the header of the template, or
+ * {@code null} if one is not set.
+ *
+ * @see Builder#setHeaderAction(Action)
+ */
+ @Nullable
+ public Action getHeaderAction() {
+ return mHeaderAction;
+ }
+
+ /**
+ * Returns the {@link ActionStrip} for this template, or {@code null} if one isn't set.
+ *
+ * @see Builder#setActionStrip(ActionStrip)
+ */
+ @Nullable
+ public ActionStrip getActionStrip() {
+ return mActionStrip;
+ }
+
+ /**
+ * Returns whether the template is loading.
+ *
+ * @see Builder#setLoading(boolean)
+ */
public boolean isLoading() {
return mIsLoading;
}
@@ -113,16 +141,6 @@
return mItemList;
}
- @Nullable
- public Action getHeaderAction() {
- return mHeaderAction;
- }
-
- @Nullable
- public ActionStrip getActionStrip() {
- return mActionStrip;
- }
-
@NonNull
@Override
public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
index 47dc3ae..ff929333 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/RoutingInfo.java
@@ -48,6 +48,15 @@
@Keep
private final boolean mIsLoading;
+ /**
+ * Returns whether the routing info is in a loading state.
+ *
+ * @see Builder#setLoading(boolean)
+ */
+ public boolean isLoading() {
+ return mIsLoading;
+ }
+
@Nullable
public Step getCurrentStep() {
return mCurrentStep;
@@ -68,10 +77,6 @@
return mJunctionImage;
}
- public boolean isLoading() {
- return mIsLoading;
- }
-
@NonNull
@Override
public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
index 5c5f324..d200bb7 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
@@ -61,7 +61,6 @@
return mRemainingDistance;
}
- // TODO(rampara): Returned time values must be in milliseconds
@SuppressWarnings("MethodNameUnits")
public long getRemainingTimeSeconds() {
return mRemainingTimeSeconds >= 0 ? mRemainingTimeSeconds : REMAINING_TIME_UNKNOWN;
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
index 9c6f01e..369b6f7 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Trip.java
@@ -50,6 +50,15 @@
@Keep
private final boolean mIsLoading;
+ /**
+ * Returns whether the trip is in a loading state.
+ *
+ * @see Builder#setLoading(boolean)
+ */
+ public boolean isLoading() {
+ return mIsLoading;
+ }
+
@NonNull
public List<Destination> getDestinations() {
return CollectionUtils.emptyIfNull(mDestinations);
@@ -75,10 +84,6 @@
return mCurrentRoad;
}
- public boolean isLoading() {
- return mIsLoading;
- }
-
@Override
@NonNull
public String toString() {
diff --git a/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java b/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java
index 41aa2a73..63151da 100644
--- a/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java
+++ b/car/app/app/src/main/java/androidx/car/app/notification/CarAppExtender.java
@@ -523,7 +523,6 @@
* widget.
* @throws NullPointerException if {@code title} or {@code intent} are {@code null}.
*/
- // TODO(rampara): Update to remove use of deprecated Action API
@SuppressWarnings("deprecation")
@NonNull
public Builder addAction(
diff --git a/car/app/app/src/main/java/androidx/car/app/serialization/Bundleable.java b/car/app/app/src/main/java/androidx/car/app/serialization/Bundleable.java
index c25dec2..01ab47a 100644
--- a/car/app/app/src/main/java/androidx/car/app/serialization/Bundleable.java
+++ b/car/app/app/src/main/java/androidx/car/app/serialization/Bundleable.java
@@ -28,7 +28,6 @@
* A class that serializes and stores an object for sending over IPC.
*/
@SuppressWarnings("BanParcelableUsage")
-// TODO(rampara): Investigate transition to VersionedParcelable.
public final class Bundleable implements Parcelable {
private final Bundle mBundle;
diff --git a/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java b/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java
index 969153e..54646dd 100644
--- a/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java
+++ b/car/app/app/src/main/java/androidx/car/app/utils/RemoteUtils.java
@@ -67,7 +67,6 @@
*/
@SuppressLint("LambdaLast")
@Nullable
- // TODO(rampara): Change method signature to change parameter order.
public static <ReturnT> ReturnT call(@NonNull RemoteCall<ReturnT> remoteCall,
@NonNull String callName) {
try {
@@ -150,7 +149,6 @@
* <p>If the app throws an exception, will call {@link IOnDoneCallback#onFailure} with a {@link
* FailureResponse} including information from the caught exception.
*/
- // TODO(rampara): Change method signature to change parameter order.
@SuppressLint("LambdaLast")
public static void dispatchHostCall(
@NonNull HostCall hostCall, @NonNull IOnDoneCallback callback,
diff --git a/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java b/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java
index abd1894..571a572 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarAppServiceTest.java
@@ -99,6 +99,8 @@
@NonNull
public Session onCreateSession() {
Session testSession = createTestSession();
+ mCarContext = TestCarContext.createCarContext(
+ ApplicationProvider.getApplicationContext());
CarAppServiceController.of(mCarContext, testSession, mCarAppService);
return testSession;
}
@@ -351,6 +353,8 @@
HandshakeInfo handshakeInfo = new HandshakeInfo(hostPackageName, hostApiLevel);
carApp.onHandshakeCompleted(Bundleable.create(handshakeInfo), mock(IOnDoneCallback.class));
carApp.onAppCreate(mMockCarHost, null, new Configuration(), mock(IOnDoneCallback.class));
+
+ currentSession = mCarAppService.getCurrentSession();
assertThat(currentSession.getCarContext().getCarService(
ScreenManager.class).getScreenStack()).hasSize(1);
}
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index 2dccb46..c7b04d2 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -75,14 +75,14 @@
}
public interface Animation<T, V extends androidx.compose.animation.core.AnimationVector> {
- method public long getDurationMillis();
+ method public long getDurationNanos();
method public T! getTargetValue();
method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
- method public T! getValue(long playTime);
- method public V getVelocityVector(long playTime);
- method public default boolean isFinished(long playTime);
+ method public T! getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
+ method public default boolean isFinishedFromNanos(long playTimeNanos);
method public boolean isInfinite();
- property public abstract long durationMillis;
+ property public abstract long durationNanos;
property public abstract boolean isInfinite;
property public abstract T! targetValue;
property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
@@ -113,7 +113,7 @@
public final class AnimationKt {
method public static androidx.compose.animation.core.DecayAnimation<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> DecayAnimation(androidx.compose.animation.core.FloatDecayAnimationSpec animationSpec, float initialValue, optional float initialVelocity);
method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TargetBasedAnimation<T,V> TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, T? initialVelocity);
- method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocity(androidx.compose.animation.core.Animation<T,V>, long playTime);
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocityFromNanos(androidx.compose.animation.core.Animation<T,V>, long playTimeNanos);
}
public final class AnimationResult<T, V extends androidx.compose.animation.core.AnimationVector> {
@@ -289,15 +289,15 @@
ctor public DecayAnimation(androidx.compose.animation.core.VectorizedDecayAnimationSpec<V> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? initialVelocity);
- method public long getDurationMillis();
+ method public long getDurationNanos();
method public T! getInitialValue();
method public V getInitialVelocityVector();
method public T! getTargetValue();
method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
- method public T! getValue(long playTime);
- method public V getVelocityVector(long playTime);
+ method public T! getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
method public boolean isInfinite();
- property public long durationMillis;
+ property public long durationNanos;
property public final T! initialValue;
property public final V initialVelocityVector;
property public boolean isInfinite;
@@ -341,19 +341,19 @@
}
public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
- method public long getDurationMillis(float start, float end, float startVelocity);
- method public default float getEndVelocity(float start, float end, float startVelocity);
- method public float getValue(long playTime, float start, float end, float startVelocity);
- method public float getVelocity(long playTime, float start, float end, float startVelocity);
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+ method public default float getEndVelocity(float initialValue, float targetValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
method public default <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFloatAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<java.lang.Float,V> converter);
}
public interface FloatDecayAnimationSpec {
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(float start, float startVelocity);
- method public float getTarget(float start, float startVelocity);
- method public float getValue(long playTime, float start, float startVelocity);
- method public float getVelocity(long playTime, float start, float startVelocity);
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
property public abstract float absVelocityThreshold;
}
@@ -364,10 +364,10 @@
ctor public FloatExponentialDecaySpec(float frictionMultiplier, float absVelocityThreshold);
ctor public FloatExponentialDecaySpec();
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(float start, float startVelocity);
- method public float getTarget(float start, float startVelocity);
- method public float getValue(long playTime, float start, float startVelocity);
- method public float getVelocity(long playTime, float start, float startVelocity);
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
property public float absVelocityThreshold;
}
@@ -375,10 +375,10 @@
ctor public FloatSpringSpec(float dampingRatio, float stiffness, float visibilityThreshold);
ctor public FloatSpringSpec();
method public float getDampingRatio();
- method public long getDurationMillis(float start, float end, float startVelocity);
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
method public float getStiffness();
- method public float getValue(long playTime, float start, float end, float startVelocity);
- method public float getVelocity(long playTime, float start, float end, float startVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
property public final float dampingRatio;
property public final float stiffness;
}
@@ -388,9 +388,9 @@
ctor public FloatTweenSpec();
method public int getDelay();
method public int getDuration();
- method public long getDurationMillis(float start, float end, float startVelocity);
- method public float getValue(long playTime, float start, float end, float startVelocity);
- method public float getVelocity(long playTime, float start, float end, float startVelocity);
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
property public final int delay;
property public final int duration;
}
@@ -558,14 +558,14 @@
public final class TargetBasedAnimation<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.Animation<T,V> {
ctor public TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, V? initialVelocityVector);
- method public long getDurationMillis();
+ method public long getDurationNanos();
method public T! getInitialValue();
method public T! getTargetValue();
method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
- method public T! getValue(long playTime);
- method public V getVelocityVector(long playTime);
+ method public T! getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
method public boolean isInfinite();
- property public long durationMillis;
+ property public long durationNanos;
property public final T! initialValue;
property public boolean isInfinite;
property public T! targetValue;
@@ -652,10 +652,10 @@
}
public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
- method public long getDurationMillis(V start, V end, V startVelocity);
- method public default V getEndVelocity(V start, V end, V startVelocity);
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public default V getEndVelocity(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
method public boolean isInfinite();
property public abstract boolean isInfinite;
}
@@ -665,17 +665,17 @@
public interface VectorizedDecayAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(V initialValue, V initialVelocity);
- method public V getTarget(V initialValue, V initialVelocity);
- method public V getValue(long playTime, V initialValue, V initialVelocity);
- method public V getVelocity(long playTime, V initialValue, V initialVelocity);
+ method public long getDurationNanos(V initialValue, V initialVelocity);
+ method public V getTargetValue(V initialValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
property public abstract float absVelocityThreshold;
}
public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
method public int getDelayMillis();
method public int getDurationMillis();
- method public default long getDurationMillis(V start, V end, V startVelocity);
+ method public default long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
property public abstract int delayMillis;
property public abstract int durationMillis;
}
@@ -687,9 +687,9 @@
public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
- method public long getDurationMillis(V start, V end, V startVelocity);
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
}
public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
@@ -702,17 +702,17 @@
ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends androidx.compose.animation.core.Easing>> keyframes, int durationMillis, int delayMillis);
method public int getDelayMillis();
method public int getDurationMillis();
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
property public int delayMillis;
property public int durationMillis;
}
public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
- method public long getDurationMillis(V start, V end, V startVelocity);
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
}
public final class VectorizedSnapSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
@@ -720,8 +720,8 @@
ctor public VectorizedSnapSpec();
method public int getDelayMillis();
method public int getDurationMillis();
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
property public int delayMillis;
property public int durationMillis;
}
@@ -740,8 +740,8 @@
method public int getDelayMillis();
method public int getDurationMillis();
method public androidx.compose.animation.core.Easing getEasing();
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
property public int delayMillis;
property public int durationMillis;
property public final androidx.compose.animation.core.Easing easing;
diff --git a/compose/animation/animation-core/api/public_plus_experimental_current.txt b/compose/animation/animation-core/api/public_plus_experimental_current.txt
index 2dccb46..c7b04d2 100644
--- a/compose/animation/animation-core/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation-core/api/public_plus_experimental_current.txt
@@ -75,14 +75,14 @@
}
public interface Animation<T, V extends androidx.compose.animation.core.AnimationVector> {
- method public long getDurationMillis();
+ method public long getDurationNanos();
method public T! getTargetValue();
method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
- method public T! getValue(long playTime);
- method public V getVelocityVector(long playTime);
- method public default boolean isFinished(long playTime);
+ method public T! getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
+ method public default boolean isFinishedFromNanos(long playTimeNanos);
method public boolean isInfinite();
- property public abstract long durationMillis;
+ property public abstract long durationNanos;
property public abstract boolean isInfinite;
property public abstract T! targetValue;
property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
@@ -113,7 +113,7 @@
public final class AnimationKt {
method public static androidx.compose.animation.core.DecayAnimation<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> DecayAnimation(androidx.compose.animation.core.FloatDecayAnimationSpec animationSpec, float initialValue, optional float initialVelocity);
method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TargetBasedAnimation<T,V> TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, T? initialVelocity);
- method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocity(androidx.compose.animation.core.Animation<T,V>, long playTime);
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocityFromNanos(androidx.compose.animation.core.Animation<T,V>, long playTimeNanos);
}
public final class AnimationResult<T, V extends androidx.compose.animation.core.AnimationVector> {
@@ -289,15 +289,15 @@
ctor public DecayAnimation(androidx.compose.animation.core.VectorizedDecayAnimationSpec<V> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? initialVelocity);
- method public long getDurationMillis();
+ method public long getDurationNanos();
method public T! getInitialValue();
method public V getInitialVelocityVector();
method public T! getTargetValue();
method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
- method public T! getValue(long playTime);
- method public V getVelocityVector(long playTime);
+ method public T! getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
method public boolean isInfinite();
- property public long durationMillis;
+ property public long durationNanos;
property public final T! initialValue;
property public final V initialVelocityVector;
property public boolean isInfinite;
@@ -341,19 +341,19 @@
}
public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
- method public long getDurationMillis(float start, float end, float startVelocity);
- method public default float getEndVelocity(float start, float end, float startVelocity);
- method public float getValue(long playTime, float start, float end, float startVelocity);
- method public float getVelocity(long playTime, float start, float end, float startVelocity);
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+ method public default float getEndVelocity(float initialValue, float targetValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
method public default <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFloatAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<java.lang.Float,V> converter);
}
public interface FloatDecayAnimationSpec {
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(float start, float startVelocity);
- method public float getTarget(float start, float startVelocity);
- method public float getValue(long playTime, float start, float startVelocity);
- method public float getVelocity(long playTime, float start, float startVelocity);
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
property public abstract float absVelocityThreshold;
}
@@ -364,10 +364,10 @@
ctor public FloatExponentialDecaySpec(float frictionMultiplier, float absVelocityThreshold);
ctor public FloatExponentialDecaySpec();
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(float start, float startVelocity);
- method public float getTarget(float start, float startVelocity);
- method public float getValue(long playTime, float start, float startVelocity);
- method public float getVelocity(long playTime, float start, float startVelocity);
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
property public float absVelocityThreshold;
}
@@ -375,10 +375,10 @@
ctor public FloatSpringSpec(float dampingRatio, float stiffness, float visibilityThreshold);
ctor public FloatSpringSpec();
method public float getDampingRatio();
- method public long getDurationMillis(float start, float end, float startVelocity);
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
method public float getStiffness();
- method public float getValue(long playTime, float start, float end, float startVelocity);
- method public float getVelocity(long playTime, float start, float end, float startVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
property public final float dampingRatio;
property public final float stiffness;
}
@@ -388,9 +388,9 @@
ctor public FloatTweenSpec();
method public int getDelay();
method public int getDuration();
- method public long getDurationMillis(float start, float end, float startVelocity);
- method public float getValue(long playTime, float start, float end, float startVelocity);
- method public float getVelocity(long playTime, float start, float end, float startVelocity);
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
property public final int delay;
property public final int duration;
}
@@ -558,14 +558,14 @@
public final class TargetBasedAnimation<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.Animation<T,V> {
ctor public TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, V? initialVelocityVector);
- method public long getDurationMillis();
+ method public long getDurationNanos();
method public T! getInitialValue();
method public T! getTargetValue();
method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
- method public T! getValue(long playTime);
- method public V getVelocityVector(long playTime);
+ method public T! getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
method public boolean isInfinite();
- property public long durationMillis;
+ property public long durationNanos;
property public final T! initialValue;
property public boolean isInfinite;
property public T! targetValue;
@@ -652,10 +652,10 @@
}
public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
- method public long getDurationMillis(V start, V end, V startVelocity);
- method public default V getEndVelocity(V start, V end, V startVelocity);
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public default V getEndVelocity(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
method public boolean isInfinite();
property public abstract boolean isInfinite;
}
@@ -665,17 +665,17 @@
public interface VectorizedDecayAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(V initialValue, V initialVelocity);
- method public V getTarget(V initialValue, V initialVelocity);
- method public V getValue(long playTime, V initialValue, V initialVelocity);
- method public V getVelocity(long playTime, V initialValue, V initialVelocity);
+ method public long getDurationNanos(V initialValue, V initialVelocity);
+ method public V getTargetValue(V initialValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
property public abstract float absVelocityThreshold;
}
public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
method public int getDelayMillis();
method public int getDurationMillis();
- method public default long getDurationMillis(V start, V end, V startVelocity);
+ method public default long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
property public abstract int delayMillis;
property public abstract int durationMillis;
}
@@ -687,9 +687,9 @@
public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
- method public long getDurationMillis(V start, V end, V startVelocity);
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
}
public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
@@ -702,17 +702,17 @@
ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends androidx.compose.animation.core.Easing>> keyframes, int durationMillis, int delayMillis);
method public int getDelayMillis();
method public int getDurationMillis();
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
property public int delayMillis;
property public int durationMillis;
}
public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
- method public long getDurationMillis(V start, V end, V startVelocity);
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
}
public final class VectorizedSnapSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
@@ -720,8 +720,8 @@
ctor public VectorizedSnapSpec();
method public int getDelayMillis();
method public int getDurationMillis();
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
property public int delayMillis;
property public int durationMillis;
}
@@ -740,8 +740,8 @@
method public int getDelayMillis();
method public int getDurationMillis();
method public androidx.compose.animation.core.Easing getEasing();
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
property public int delayMillis;
property public int durationMillis;
property public final androidx.compose.animation.core.Easing easing;
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index 9cf625e..1333ce8 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -75,14 +75,14 @@
}
public interface Animation<T, V extends androidx.compose.animation.core.AnimationVector> {
- method public long getDurationMillis();
+ method public long getDurationNanos();
method public T! getTargetValue();
method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
- method public T! getValue(long playTime);
- method public V getVelocityVector(long playTime);
- method public default boolean isFinished(long playTime);
+ method public T! getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
+ method public default boolean isFinishedFromNanos(long playTimeNanos);
method public boolean isInfinite();
- property public abstract long durationMillis;
+ property public abstract long durationNanos;
property public abstract boolean isInfinite;
property public abstract T! targetValue;
property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
@@ -113,7 +113,7 @@
public final class AnimationKt {
method public static androidx.compose.animation.core.DecayAnimation<java.lang.Float,androidx.compose.animation.core.AnimationVector1D> DecayAnimation(androidx.compose.animation.core.FloatDecayAnimationSpec animationSpec, float initialValue, optional float initialVelocity);
method public static <T, V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.TargetBasedAnimation<T,V> TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, T? initialVelocity);
- method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocity(androidx.compose.animation.core.Animation<T,V>, long playTime);
+ method public static <T, V extends androidx.compose.animation.core.AnimationVector> T! getVelocityFromNanos(androidx.compose.animation.core.Animation<T,V>, long playTimeNanos);
}
public final class AnimationResult<T, V extends androidx.compose.animation.core.AnimationVector> {
@@ -289,15 +289,15 @@
ctor public DecayAnimation(androidx.compose.animation.core.VectorizedDecayAnimationSpec<V> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, V initialVelocityVector);
ctor public DecayAnimation(androidx.compose.animation.core.DecayAnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? initialVelocity);
- method public long getDurationMillis();
+ method public long getDurationNanos();
method public T! getInitialValue();
method public V getInitialVelocityVector();
method public T! getTargetValue();
method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
- method public T! getValue(long playTime);
- method public V getVelocityVector(long playTime);
+ method public T! getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
method public boolean isInfinite();
- property public long durationMillis;
+ property public long durationNanos;
property public final T! initialValue;
property public final V initialVelocityVector;
property public boolean isInfinite;
@@ -341,19 +341,19 @@
}
public interface FloatAnimationSpec extends androidx.compose.animation.core.AnimationSpec<java.lang.Float> {
- method public long getDurationMillis(float start, float end, float startVelocity);
- method public default float getEndVelocity(float start, float end, float startVelocity);
- method public float getValue(long playTime, float start, float end, float startVelocity);
- method public float getVelocity(long playTime, float start, float end, float startVelocity);
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+ method public default float getEndVelocity(float initialValue, float targetValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
method public default <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedFloatAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<java.lang.Float,V> converter);
}
public interface FloatDecayAnimationSpec {
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(float start, float startVelocity);
- method public float getTarget(float start, float startVelocity);
- method public float getValue(long playTime, float start, float startVelocity);
- method public float getVelocity(long playTime, float start, float startVelocity);
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
property public abstract float absVelocityThreshold;
}
@@ -364,10 +364,10 @@
ctor public FloatExponentialDecaySpec(float frictionMultiplier, float absVelocityThreshold);
ctor public FloatExponentialDecaySpec();
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(float start, float startVelocity);
- method public float getTarget(float start, float startVelocity);
- method public float getValue(long playTime, float start, float startVelocity);
- method public float getVelocity(long playTime, float start, float startVelocity);
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
property public float absVelocityThreshold;
}
@@ -375,10 +375,10 @@
ctor public FloatSpringSpec(float dampingRatio, float stiffness, float visibilityThreshold);
ctor public FloatSpringSpec();
method public float getDampingRatio();
- method public long getDurationMillis(float start, float end, float startVelocity);
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
method public float getStiffness();
- method public float getValue(long playTime, float start, float end, float startVelocity);
- method public float getVelocity(long playTime, float start, float end, float startVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
property public final float dampingRatio;
property public final float stiffness;
}
@@ -388,9 +388,9 @@
ctor public FloatTweenSpec();
method public int getDelay();
method public int getDuration();
- method public long getDurationMillis(float start, float end, float startVelocity);
- method public float getValue(long playTime, float start, float end, float startVelocity);
- method public float getVelocity(long playTime, float start, float end, float startVelocity);
+ method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float targetValue, float initialVelocity);
property public final int delay;
property public final int duration;
}
@@ -558,14 +558,14 @@
public final class TargetBasedAnimation<T, V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.Animation<T,V> {
ctor public TargetBasedAnimation(androidx.compose.animation.core.AnimationSpec<T> animationSpec, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T? initialValue, T? targetValue, V? initialVelocityVector);
- method public long getDurationMillis();
+ method public long getDurationNanos();
method public T! getInitialValue();
method public T! getTargetValue();
method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
- method public T! getValue(long playTime);
- method public V getVelocityVector(long playTime);
+ method public T! getValueFromNanos(long playTimeNanos);
+ method public V getVelocityVectorFromNanos(long playTimeNanos);
method public boolean isInfinite();
- property public long durationMillis;
+ property public long durationNanos;
property public final T! initialValue;
property public boolean isInfinite;
property public T! targetValue;
@@ -660,10 +660,10 @@
}
public interface VectorizedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
- method public long getDurationMillis(V start, V end, V startVelocity);
- method public default V getEndVelocity(V start, V end, V startVelocity);
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public default V getEndVelocity(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
method public boolean isInfinite();
property public abstract boolean isInfinite;
}
@@ -673,17 +673,17 @@
public interface VectorizedDecayAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> {
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(V initialValue, V initialVelocity);
- method public V getTarget(V initialValue, V initialVelocity);
- method public V getValue(long playTime, V initialValue, V initialVelocity);
- method public V getVelocity(long playTime, V initialValue, V initialVelocity);
+ method public long getDurationNanos(V initialValue, V initialVelocity);
+ method public V getTargetValue(V initialValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V initialVelocity);
property public abstract float absVelocityThreshold;
}
public interface VectorizedDurationBasedAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
method public int getDelayMillis();
method public int getDurationMillis();
- method public default long getDurationMillis(V start, V end, V startVelocity);
+ method public default long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
property public abstract int delayMillis;
property public abstract int durationMillis;
}
@@ -695,9 +695,9 @@
public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
ctor public VectorizedFloatAnimationSpec(androidx.compose.animation.core.FloatAnimationSpec anim);
- method public long getDurationMillis(V start, V end, V startVelocity);
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
}
public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
@@ -710,17 +710,17 @@
ctor public VectorizedKeyframesSpec(java.util.Map<java.lang.Integer,? extends kotlin.Pair<? extends V,? extends androidx.compose.animation.core.Easing>> keyframes, int durationMillis, int delayMillis);
method public int getDelayMillis();
method public int getDurationMillis();
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
property public int delayMillis;
property public int durationMillis;
}
public final class VectorizedRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
ctor public VectorizedRepeatableSpec(int iterations, androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
- method public long getDurationMillis(V start, V end, V startVelocity);
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public long getDurationNanos(V initialValue, V targetValue, V initialVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
}
public final class VectorizedSnapSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
@@ -728,8 +728,8 @@
ctor public VectorizedSnapSpec();
method public int getDelayMillis();
method public int getDurationMillis();
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
property public int delayMillis;
property public int durationMillis;
}
@@ -748,8 +748,8 @@
method public int getDelayMillis();
method public int getDurationMillis();
method public androidx.compose.animation.core.Easing getEasing();
- method public V getValue(long playTime, V start, V end, V startVelocity);
- method public V getVelocity(long playTime, V start, V end, V startVelocity);
+ method public V getValueFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
+ method public V getVelocityFromNanos(long playTimeNanos, V initialValue, V targetValue, V initialVelocity);
property public int delayMillis;
property public int durationMillis;
property public final androidx.compose.animation.core.Easing easing;
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
index 0ce2950..766b3d2 100644
--- a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
@@ -87,13 +87,15 @@
val startTime = withFrameNanos { it }
var playTime = 0L
while (playTime < 2100L) {
- playTime = (withFrameNanos { it } - startTime) / 1_000_000L
- var iterationTime = playTime % 2000
- if (iterationTime > 1000L) {
- iterationTime = 2000L - iterationTime
+ playTime = withFrameNanos { it } - startTime
+ var iterationTime = playTime % (2000 * MillisToNanos)
+ if (iterationTime > 1000 * MillisToNanos) {
+ iterationTime = 2000L * MillisToNanos - iterationTime
}
- val expectedFloat = keyframesAnim.getValue(iterationTime)
- val expectedColor = colorAnim.getValue(playTime % 1000)
+ val expectedFloat = keyframesAnim.getValueFromNanos(iterationTime)
+ val expectedColor = colorAnim.getValueFromNanos(
+ playTime % (1000 * MillisToNanos)
+ )
assertEquals(expectedFloat, animFloat.value, 0.01f)
assertEquals(expectedColor, animColor.value)
}
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
index fe66c47..b8acd42 100644
--- a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
@@ -350,28 +350,31 @@
val floatValue by animateFloatAsState(if (enabled) 100f else 0f)
- val durationForFloat = specForFloat.getDurationMillis(0f, 100f, 0f)
- val durationForOffset = specForOffset.getDurationMillis(0f, 100f, 0f)
- val durationForBounds = specForBounds.getDurationMillis(0f, 100f, 0f)
+ val durationForFloat = specForFloat.getDurationNanos(0f, 100f, 0f)
+ val durationForOffset = specForOffset.getDurationNanos(0f, 100f, 0f)
+ val durationForBounds = specForBounds.getDurationNanos(0f, 100f, 0f)
if (enabled) {
LaunchedEffect(Unit) {
val startTime = withFrameNanos { it }
var frameTime = startTime
do {
- val playTime = (frameTime - startTime) / 1_000_000L
- val expectFloat = specForFloat.getValue(playTime, 0f, 100f, 0f)
+ val playTime = frameTime - startTime
+ val expectFloat =
+ specForFloat.getValueFromNanos(playTime, 0f, 100f, 0f)
assertEquals("play time: $playTime", expectFloat, floatValue)
if (playTime < durationForOffset) {
- val expectOffset = specForOffset.getValue(playTime, 0f, 100f, 0f)
+ val expectOffset =
+ specForOffset.getValueFromNanos(playTime, 0f, 100f, 0f)
assertEquals(Offset(expectOffset, expectOffset), offsetValue)
} else {
assertEquals(Offset(100f, 100f), offsetValue)
}
if (playTime < durationForBounds) {
- val expectBounds = specForBounds.getValue(playTime, 0f, 100f, 0f)
+ val expectBounds =
+ specForBounds.getValueFromNanos(playTime, 0f, 100f, 0f)
assertEquals(
Bounds(
expectBounds.dp,
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
index 47bccf1..157183c9 100644
--- a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
@@ -152,15 +152,15 @@
if (transition.isRunning) {
if (transition.targetState == AnimStates.To) {
assertEquals(
- floatAnim1.getValue(transition.playTimeNanos / 1_000_000L),
+ floatAnim1.getValueFromNanos(transition.playTimeNanos),
animFloat.value, 0.00001f
)
assertEquals(
- colorAnim1.getValue(transition.playTimeNanos / 1_000_000L),
+ colorAnim1.getValueFromNanos(transition.playTimeNanos),
animColor.value
)
assertEquals(
- keyframesAnim1.getValue(transition.playTimeNanos / 1_000_000L),
+ keyframesAnim1.getValueFromNanos(transition.playTimeNanos),
animFloatWithKeyframes.value, 0.00001f
)
@@ -168,15 +168,15 @@
assertEquals(AnimStates.From, transition.segment.initialState)
} else {
assertEquals(
- floatAnim2.getValue(transition.playTimeNanos / 1_000_000L),
+ floatAnim2.getValueFromNanos(transition.playTimeNanos),
animFloat.value, 0.00001f
)
assertEquals(
- colorAnim2.getValue(transition.playTimeNanos / 1_000_000L),
+ colorAnim2.getValueFromNanos(transition.playTimeNanos),
animColor.value
)
assertEquals(
- keyframesAnim2.getValue(transition.playTimeNanos / 1_000_000L),
+ keyframesAnim2.getValueFromNanos(transition.playTimeNanos),
animFloatWithKeyframes.value, 0.00001f
)
assertEquals(AnimStates.From, transition.segment.targetState)
@@ -225,9 +225,9 @@
assertEquals(0f, actual.value)
do {
- playTime = (withFrameNanos { it } - startTime) / 1_000_000L
- assertEquals(anim.getValue(playTime), actual.value)
- } while (playTime <= 200)
+ playTime = withFrameNanos { it } - startTime
+ assertEquals(anim.getValueFromNanos(playTime), actual.value)
+ } while (playTime <= 200 * MillisToNanos)
}
}
}
@@ -236,7 +236,7 @@
target.value = AnimStates.To
}
rule.waitForIdle()
- assertTrue(playTime > 200)
+ assertTrue(playTime > 200 * MillisToNanos)
}
@Test
@@ -260,8 +260,8 @@
if (it == AnimStates.From) 0f else 1000f
}
val anim = TargetBasedAnimation(tween(800), Float.VectorConverter, 0f, 1000f)
- playTime = (transition.playTimeNanos - startTime) / 1_000_000L
- assertEquals(anim.getValue(playTime), laterAdded.value)
+ playTime = transition.playTimeNanos - startTime
+ assertEquals(anim.getValueFromNanos(playTime), laterAdded.value)
}
}
@@ -269,7 +269,7 @@
target.value = AnimStates.To
}
rule.waitForIdle()
- assertTrue(playTime > 800)
+ assertTrue(playTime > 800 * MillisToNanos)
}
@Test
@@ -289,14 +289,14 @@
LaunchedEffect(transition) {
val startTime = withFrameNanos { it }
val anim = TargetBasedAnimation(tween(800), Float.VectorConverter, 0f, 1000f)
- while (!anim.isFinished(playTime)) {
- playTime = (withFrameNanos { it } - startTime) / 1_000_000L
- assertEquals(anim.getValue(playTime), floatAnim?.value)
+ while (!anim.isFinishedFromNanos(playTime)) {
+ playTime = withFrameNanos { it } - startTime
+ assertEquals(anim.getValueFromNanos(playTime), floatAnim?.value)
}
}
}
rule.waitForIdle()
- assertTrue(playTime >= 800)
+ assertTrue(playTime >= 800 * MillisToNanos)
assertEquals(1000f, floatAnim?.value)
}
@@ -327,9 +327,9 @@
val startTime = withFrameNanos { it }
val anim = TargetBasedAnimation(tween(800), Float.VectorConverter, 0f, 1000f)
- while (!anim.isFinished(playTime)) {
- playTime = (withFrameNanos { it } - startTime) / 1_000_000L
- assertEquals(anim.getValue(playTime), floatAnim.value)
+ while (!anim.isFinishedFromNanos(playTime)) {
+ playTime = withFrameNanos { it } - startTime
+ assertEquals(anim.getValueFromNanos(playTime), floatAnim.value)
}
}
}
@@ -337,6 +337,6 @@
rule.waitForIdle()
assertTrue(targetRecreated)
- assertTrue(playTime >= 800)
+ assertTrue(playTime >= 800 * MillisToNanos)
}
}
\ No newline at end of file
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
index 30775bb..75d11c7 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
@@ -105,6 +105,7 @@
internal var onEnd: ((AnimationEndReason, T) -> Unit)? = null
private lateinit var anim: Animation<T, V>
private var startTime: Long = Unset
+
// last frame time only gets updated during the animation pulse. It will be reset at the
// end of the animation.
private var lastFrameTime: Long = Unset
@@ -188,21 +189,21 @@
}
lastFrameTime = timeMillis
- velocityVector = anim.getVelocityVector(playtime)
- value = anim.getValue(playtime)
+ velocityVector = anim.getVelocityVectorFromNanos(playtime * MillisToNanos)
+ value = anim.getValueFromNanos(playtime * MillisToNanos)
checkFinished(playtime)
}
protected open fun checkFinished(playtime: Long) {
- val animationFinished = anim.isFinished(playtime)
+ val animationFinished = anim.isFinishedFromNanos(playtime * MillisToNanos)
if (animationFinished) endAnimation()
}
internal fun startAnimation(anim: Animation<T, V>) {
this.anim = anim
// Quick check before officially starting
- if (anim.isFinished(0)) {
+ if (anim.isFinishedFromNanos(0)) {
// If the animation value & velocity is already meeting the finished condition before
// the animation even starts, end it now.
endAnimation()
@@ -275,6 +276,7 @@
*/
var min: Float = Float.NEGATIVE_INFINITY
private set
+
/**
* Upper bound of the animation value. When animations reach this upper bound, it will
* automatically stop with [AnimationEndReason] being [AnimationEndReason.BoundReached].
@@ -367,7 +369,7 @@
}
// start from current value with the given velocity
- targetValue = decay.getTarget(value, startVelocity)
+ targetValue = decay.getTargetValue(value, startVelocity)
startAnimation(DecayAnimation(decay, value, startVelocity))
}
@@ -402,7 +404,7 @@
}
// start from current value with the given velocity
- targetValue = decay.getTarget(value, startVelocity)
+ targetValue = decay.getTargetValue(value, startVelocity)
val targetAnimation = adjustTarget(targetValue)
if (targetAnimation == null) {
val animWrapper = decay.createAnimation(value, startVelocity)
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
index 94ff29b..a9157b88 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
@@ -27,18 +27,19 @@
*
* __Note__: [Animation] does not track the lifecycle of an animation. It merely reacts to play time
* change and returns the new value/velocity as a result. It can be used as a building block for
- * more lifecycle aware animations. In contrast, [Animatable] and [TransitionAnimation] are
+ * more lifecycle aware animations. In contrast, [Animatable] and [Transition] are
* stateful and manage their own lifecycles, and subscribe/unsubscribe from an
* [AnimationClockObservable] as needed.
*
* @see [Animatable]
- * @see [androidx.compose.animation.transition]
+ * @see [updateTransition]
*/
interface Animation<T, V : AnimationVector> {
/**
- * This amount of time in milliseconds that the animation will run before it finishes
+ * This amount of time in nanoseconds that the animation will run before it finishes
*/
- val durationMillis: Long
+ @get:Suppress("MethodNameUnits")
+ val durationNanos: Long
/**
* The [TwoWayConverter] that will be used to convert value/velocity from any arbitrary data
@@ -62,35 +63,39 @@
/**
* Returns the value of the animation at the given play time.
*
- * @param playTime the play time that is used to determine the value of the animation.
+ * @param playTimeNanos the play time that is used to determine the value of the animation.
*/
- fun getValue(playTime: Long): T
+ fun getValueFromNanos(playTimeNanos: Long): T
/**
* Returns the velocity (in [AnimationVector] form) of the animation at the given play time.
*
- * @param playTime the play time that is used to calculate the velocity of the animation.
+ * @param playTimeNanos the play time that is used to calculate the velocity of the animation.
*/
- fun getVelocityVector(playTime: Long): V
+ fun getVelocityVectorFromNanos(playTimeNanos: Long): V
/**
* Returns whether the animation is finished at the given play time.
*
- * @param playTime the play time used to determine whether the animation is finished.
+ * @param playTimeNanos the play time used to determine whether the animation is finished.
*/
- fun isFinished(playTime: Long): Boolean {
- return playTime >= durationMillis
+ fun isFinishedFromNanos(playTimeNanos: Long): Boolean {
+ return playTimeNanos >= durationNanos
}
}
+internal val Animation<*, *>.durationMillis: Long
+ get() = durationNanos / MillisToNanos
+
+internal const val MillisToNanos: Long = 1_000_000L
+
/**
* Returns the velocity of the animation at the given play time.
*
- * @param playTime the play time that is used to calculate the velocity of the animation.
+ * @param playTimeNanos the play time that is used to calculate the velocity of the animation.
*/
-fun <T, V : AnimationVector> Animation<T, V>.getVelocity(playTime: Long): T =
- typeConverter.convertFromVector(getVelocityVector(playTime))
-
+fun <T, V : AnimationVector> Animation<T, V>.getVelocityFromNanos(playTimeNanos: Long): T =
+ typeConverter.convertFromVector(getVelocityVectorFromNanos(playTimeNanos))
/**
* Creates a [TargetBasedAnimation] from a given [VectorizedAnimationSpec] of [AnimationVector] type. This
* convenient method is intended for when the value being animated (i.e. start value, end value,
@@ -127,7 +132,7 @@
* __Note__: When interruptions happen to the [TargetBasedAnimation], a new instance should
* be created that use the current value and velocity as the starting conditions. This type of
* interruption handling is the default behavior for both [Animatable] and
- * [TransitionAnimation]. Consider using those APIs for the interruption handling, as well as
+ * [Transition]. Consider using those APIs for the interruption handling, as well as
* built-in animation lifecycle management.
*
* @param animationSpec the [AnimationSpec] that will be used to calculate value/velocity
@@ -161,7 +166,7 @@
* __Note__: When interruptions happen to the [TargetBasedAnimation], a new instance should
* be created that use the current value and velocity as the starting conditions. This type of
* interruption handling is the default behavior for both [Animatable] and
- * [TransitionAnimation]. Consider using those APIs for the interruption handling, as well as
+ * [Transition]. Consider using those APIs for the interruption handling, as well as
* built-in animation lifecycle management.
*
* @param animationSpec the [VectorizedAnimationSpec] that will be used to calculate value/velocity
@@ -170,8 +175,8 @@
* @param typeConverter the [TwoWayConverter] that is used to convert animation type [T] from/to [V]
* @param initialVelocityVector the start velocity of the animation in the form of [AnimationVector]
*
- * @see [TransitionAnimation]
- * @see [androidx.compose.animation.transition]
+ * @see [Transition]
+ * @see [updateTransition]
* @see [Animatable]
*/
class TargetBasedAnimation<T, V : AnimationVector> internal constructor(
@@ -194,7 +199,7 @@
* __Note__: When interruptions happen to the [TargetBasedAnimation], a new instance should
* be created that use the current value and velocity as the starting conditions. This type of
* interruption handling is the default behavior for both [Animatable] and
- * [TransitionAnimation]. Consider using those APIs for the interruption handling, as well as
+ * [Transition]. Consider using those APIs for the interruption handling, as well as
* built-in animation lifecycle management.
*
* @param animationSpec the [AnimationSpec] that will be used to calculate value/velocity
@@ -224,12 +229,11 @@
.newInstance()
override val isInfinite: Boolean get() = animationSpec.isInfinite
-
- override fun getValue(playTime: Long): T {
- return if (playTime < durationMillis) {
+ override fun getValueFromNanos(playTimeNanos: Long): T {
+ return if (!isFinishedFromNanos(playTimeNanos)) {
typeConverter.convertFromVector(
- animationSpec.getValue(
- playTime, initialValueVector,
+ animationSpec.getValueFromNanos(
+ playTimeNanos, initialValueVector,
targetValueVector, initialVelocityVector
)
)
@@ -238,10 +242,11 @@
}
}
- override val durationMillis: Long = animationSpec.getDurationMillis(
- start = initialValueVector,
- end = targetValueVector,
- startVelocity = this.initialVelocityVector
+ @get:Suppress("MethodNameUnits")
+ override val durationNanos: Long = animationSpec.getDurationNanos(
+ initialValue = initialValueVector,
+ targetValue = targetValueVector,
+ initialVelocity = this.initialVelocityVector
)
private val endVelocity = animationSpec.getEndVelocity(
@@ -250,10 +255,10 @@
this.initialVelocityVector
)
- override fun getVelocityVector(playTime: Long): V {
- return if (playTime < durationMillis) {
- animationSpec.getVelocity(
- playTime,
+ override fun getVelocityVectorFromNanos(playTimeNanos: Long): V {
+ return if (!isFinishedFromNanos(playTimeNanos)) {
+ animationSpec.getVelocityFromNanos(
+ playTimeNanos,
initialValueVector,
targetValueVector,
initialVelocityVector
@@ -289,9 +294,10 @@
private val endVelocity: V
override val targetValue: T = typeConverter.convertFromVector(
- animationSpec.getTarget(initialValueVector, initialVelocityVector)
+ animationSpec.getTargetValue(initialValueVector, initialVelocityVector)
)
- override val durationMillis: Long
+ @get:Suppress("MethodNameUnits")
+ override val durationNanos: Long
// DecayAnimation finishes by design
override val isInfinite: Boolean = false
@@ -359,11 +365,11 @@
)
init {
- durationMillis = animationSpec.getDurationMillis(
+ durationNanos = animationSpec.getDurationNanos(
initialValueVector, initialVelocityVector
)
- endVelocity = animationSpec.getVelocity(
- durationMillis,
+ endVelocity = animationSpec.getVelocityFromNanos(
+ durationNanos,
initialValueVector,
initialVelocityVector
).copy()
@@ -375,19 +381,27 @@
}
}
- override fun getValue(playTime: Long): T {
- if (!isFinished(playTime)) {
+ override fun getValueFromNanos(playTimeNanos: Long): T {
+ if (!isFinishedFromNanos(playTimeNanos)) {
return typeConverter.convertFromVector(
- animationSpec.getValue(playTime, initialValueVector, initialVelocityVector)
+ animationSpec.getValueFromNanos(
+ playTimeNanos,
+ initialValueVector,
+ initialVelocityVector
+ )
)
} else {
return targetValue
}
}
- override fun getVelocityVector(playTime: Long): V {
- if (!isFinished(playTime)) {
- return animationSpec.getVelocity(playTime, initialValueVector, initialVelocityVector)
+ override fun getVelocityVectorFromNanos(playTimeNanos: Long): V {
+ if (!isFinishedFromNanos(playTimeNanos)) {
+ return animationSpec.getVelocityFromNanos(
+ playTimeNanos,
+ initialValueVector,
+ initialVelocityVector
+ )
} else {
return endVelocity
}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
index 3bee21f..959c2d5 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
@@ -60,7 +60,7 @@
initialVelocity: T
): T {
val vectorizedSpec = vectorize(typeConverter)
- val targetVector = vectorizedSpec.getTarget(
+ val targetVector = vectorizedSpec.getTargetValue(
typeConverter.convertToVector(initialValue),
typeConverter.convertToVector(initialVelocity)
)
@@ -78,7 +78,7 @@
initialVelocity: Float
): Float {
val vectorizedSpec = vectorize(Float.VectorConverter)
- val targetVector = vectorizedSpec.getTarget(
+ val targetVector = vectorizedSpec.getTargetValue(
AnimationVector(initialValue),
AnimationVector(initialVelocity)
)
@@ -136,17 +136,21 @@
private lateinit var targetVector: V
override val absVelocityThreshold: Float = floatDecaySpec.absVelocityThreshold
- override fun getValue(playTime: Long, initialValue: V, initialVelocity: V): V {
+ override fun getValueFromNanos(playTimeNanos: Long, initialValue: V, initialVelocity: V): V {
if (!::valueVector.isInitialized) {
valueVector = initialValue.newInstance()
}
for (i in 0 until valueVector.size) {
- valueVector[i] = floatDecaySpec.getValue(playTime, initialValue[i], initialVelocity[i])
+ valueVector[i] = floatDecaySpec.getValueFromNanos(
+ playTimeNanos,
+ initialValue[i],
+ initialVelocity[i]
+ )
}
return valueVector
}
- override fun getDurationMillis(initialValue: V, initialVelocity: V): Long {
+ override fun getDurationNanos(initialValue: V, initialVelocity: V): Long {
var maxDuration = 0L
if (!::velocityVector.isInitialized) {
velocityVector = initialValue.newInstance()
@@ -154,19 +158,19 @@
for (i in 0 until velocityVector.size) {
maxDuration = maxOf(
maxDuration,
- floatDecaySpec.getDurationMillis(initialValue[i], initialVelocity[i])
+ floatDecaySpec.getDurationNanos(initialValue[i], initialVelocity[i])
)
}
return maxDuration
}
- override fun getVelocity(playTime: Long, initialValue: V, initialVelocity: V): V {
+ override fun getVelocityFromNanos(playTimeNanos: Long, initialValue: V, initialVelocity: V): V {
if (!::velocityVector.isInitialized) {
velocityVector = initialValue.newInstance()
}
for (i in 0 until velocityVector.size) {
- velocityVector[i] = floatDecaySpec.getVelocity(
- playTime,
+ velocityVector[i] = floatDecaySpec.getVelocityFromNanos(
+ playTimeNanos,
initialValue[i],
initialVelocity[i]
)
@@ -174,12 +178,12 @@
return velocityVector
}
- override fun getTarget(initialValue: V, initialVelocity: V): V {
+ override fun getTargetValue(initialValue: V, initialVelocity: V): V {
if (!::targetVector.isInitialized) {
targetVector = initialValue.newInstance()
}
for (i in 0 until targetVector.size) {
- targetVector[i] = floatDecaySpec.getTarget(
+ targetVector[i] = floatDecaySpec.getTargetValue(
initialValue[i],
initialVelocity[i]
)
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
index 56592ee..3dd1872 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
@@ -19,12 +19,12 @@
import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
/**
- * [FloatAnimationSpec] interface is similar to [VectorizedAnimationSpec], except it deals exclusively with
- * floats.
+ * [FloatAnimationSpec] interface is similar to [VectorizedAnimationSpec], except it deals
+ * exclusively with floats.
*
- * Like [VectorizedAnimationSpec], [FloatAnimationSpec] is entirely stateless as well. It requires start/end
- * values and start velocity to be passed in for the query of velocity and value of the animation.
- * The [FloatAnimationSpec] itself stores only the animation configuration (such as the
+ * Like [VectorizedAnimationSpec], [FloatAnimationSpec] is entirely stateless as well. It requires
+ * start/end values and start velocity to be passed in for the query of velocity and value of the
+ * animation. The [FloatAnimationSpec] itself stores only the animation configuration (such as the
* delay, duration and easing curve for [FloatTweenSpec], or spring constants for
* [FloatSpringSpec].
*
@@ -37,32 +37,32 @@
* Calculates the value of the animation at given the playtime, with the provided start/end
* values, and start velocity.
*
- * @param playTime time since the start of the animation
- * @param start start value of the animation
- * @param end end value of the animation
- * @param startVelocity start velocity of the animation
+ * @param playTimeNanos time since the start of the animation
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
*/
- fun getValue(
- playTime: Long,
- start: Float,
- end: Float,
- startVelocity: Float
+ fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
): Float
/**
* Calculates the velocity of the animation at given the playtime, with the provided start/end
* values, and start velocity.
*
- * @param playTime time since the start of the animation
- * @param start start value of the animation
- * @param end end value of the animation
- * @param startVelocity start velocity of the animation
+ * @param playTimeNanos time since the start of the animation
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
*/
- fun getVelocity(
- playTime: Long,
- start: Float,
- end: Float,
- startVelocity: Float
+ fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
): Float
/**
@@ -71,33 +71,40 @@
* animation at the duration time. This is also the default assumption. However, for
* spring animations, the transient trailing velocity will be snapped to zero.
*
- * @param start start value of the animation
- * @param end end value of the animation
- * @param startVelocity start velocity of the animation
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
*/
fun getEndVelocity(
- start: Float,
- end: Float,
- startVelocity: Float
- ): Float = getVelocity(getDurationMillis(start, end, startVelocity), start, end, startVelocity)
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
+ ): Float =
+ getVelocityFromNanos(
+ getDurationNanos(initialValue, targetValue, initialVelocity),
+ initialValue,
+ targetValue,
+ initialVelocity
+ )
/**
* Calculates the duration of an animation. For duration-based animations, this will return the
* pre-defined duration. For physics-based animations, the duration will be estimated based on
* the physics configuration (such as spring stiffness, damping ratio, visibility threshold)
- * as well as the [start], [end] values, and [startVelocity].
+ * as well as the [initialValue], [targetValue] values, and [initialVelocity].
*
* __Note__: this may be a computation that is expensive - especially with spring based
* animations
*
- * @param start start value of the animation
- * @param end end value of the animation
- * @param startVelocity start velocity of the animation
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
*/
- fun getDurationMillis(
- start: Float,
- end: Float,
- startVelocity: Float
+ @Suppress("MethodNameUnits")
+ fun getDurationNanos(
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
): Long
/**
@@ -130,42 +137,51 @@
it.stiffness = stiffness
}
- override fun getValue(
- playTime: Long,
- start: Float,
- end: Float,
- startVelocity: Float
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
): Float {
- spring.finalPosition = end
- val value = spring.updateValues(start, startVelocity, playTime).value
+ // TODO: Properly support Nanos in the spring impl
+ val playTimeMillis = playTimeNanos / MillisToNanos
+ spring.finalPosition = targetValue
+ val value = spring.updateValues(initialValue, initialVelocity, playTimeMillis).value
return value
}
- override fun getVelocity(
- playTime: Long,
- start: Float,
- end: Float,
- startVelocity: Float
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
): Float {
- spring.finalPosition = end
- val velocity = spring.updateValues(start, startVelocity, playTime).velocity
+ // TODO: Properly support Nanos in the spring impl
+ val playTimeMillis = playTimeNanos / MillisToNanos
+ spring.finalPosition = targetValue
+ val velocity = spring.updateValues(initialValue, initialVelocity, playTimeMillis).velocity
return velocity
}
override fun getEndVelocity(
- start: Float,
- end: Float,
- startVelocity: Float
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
): Float = 0f
- override fun getDurationMillis(start: Float, end: Float, startVelocity: Float): Long =
+ @Suppress("MethodNameUnits")
+ override fun getDurationNanos(
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
+ ): Long =
estimateAnimationDurationMillis(
stiffness = spring.stiffness,
dampingRatio = spring.dampingRatio,
- initialDisplacement = (start - end) / visibilityThreshold,
- initialVelocity = startVelocity / visibilityThreshold,
+ initialDisplacement = (initialValue - targetValue) / visibilityThreshold,
+ initialVelocity = initialVelocity / visibilityThreshold,
delta = 1f
- )
+ ) * MillisToNanos
}
/**
@@ -184,43 +200,62 @@
val delay: Int = 0,
private val easing: Easing = FastOutSlowInEasing
) : FloatAnimationSpec {
- override fun getValue(
- playTime: Long,
- start: Float,
- end: Float,
- startVelocity: Float
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
): Float {
- val clampedPlayTime = clampPlayTime(playTime)
+ // TODO: Properly support Nanos in the impl
+ val playTimeMillis = playTimeNanos / MillisToNanos
+ val clampedPlayTime = clampPlayTime(playTimeMillis)
val rawFraction = if (duration == 0) 1f else clampedPlayTime / duration.toFloat()
val fraction = easing.transform(rawFraction.coerceIn(0f, 1f))
- return lerp(start, end, fraction)
+ return lerp(initialValue, targetValue, fraction)
}
private fun clampPlayTime(playTime: Long): Long {
return (playTime - delay).coerceIn(0, duration.toLong())
}
- override fun getDurationMillis(start: Float, end: Float, startVelocity: Float): Long {
- return delay + duration.toLong()
+ @Suppress("MethodNameUnits")
+ override fun getDurationNanos(
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
+ ): Long {
+ return (delay + duration) * MillisToNanos
}
// Calculate velocity by difference between the current value and the value 1 ms ago. This is a
// preliminary way of calculating velocity used by easing curve based animations, and keyframe
// animations. Physics-based animations give a much more accurate velocity.
- override fun getVelocity(
- playTime: Long,
- start: Float,
- end: Float,
- startVelocity: Float
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ targetValue: Float,
+ initialVelocity: Float
): Float {
- val clampedPlayTime = clampPlayTime(playTime)
+ // TODO: Properly support Nanos in the impl
+ val playTimeMillis = playTimeNanos / MillisToNanos
+ val clampedPlayTime = clampPlayTime(playTimeMillis)
if (clampedPlayTime < 0) {
return 0f
} else if (clampedPlayTime == 0L) {
- return startVelocity
+ return initialVelocity
}
- val startNum = getValue(clampedPlayTime - 1, start, end, startVelocity)
- val endNum = getValue(clampedPlayTime, start, end, startVelocity)
+ val startNum = getValueFromNanos(
+ (clampedPlayTime - 1) * MillisToNanos,
+ initialValue,
+ targetValue,
+ initialVelocity
+ )
+ val endNum = getValueFromNanos(
+ clampedPlayTime * MillisToNanos,
+ initialValue,
+ targetValue,
+ initialVelocity
+ )
return (endNum - startNum) * 1000f
}
}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
index 2eacf04..98cc051 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
@@ -36,50 +36,51 @@
/**
* Returns the value of the animation at the given time.
*
- * @param playTime The time elapsed in milliseconds since the start of the animation
- * @param start The start value of the animation
- * @param startVelocity The start velocity of the animation
+ * @param playTimeNanos The time elapsed in milliseconds since the start of the animation
+ * @param initialValue The start value of the animation
+ * @param initialVelocity The start velocity of the animation
*/
- fun getValue(
- playTime: Long,
- start: Float,
- startVelocity: Float
+ fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ initialVelocity: Float
): Float
/**
- * Returns the duration of the decay animation, in milliseconds.
+ * Returns the duration of the decay animation, in nanoseconds.
*
- * @param start start value of the animation
- * @param startVelocity start velocity of the animation
+ * @param initialValue start value of the animation
+ * @param initialVelocity start velocity of the animation
*/
- fun getDurationMillis(
- start: Float,
- startVelocity: Float
+ @Suppress("MethodNameUnits")
+ fun getDurationNanos(
+ initialValue: Float,
+ initialVelocity: Float
): Long
/**
* Returns the velocity of the animation at the given time.
*
- * @param playTime The time elapsed in milliseconds since the start of the animation
- * @param start The start value of the animation
- * @param startVelocity The start velocity of the animation
+ * @param playTimeNanos The time elapsed in milliseconds since the start of the animation
+ * @param initialValue The start value of the animation
+ * @param initialVelocity The start velocity of the animation
*/
- fun getVelocity(
- playTime: Long,
- start: Float,
- startVelocity: Float
+ fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ initialVelocity: Float
): Float
/**
* Returns the target value of the animation based on the starting condition of the animation (
* i.e. start value and start velocity).
*
- * @param start The start value of the animation
- * @param startVelocity The start velocity of the animation
+ * @param initialValue The start value of the animation
+ * @param initialVelocity The start velocity of the animation
*/
- fun getTarget(
- start: Float,
- startVelocity: Float
+ fun getTargetValue(
+ initialValue: Float,
+ initialVelocity: Float
): Float
}
@@ -113,40 +114,46 @@
override val absVelocityThreshold: Float = max(0.0000001f, abs(absVelocityThreshold))
private val friction: Float = ExponentialDecayFriction * max(0.0001f, frictionMultiplier)
- override fun getValue(
- playTime: Long,
- start: Float,
- startVelocity: Float
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ initialVelocity: Float
): Float {
- return start - startVelocity / friction +
- startVelocity / friction * exp(friction * playTime / 1000f)
+ // TODO: Properly support nanos
+ val playTimeMillis = playTimeNanos / MillisToNanos
+ return initialValue - initialVelocity / friction +
+ initialVelocity / friction * exp(friction * playTimeMillis / 1000f)
}
- override fun getVelocity(
- playTime: Long,
- start: Float,
- startVelocity: Float
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ initialVelocity: Float
): Float {
- return (startVelocity * exp(((playTime / 1000f) * friction)))
+ // TODO: Properly support nanos
+ val playTimeMillis = playTimeNanos / MillisToNanos
+ return (initialVelocity * exp(((playTimeMillis / 1000f) * friction)))
}
- override fun getDurationMillis(start: Float, startVelocity: Float): Long {
+ @Suppress("MethodNameUnits")
+ override fun getDurationNanos(initialValue: Float, initialVelocity: Float): Long {
// Inverse of getVelocity
- return (1000f * ln(absVelocityThreshold / abs(startVelocity)) / friction).toLong()
+ return (1000f * ln(absVelocityThreshold / abs(initialVelocity)) / friction)
+ .toLong() * MillisToNanos
}
- override fun getTarget(
- start: Float,
- startVelocity: Float
+ override fun getTargetValue(
+ initialValue: Float,
+ initialVelocity: Float
): Float {
- if (abs(startVelocity) <= absVelocityThreshold) {
- return start
+ if (abs(initialVelocity) <= absVelocityThreshold) {
+ return initialValue
}
val duration: Double =
- ln(abs(absVelocityThreshold / startVelocity).toDouble()) / friction * 1000
+ ln(abs(absVelocityThreshold / initialVelocity).toDouble()) / friction * 1000
- return start - startVelocity / friction +
- startVelocity / friction * exp((friction * duration / 1000f)).toFloat()
+ return initialValue - initialVelocity / friction +
+ initialVelocity / friction * exp((friction * duration / 1000f)).toFloat()
}
}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
index dd37aaa..f1c9d3a 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
@@ -106,9 +106,9 @@
startOnTheNextFrame = false
playTimeNanosOffset = playTimeNanos
}
- val playTimeMillis = (playTimeNanos - playTimeNanosOffset) / 1_000_000L
- value = animation.getValue(playTimeMillis)
- isFinished = animation.isFinished(playTimeMillis)
+ val playTime = playTimeNanos - playTimeNanosOffset
+ value = animation.getValueFromNanos(playTime)
+ isFinished = animation.isFinishedFromNanos(playTime)
}
}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
index 5594bfd..5ad9c06 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
@@ -221,8 +221,8 @@
startTimeNanos: Long = AnimationConstants.UnspecifiedTime,
block: AnimationScope<T, V>.() -> Unit = {}
) {
- val initialValue = animation.getValue(0)
- val initialVelocityVector = animation.getVelocityVector(0)
+ val initialValue = animation.getValueFromNanos(0)
+ val initialVelocityVector = animation.getVelocityVectorFromNanos(0)
var lateInitScope: AnimationScope<T, V>? = null
try {
val startTimeNanosSpecified =
@@ -278,11 +278,10 @@
block: AnimationScope<T, V>.() -> Unit
) {
lastFrameTimeNanos = frameTimeNanos
- val playTimeMillis = (frameTimeNanos - startTimeNanos) / 1_000_000L
- // TODO: [Animation] should use nanos for all the value/velocity queries
- value = anim.getValue(playTimeMillis)
- velocityVector = anim.getVelocityVector(playTimeMillis)
- val isLastFrame = anim.isFinished(playTimeMillis)
+ val playTimeNanos = frameTimeNanos - startTimeNanos
+ value = anim.getValueFromNanos(playTimeNanos)
+ velocityVector = anim.getVelocityVectorFromNanos(playTimeNanos)
+ val isLastFrame = anim.isFinishedFromNanos(playTimeNanos)
if (isLastFrame) {
// TODO: This could probably be a little more granular
// TODO: end time isn't necessarily last frame time
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index fb21aa8..d7b4510 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -375,13 +375,13 @@
internal set
internal var velocityVector: V = initialVelocityVector
internal val durationNanos
- get() = animation.durationMillis.times(1_000_000L)
+ get() = animation.durationNanos
internal fun onPlayTimeChanged(playTimeNanos: Long) {
- val playTimeMillis = (playTimeNanos - offsetTimeNanos) / 1_000_000L
- value = animation.getValue(playTimeMillis)
- velocityVector = animation.getVelocityVector(playTimeMillis)
- if (animation.isFinished(playTimeMillis)) {
+ val playTime = playTimeNanos - offsetTimeNanos
+ value = animation.getValueFromNanos(playTime)
+ velocityVector = animation.getVelocityVectorFromNanos(playTime)
+ if (animation.isFinishedFromNanos(playTime)) {
isFinished = true
offsetTimeNanos = 0
}
@@ -390,9 +390,8 @@
internal fun seekTo(playTimeNanos: Long) {
// TODO: unlikely but need to double check that animation returns the correct values
// when play time is way past their durations.
- val playTimeMillis = playTimeNanos / 1_000_000L
- value = animation.getValue(playTimeMillis)
- velocityVector = animation.getVelocityVector(playTimeMillis)
+ value = animation.getValueFromNanos(playTimeNanos)
+ velocityVector = animation.getVelocityVectorFromNanos(playTimeNanos)
}
private fun updateAnimation(initialValue: T = value) {
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
index 5b5677a..f9d455c 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
@@ -31,7 +31,7 @@
* Since [VectorizedAnimationSpec]s are stateless, it requires starting value/velocity and ending
* value to be passed in, along with playtime, to calculate the value or velocity at that time. Play
* time here is the progress of the animation in terms of milliseconds, where 0 means the start
- * of the animation and [getDurationMillis] returns the play time for the end of the animation.
+ * of the animation and [getDurationNanos] returns the play time for the end of the animation.
*
* __Note__: For use cases where the starting values/velocity and ending values aren't expected
* to change, it is recommended to use [Animation] that caches these static values and hence
@@ -51,48 +51,49 @@
* Calculates the value of the animation at given the playtime, with the provided start/end
* values, and start velocity.
*
- * @param playTime time since the start of the animation
- * @param start start value of the animation
- * @param end end value of the animation
- * @param startVelocity start velocity of the animation
+ * @param playTimeNanos time since the start of the animation
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
*/
- fun getValue(
- playTime: Long,
- start: V,
- end: V,
- startVelocity: V
+ fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
): V
/**
* Calculates the velocity of the animation at given the playtime, with the provided start/end
* values, and start velocity.
*
- * @param playTime time since the start of the animation
- * @param start start value of the animation
- * @param end end value of the animation
- * @param startVelocity start velocity of the animation
+ * @param playTimeNanos time since the start of the animation
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
*/
- fun getVelocity(
- playTime: Long,
- start: V,
- end: V,
- startVelocity: V
+ fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
): V
/**
* Calculates the duration of an animation. For duration-based animations, this will return the
* pre-defined duration. For physics-based animations, the duration will be estimated based on
* the physics configuration (such as spring stiffness, damping ratio, visibility threshold)
- * as well as the [start], [end] values, and [startVelocity].
+ * as well as the [initialValue], [targetValue] values, and [initialVelocity].
*
- * @param start start value of the animation
- * @param end end value of the animation
- * @param startVelocity start velocity of the animation
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
*/
- fun getDurationMillis(
- start: V,
- end: V,
- startVelocity: V
+ @Suppress("MethodNameUnits")
+ fun getDurationNanos(
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
): Long
/**
@@ -101,18 +102,56 @@
* animation at the duration time. This is also the default assumption. However, for
* physics-based animations, end velocity is an [AnimationVector] of 0s.
*
- * @param start start value of the animation
- * @param end end value of the animation
- * @param startVelocity start velocity of the animation
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
*/
fun getEndVelocity(
- start: V,
- end: V,
- startVelocity: V
- ): V = getVelocity(getDurationMillis(start, end, startVelocity), start, end, startVelocity)
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
+ ): V = getVelocityFromNanos(
+ getDurationNanos(initialValue, targetValue, initialVelocity),
+ initialValue,
+ targetValue,
+ initialVelocity
+ )
}
/**
+ * Calculates the duration of an animation. For duration-based animations, this will return the
+ * pre-defined duration. For physics-based animations, the duration will be estimated based on
+ * the physics configuration (such as spring stiffness, damping ratio, visibility threshold)
+ * as well as the [initialValue], [targetValue] values, and [initialVelocity].
+ *
+ * @param initialValue start value of the animation
+ * @param targetValue end value of the animation
+ * @param initialVelocity start velocity of the animation
+ */
+internal fun <V : AnimationVector> VectorizedAnimationSpec<V>.getDurationMillis(
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
+): Long = getDurationNanos(initialValue, targetValue, initialVelocity) / MillisToNanos
+
+/**
+ * Calculates the value of the animation at given the playtime, with the provided start/end
+ * values, and start velocity.
+ *
+ * @param playTimeMillis time since the start of the animation
+ * @param start start value of the animation
+ * @param end end value of the animation
+ * @param startVelocity start velocity of the animation
+ */
+// TODO: Move tests off this API
+internal fun <V : AnimationVector> VectorizedAnimationSpec<V>.getValueFromMillis(
+ playTimeMillis: Long,
+ start: V,
+ end: V,
+ startVelocity: V
+): V = getValueFromNanos(playTimeMillis * MillisToNanos, start, end, startVelocity)
+
+/**
* All the finite [VectorizedAnimationSpec]s implement this interface, including:
* [VectorizedKeyframesSpec], [VectorizedTweenSpec], [VectorizedRepeatableSpec],
* [VectorizedSnapSpec], [VectorizedSpringSpec], etc. The [VectorizedAnimationSpec] that does
@@ -137,8 +176,9 @@
*/
val delayMillis: Int
- override fun getDurationMillis(start: V, end: V, startVelocity: V): Long =
- (delayMillis + durationMillis).toLong()
+ @Suppress("MethodNameUnits")
+ override fun getDurationNanos(initialValue: V, targetValue: V, initialVelocity: V): Long =
+ (delayMillis + durationMillis) * MillisToNanos
}
/**
@@ -188,25 +228,26 @@
private lateinit var valueVector: V
private lateinit var velocityVector: V
- override fun getValue(
- playTime: Long,
- start: V,
- end: V,
- startVelocity: V
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
): V {
- val clampedPlayTime = clampPlayTime(playTime).toInt()
+ val playTimeMillis = playTimeNanos / MillisToNanos
+ val clampedPlayTime = clampPlayTime(playTimeMillis).toInt()
// If there is a key frame defined with the given time stamp, return that value
if (keyframes.containsKey(clampedPlayTime)) {
return keyframes.getValue(clampedPlayTime).first
}
if (clampedPlayTime >= durationMillis) {
- return end
- } else if (clampedPlayTime <= 0) return start
+ return targetValue
+ } else if (clampedPlayTime <= 0) return initialValue
var startTime = 0
- var startVal = start
- var endVal = end
+ var startVal = initialValue
+ var endVal = targetValue
var endTime: Int = durationMillis
var easing: Easing = LinearEasing
for ((timestamp, value) in keyframes) {
@@ -224,7 +265,7 @@
val fraction = easing.transform(
(clampedPlayTime - startTime) / (endTime - startTime).toFloat()
)
- init(start)
+ init(initialValue)
for (i in 0 until startVal.size) {
valueVector[i] = lerp(startVal[i], endVal[i], fraction)
}
@@ -238,20 +279,31 @@
}
}
- override fun getVelocity(
- playTime: Long,
- start: V,
- end: V,
- startVelocity: V
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
): V {
- val clampedPlayTime = clampPlayTime(playTime)
+ val playTimeMillis = playTimeNanos / MillisToNanos
+ val clampedPlayTime = clampPlayTime(playTimeMillis)
if (clampedPlayTime <= 0L) {
- return startVelocity
+ return initialVelocity
}
- val startNum = getValue(clampedPlayTime - 1, start, end, startVelocity)
- val endNum = getValue(clampedPlayTime, start, end, startVelocity)
+ val startNum = getValueFromMillis(
+ clampedPlayTime - 1,
+ initialValue,
+ targetValue,
+ initialVelocity
+ )
+ val endNum = getValueFromMillis(
+ clampedPlayTime,
+ initialValue,
+ targetValue,
+ initialVelocity
+ )
- init(start)
+ init(initialValue)
for (i in 0 until startNum.size) {
velocityVector[i] = (startNum[i] - endNum[i]) * 1000f
}
@@ -269,16 +321,26 @@
override val delayMillis: Int = 0
) : VectorizedDurationBasedAnimationSpec<V> {
- override fun getValue(playTime: Long, start: V, end: V, startVelocity: V): V {
- if (playTime < delayMillis) {
- return start
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
+ ): V {
+ if (playTimeNanos < delayMillis * MillisToNanos) {
+ return initialValue
} else {
- return end
+ return targetValue
}
}
- override fun getVelocity(playTime: Long, start: V, end: V, startVelocity: V): V {
- return startVelocity
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
+ ): V {
+ return initialVelocity
}
override val durationMillis: Int
@@ -329,55 +391,61 @@
}
}
- internal val duration: Int = animation.delayMillis + animation.durationMillis
+ internal val durationNanos: Long =
+ (animation.delayMillis + animation.durationMillis) * MillisToNanos
- private fun repetitionPlayTime(playTime: Long): Long {
- val repeatsCount = min(playTime / duration, iterations - 1L)
+ private fun repetitionPlayTimeNanos(playTimeNanos: Long): Long {
+ val repeatsCount = min(playTimeNanos / durationNanos, iterations - 1L)
if (repeatMode == RepeatMode.Restart || repeatsCount % 2 == 0L) {
- return playTime - repeatsCount * duration
+ return playTimeNanos - repeatsCount * durationNanos
} else {
- return (repeatsCount + 1) * duration - playTime
+ return (repeatsCount + 1) * durationNanos - playTimeNanos
}
}
- private fun repetitionStartVelocity(playTime: Long, start: V, startVelocity: V, end: V): V =
- if (playTime > duration) {
- // Start velocity of the 2nd and subsequent iteration will be the velocity at the end
- // of the first iteration, instead of the initial velocity.
- getVelocity(duration.toLong(), start, startVelocity, end)
- } else
- startVelocity
-
- override fun getValue(
- playTime: Long,
+ private fun repetitionStartVelocity(
+ playTimeNanos: Long,
start: V,
- end: V,
- startVelocity: V
+ startVelocity: V,
+ end: V
+ ): V = if (playTimeNanos > durationNanos) {
+ // Start velocity of the 2nd and subsequent iteration will be the velocity at the end
+ // of the first iteration, instead of the initial velocity.
+ getVelocityFromNanos(durationNanos, start, startVelocity, end)
+ } else
+ startVelocity
+
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
): V {
- return animation.getValue(
- repetitionPlayTime(playTime),
- start,
- end,
- repetitionStartVelocity(playTime, start, startVelocity, end)
+ return animation.getValueFromNanos(
+ repetitionPlayTimeNanos(playTimeNanos),
+ initialValue,
+ targetValue,
+ repetitionStartVelocity(playTimeNanos, initialValue, initialVelocity, targetValue)
)
}
- override fun getVelocity(
- playTime: Long,
- start: V,
- end: V,
- startVelocity: V
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
): V {
- return animation.getVelocity(
- repetitionPlayTime(playTime),
- start,
- end,
- repetitionStartVelocity(playTime, start, startVelocity, end)
+ return animation.getVelocityFromNanos(
+ repetitionPlayTimeNanos(playTimeNanos),
+ initialValue,
+ targetValue,
+ repetitionStartVelocity(playTimeNanos, initialValue, initialVelocity, targetValue)
)
}
- override fun getDurationMillis(start: V, end: V, startVelocity: V): Long {
- return iterations * duration.toLong()
+ @Suppress("MethodNameUnits")
+ override fun getDurationNanos(initialValue: V, targetValue: V, initialVelocity: V): Long {
+ return iterations * durationNanos
}
}
@@ -517,12 +585,22 @@
FloatTweenSpec(durationMillis, delayMillis, easing)
)
- override fun getValue(playTime: Long, start: V, end: V, startVelocity: V): V {
- return anim.getValue(playTime, start, end, startVelocity)
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
+ ): V {
+ return anim.getValueFromNanos(playTimeNanos, initialValue, targetValue, initialVelocity)
}
- override fun getVelocity(playTime: Long, start: V, end: V, startVelocity: V): V {
- return anim.getVelocity(playTime, start, end, startVelocity)
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
+ ): V {
+ return anim.getVelocityFromNanos(playTimeNanos, initialValue, targetValue, initialVelocity)
}
}
@@ -550,43 +628,65 @@
}
})
- override fun getValue(playTime: Long, start: V, end: V, startVelocity: V): V {
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
+ ): V {
if (!::valueVector.isInitialized) {
- valueVector = start.newInstance()
+ valueVector = initialValue.newInstance()
}
for (i in 0 until valueVector.size) {
- valueVector[i] = anims[i].getValue(playTime, start[i], end[i], startVelocity[i])
+ valueVector[i] = anims[i].getValueFromNanos(
+ playTimeNanos,
+ initialValue[i],
+ targetValue[i],
+ initialVelocity[i]
+ )
}
return valueVector
}
- override fun getVelocity(playTime: Long, start: V, end: V, startVelocity: V): V {
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: V,
+ targetValue: V,
+ initialVelocity: V
+ ): V {
if (!::velocityVector.isInitialized) {
- velocityVector = startVelocity.newInstance()
+ velocityVector = initialVelocity.newInstance()
}
for (i in 0 until velocityVector.size) {
- velocityVector[i] = anims[i].getVelocity(playTime, start[i], end[i], startVelocity[i])
+ velocityVector[i] =
+ anims[i].getVelocityFromNanos(
+ playTimeNanos,
+ initialValue[i],
+ targetValue[i],
+ initialVelocity[i]
+ )
}
return velocityVector
}
- override fun getEndVelocity(start: V, end: V, startVelocity: V): V {
+ override fun getEndVelocity(initialValue: V, targetValue: V, initialVelocity: V): V {
if (!::endVelocityVector.isInitialized) {
- endVelocityVector = startVelocity.newInstance()
+ endVelocityVector = initialVelocity.newInstance()
}
for (i in 0 until endVelocityVector.size) {
endVelocityVector[i] =
- anims[i].getEndVelocity(start[i], end[i], startVelocity[i])
+ anims[i].getEndVelocity(initialValue[i], targetValue[i], initialVelocity[i])
}
return endVelocityVector
}
- override fun getDurationMillis(start: V, end: V, startVelocity: V): Long {
+ @Suppress("MethodNameUnits")
+ override fun getDurationNanos(initialValue: V, targetValue: V, initialVelocity: V): Long {
var maxDuration = 0L
- (0 until start.size).forEach {
+ (0 until initialValue.size).forEach {
maxDuration = maxOf(
maxDuration,
- anims[it].getDurationMillis(start[it], end[it], startVelocity[it])
+ anims[it].getDurationNanos(initialValue[it], targetValue[it], initialVelocity[it])
)
}
return maxDuration
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt
index fba75bb..e893ad2 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt
@@ -26,7 +26,7 @@
* Since [VectorizedDecayAnimationSpec]s are stateless, it requires starting value/velocity and
* ending value to be passed in, along with playtime, to calculate the value or velocity at that
* time. Play time here is the progress of the animation in terms of milliseconds, where 0 means the
- * start of the animation and [getDurationMillis] returns the play time for the end of the
+ * start of the animation and [getDurationNanos] returns the play time for the end of the
* animation.
*
* __Note__: For use cases where the starting values/velocity and ending values aren't expected
@@ -45,23 +45,24 @@
/**
* Returns the value of the animation at the given time.
*
- * @param playTime The time elapsed in milliseconds since the initialValue of the animation
+ * @param playTimeNanos The time elapsed in milliseconds since the initialValue of the animation
* @param initialValue The initialValue value of the animation
* @param initialVelocity The initialValue velocity of the animation
*/
- fun getValue(
- playTime: Long,
+ fun getValueFromNanos(
+ playTimeNanos: Long,
initialValue: V,
initialVelocity: V
): V
/**
- * Returns the duration of the decay animation, in milliseconds.
+ * Returns the duration of the decay animation, in nanoseconds.
*
* @param initialValue initialValue value of the animation
* @param initialVelocity initialValue velocity of the animation
*/
- fun getDurationMillis(
+ @Suppress("MethodNameUnits")
+ fun getDurationNanos(
initialValue: V,
initialVelocity: V
): Long
@@ -69,12 +70,12 @@
/**
* Returns the velocity of the animation at the given time.
*
- * @param playTime The time elapsed in milliseconds since the initialValue of the animation
+ * @param playTimeNanos The time elapsed in milliseconds since the initialValue of the animation
* @param initialValue The initialValue value of the animation
* @param initialVelocity The initialValue velocity of the animation
*/
- fun getVelocity(
- playTime: Long,
+ fun getVelocityFromNanos(
+ playTimeNanos: Long,
initialValue: V,
initialVelocity: V
): V
@@ -86,7 +87,7 @@
* @param initialValue The initial value of the animation
* @param initialVelocity The initial velocity of the animation
*/
- fun getTarget(
+ fun getTargetValue(
initialValue: V,
initialVelocity: V
): V
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatableTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatableTest.kt
index 3f9d010..0cdf8da 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatableTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatableTest.kt
@@ -60,8 +60,12 @@
val result = animatable.animateDecay(20f, animationSpec = exponentialDecay()) {
assertTrue(isRunning)
assertEquals(anim.targetValue, targetValue)
- TestCase.assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
- TestCase.assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+ TestCase.assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+ TestCase.assertEquals(
+ anim.getVelocityFromMillis(playTimeMillis),
+ velocity,
+ 0.001f
+ )
playTimeMillis += interval
TestCase.assertEquals(value, animatable.value, 0.0001f)
TestCase.assertEquals(velocity, animatable.velocity, 0.0001f)
@@ -98,8 +102,8 @@
) {
assertTrue(isRunning)
assertEquals(1f, targetValue)
- assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
- assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+ assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+ assertEquals(anim.getVelocityFromMillis(playTimeMillis), velocity, 0.001f)
playTimeMillis += interval
}
// After animation
@@ -146,7 +150,7 @@
) {
assertTrue("PlayTime Millis: $playTimeMillis", isRunning)
assertEquals(to, targetValue)
- val expectedValue = anim.getValue(playTimeMillis)
+ val expectedValue = anim.getValueFromMillis(playTimeMillis)
assertEquals(
"PlayTime Millis: $playTimeMillis",
expectedValue.x,
@@ -207,8 +211,8 @@
) {
assertTrue(isRunning)
assertEquals(targetValue, 200f)
- assertEquals(anim1.getValue(playTimeMillis), value)
- assertEquals(anim1.getVelocity(playTimeMillis), velocity)
+ assertEquals(anim1.getValueFromMillis(playTimeMillis), value)
+ assertEquals(anim1.getVelocityFromMillis(playTimeMillis), velocity)
assertTrue(playTimeMillis <= 100)
if (playTimeMillis == 100L) {
@@ -252,11 +256,11 @@
assertTrue(isRunning)
assertEquals(300f, targetValue)
assertEquals(
- anim2.getValue((playTimeMillis2 - 100)),
+ anim2.getValueFromMillis((playTimeMillis2 - 100)),
value
)
assertEquals(
- anim2.getVelocity((playTimeMillis2 - 100)),
+ anim2.getVelocityFromMillis((playTimeMillis2 - 100)),
velocity
)
playTimeMillis2 += interval
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt
index 614cc8a..3321867 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt
@@ -41,30 +41,66 @@
val start1D = AnimationVector(0f)
val end1D = AnimationVector(1f)
- assertEquals(start1D, snap1D.getValue(0L, start1D, end1D, start1D))
- assertEquals(start1D, snap1D.getValue(snap1D.delayMillis - 1L, start1D, end1D, start1D))
- assertEquals(end1D, snap1D.getValue(snap1D.delayMillis.toLong(), start1D, end1D, start1D))
- assertEquals(end1D, snap1D.getValue(snap1D.delayMillis + 100L, start1D, end1D, start1D))
+ assertEquals(start1D, snap1D.getValueFromMillis(0L, start1D, end1D, start1D))
+ assertEquals(
+ start1D,
+ snap1D.getValueFromMillis(snap1D.delayMillis - 1L, start1D, end1D, start1D)
+ )
+ assertEquals(
+ end1D,
+ snap1D.getValueFromMillis(snap1D.delayMillis.toLong(), start1D, end1D, start1D)
+ )
+ assertEquals(
+ end1D,
+ snap1D.getValueFromMillis(snap1D.delayMillis + 100L, start1D, end1D, start1D)
+ )
val start2D = AnimationVector(0f, 50f)
val end2D = AnimationVector(1f, -50f)
- assertEquals(start2D, snap2D.getValue(0L, start2D, end2D, start2D))
- assertEquals(start2D, snap2D.getValue(snap2D.delayMillis - 1L, start2D, end2D, start2D))
- assertEquals(end2D, snap2D.getValue(snap2D.delayMillis.toLong(), start2D, end2D, start2D))
- assertEquals(end2D, snap2D.getValue(snap2D.delayMillis + 100L, start2D, end2D, start2D))
+ assertEquals(start2D, snap2D.getValueFromMillis(0L, start2D, end2D, start2D))
+ assertEquals(
+ start2D,
+ snap2D.getValueFromMillis(snap2D.delayMillis - 1L, start2D, end2D, start2D)
+ )
+ assertEquals(
+ end2D,
+ snap2D.getValueFromMillis(snap2D.delayMillis.toLong(), start2D, end2D, start2D)
+ )
+ assertEquals(
+ end2D,
+ snap2D.getValueFromMillis(snap2D.delayMillis + 100L, start2D, end2D, start2D)
+ )
val start3D = AnimationVector(0f, 20f, -100f)
val end3D = AnimationVector(-40f, 0f, 200f)
- assertEquals(start3D, snap3D.getValue(snap3D.delayMillis - 1L, start3D, end3D, start3D))
- assertEquals(end3D, snap3D.getValue(snap3D.delayMillis.toLong(), start3D, end3D, start3D))
- assertEquals(end3D, snap3D.getValue(snap3D.delayMillis + 100L, start3D, end3D, start3D))
+ assertEquals(
+ start3D,
+ snap3D.getValueFromMillis(snap3D.delayMillis - 1L, start3D, end3D, start3D)
+ )
+ assertEquals(
+ end3D,
+ snap3D.getValueFromMillis(snap3D.delayMillis.toLong(), start3D, end3D, start3D)
+ )
+ assertEquals(
+ end3D,
+ snap3D.getValueFromMillis(snap3D.delayMillis + 100L, start3D, end3D, start3D)
+ )
val start4D = AnimationVector(48f, 26f, 88f, 177f)
val end4D = AnimationVector(64f, 286f, -999f, 40f)
- assertEquals(start4D, snap4D.getValue(0L, start4D, end4D, start4D))
- assertEquals(start4D, snap4D.getValue(snap4D.delayMillis - 1L, start4D, end4D, start4D))
- assertEquals(end4D, snap4D.getValue(snap4D.delayMillis.toLong(), start4D, end4D, start4D))
- assertEquals(end4D, snap4D.getValue(snap4D.delayMillis + 100L, start4D, end4D, start4D))
+ assertEquals(start4D, snap4D.getValueFromMillis(0L, start4D, end4D, start4D))
+ assertEquals(
+ start4D,
+ snap4D.getValueFromMillis(snap4D.delayMillis - 1L, start4D, end4D, start4D)
+ )
+ assertEquals(
+ end4D,
+ snap4D.getValueFromMillis(snap4D.delayMillis.toLong(), start4D, end4D, start4D)
+ )
+ assertEquals(
+ end4D,
+ snap4D.getValueFromMillis(snap4D.delayMillis + 100L, start4D, end4D, start4D)
+ )
}
@Test
@@ -81,10 +117,13 @@
delay
)
- assertEquals(startValue, keyframes.getValue(0L, startValue, endValue, startValue))
assertEquals(
startValue,
- keyframes.getValue(delay.toLong(), startValue, endValue, startValue)
+ keyframes.getValueFromMillis(0L, startValue, endValue, startValue)
+ )
+ assertEquals(
+ startValue,
+ keyframes.getValueFromMillis(delay.toLong(), startValue, endValue, startValue)
)
for (i in 0..200 step 50) {
val fraction: Float
@@ -100,7 +139,7 @@
)
assertEquals(
animValue,
- keyframes.getValue(
+ keyframes.getValueFromMillis(
delay + i.toLong(),
startValue, endValue, startValue
)
@@ -108,7 +147,10 @@
}
// Test playtime > duration + delay
- assertEquals(endValue, keyframes.getValue(500L, startValue, endValue, startValue))
+ assertEquals(
+ endValue,
+ keyframes.getValueFromMillis(500L, startValue, endValue, startValue)
+ )
}
@Test
@@ -122,11 +164,14 @@
// 1D vector
val start1D = AnimationVector(0f)
val end1D = AnimationVector(1f)
- assertEquals(start1D, tween1D.getValue(0L, start1D, end1D, start1D))
- assertEquals(start1D, tween1D.getValue(tween1D.delayMillis - 1L, start1D, end1D, start1D))
+ assertEquals(start1D, tween1D.getValueFromMillis(0L, start1D, end1D, start1D))
assertEquals(
start1D,
- tween1D.getValue(tween1D.delayMillis.toLong(), start1D, end1D, start1D)
+ tween1D.getValueFromMillis(tween1D.delayMillis - 1L, start1D, end1D, start1D)
+ )
+ assertEquals(
+ start1D,
+ tween1D.getValueFromMillis(tween1D.delayMillis.toLong(), start1D, end1D, start1D)
)
val animValue1D = AnimationVector(
lerp(
@@ -136,17 +181,20 @@
)
assertEquals(
animValue1D,
- tween1D.getValue(tween1D.delayMillis + 100L, start1D, end1D, start1D)
+ tween1D.getValueFromMillis(tween1D.delayMillis + 100L, start1D, end1D, start1D)
)
// 2D vector
val start2D = AnimationVector(0f, 50f)
val end2D = AnimationVector(1f, -50f)
- assertEquals(start2D, tween2D.getValue(0L, start2D, end2D, start2D))
- assertEquals(start2D, tween2D.getValue(tween2D.delayMillis - 1L, start2D, end2D, start2D))
+ assertEquals(start2D, tween2D.getValueFromMillis(0L, start2D, end2D, start2D))
assertEquals(
start2D,
- tween2D.getValue(tween2D.delayMillis.toLong(), start2D, end2D, start2D)
+ tween2D.getValueFromMillis(tween2D.delayMillis - 1L, start2D, end2D, start2D)
+ )
+ assertEquals(
+ start2D,
+ tween2D.getValueFromMillis(tween2D.delayMillis.toLong(), start2D, end2D, start2D)
)
val animValue2D = AnimationVector(
lerp(start2D.v1, end2D.v1, 100f / tween2D.durationMillis),
@@ -154,17 +202,20 @@
)
assertEquals(
animValue2D,
- tween2D.getValue(tween2D.delayMillis + 100L, start2D, end2D, start2D)
+ tween2D.getValueFromMillis(tween2D.delayMillis + 100L, start2D, end2D, start2D)
)
// 3D Vector
val start3D = AnimationVector(0f, 20f, -100f)
val end3D = AnimationVector(-40f, 0f, 200f)
- assertEquals(start3D, tween3D.getValue(0L, start3D, end3D, start3D))
- assertEquals(start3D, tween3D.getValue(tween3D.delayMillis - 1L, start3D, end3D, start3D))
+ assertEquals(start3D, tween3D.getValueFromMillis(0L, start3D, end3D, start3D))
assertEquals(
start3D,
- tween3D.getValue(tween3D.delayMillis.toLong(), start3D, end3D, start3D)
+ tween3D.getValueFromMillis(tween3D.delayMillis - 1L, start3D, end3D, start3D)
+ )
+ assertEquals(
+ start3D,
+ tween3D.getValueFromMillis(tween3D.delayMillis.toLong(), start3D, end3D, start3D)
)
val animValue3D = AnimationVector(
lerp(
@@ -185,17 +236,20 @@
)
assertEquals(
animValue3D,
- tween3D.getValue(tween3D.delayMillis + 100L, start3D, end3D, start3D)
+ tween3D.getValueFromMillis(tween3D.delayMillis + 100L, start3D, end3D, start3D)
)
// 4D Vector
val start4D = AnimationVector(48f, 26f, 88f, 177f)
val end4D = AnimationVector(64f, 286f, -999f, 40f)
- assertEquals(start4D, tween4D.getValue(0L, start4D, end4D, start4D))
- assertEquals(start4D, tween4D.getValue(tween4D.delayMillis - 1L, start4D, end4D, start4D))
+ assertEquals(start4D, tween4D.getValueFromMillis(0L, start4D, end4D, start4D))
assertEquals(
start4D,
- tween4D.getValue(tween4D.delayMillis.toLong(), start4D, end4D, start4D)
+ tween4D.getValueFromMillis(tween4D.delayMillis - 1L, start4D, end4D, start4D)
+ )
+ assertEquals(
+ start4D,
+ tween4D.getValueFromMillis(tween4D.delayMillis.toLong(), start4D, end4D, start4D)
)
val animValue4D = AnimationVector(
lerp(
@@ -217,7 +271,7 @@
)
assertEquals(
animValue4D,
- tween4D.getValue(tween4D.delayMillis + 100L, start4D, end4D, start4D)
+ tween4D.getValueFromMillis(tween4D.delayMillis + 100L, start4D, end4D, start4D)
)
}
@@ -248,11 +302,11 @@
for (i in 0..duration step 100) {
assertEquals(
AnimationVector(
- floatAnim.getValue(i, start.v1, end.v1, startVelocity.v1),
- floatAnim.getValue(i, start.v2, end.v2, startVelocity.v2),
- floatAnim.getValue(i, start.v3, end.v3, startVelocity.v3)
+ floatAnim.getValueFromMillis(i, start.v1, end.v1, startVelocity.v1),
+ floatAnim.getValueFromMillis(i, start.v2, end.v2, startVelocity.v2),
+ floatAnim.getValueFromMillis(i, start.v3, end.v3, startVelocity.v3)
),
- anim3D.getValue(i, start, end, startVelocity)
+ anim3D.getValueFromMillis(i, start, end, startVelocity)
)
}
}
@@ -304,13 +358,13 @@
)
for (playtime in 0..fixedAnim.durationMillis step 100) {
assertEquals(
- anim.getValue(playtime, start, end, startVelocity),
- fixedAnim.getValue(playtime)
+ anim.getValueFromMillis(playtime, start, end, startVelocity),
+ fixedAnim.getValueFromMillis(playtime)
)
assertEquals(
- anim.getVelocity(playtime, start, end, startVelocity),
- fixedAnim.getVelocity(playtime)
+ anim.getVelocityFromNanos(playtime * MillisToNanos, start, end, startVelocity),
+ fixedAnim.getVelocityFromMillis(playtime)
)
}
assertEquals(
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt
index 93d1917..fcb63dc 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt
@@ -17,7 +17,7 @@
package androidx.compose.animation.core
internal fun VectorizedAnimationSpec<AnimationVector1D>.at(time: Long): Float =
- getValue(
+ getValueFromMillis(
time,
AnimationVector1D(0f),
AnimationVector1D(1f),
@@ -31,7 +31,7 @@
start: Number,
end: Number,
startVelocity: Number
-) = getValue(
+) = getValueFromMillis(
playTime,
AnimationVector1D(start.toFloat()),
AnimationVector1D(end.toFloat()),
@@ -43,9 +43,79 @@
start: Number,
end: Number,
startVelocity: Number
-) = getVelocity(
- playTime,
+) = getVelocityFromNanos(
+ playTime * MillisToNanos,
AnimationVector1D(start.toFloat()),
AnimationVector1D(end.toFloat()),
AnimationVector1D(startVelocity.toFloat())
).value
+
+/**
+ * Returns the value of the animation at the given play time.
+ *
+ * @param playTimeMillis the play time that is used to determine the value of the animation.
+ */
+internal fun <T> Animation<T, *>.getValueFromMillis(playTimeMillis: Long): T =
+ getValueFromNanos(playTimeMillis * MillisToNanos)
+
+/**
+ * Returns the velocity (in [AnimationVector] form) of the animation at the given play time.
+ *
+ * @param playTimeMillis the play time that is used to calculate the velocity of the animation.
+ */
+internal fun <V : AnimationVector> Animation<*, V>.getVelocityVectorFromMillis(
+ playTimeMillis: Long
+): V = getVelocityVectorFromNanos(playTimeMillis * MillisToNanos)
+
+/**
+ * Returns whether the animation is finished at the given play time.
+ *
+ * @param playTimeMillis the play time used to determine whether the animation is finished.
+ */
+internal fun Animation<*, *>.isFinishedFromMillis(playTimeMillis: Long): Boolean {
+ return playTimeMillis >= durationMillis
+}
+
+internal fun <T, V : AnimationVector> Animation<T, V>.getVelocityFromMillis(
+ playTimeMillis: Long
+): T = typeConverter.convertFromVector(getVelocityVectorFromMillis(playTimeMillis))
+
+internal fun FloatAnimationSpec.getDurationMillis(
+ start: Float,
+ end: Float,
+ startVelocity: Float
+): Long = getDurationNanos(start, end, startVelocity) / MillisToNanos
+
+/**
+ * Calculates the value of the animation at given the playtime, with the provided start/end
+ * values, and start velocity.
+ *
+ * @param playTimeMillis time since the start of the animation
+ * @param start start value of the animation
+ * @param end end value of the animation
+ * @param startVelocity start velocity of the animation
+ */
+// TODO: bring all tests on to `getValueFromNanos`
+internal fun FloatAnimationSpec.getValueFromMillis(
+ playTimeMillis: Long,
+ start: Float,
+ end: Float,
+ startVelocity: Float
+): Float = getValueFromNanos(playTimeMillis * MillisToNanos, start, end, startVelocity)
+
+/**
+ * Calculates the velocity of the animation at given the playtime, with the provided start/end
+ * values, and start velocity.
+ *
+ * @param playTimeMillis time since the start of the animation
+ * @param start start value of the animation
+ * @param end end value of the animation
+ * @param startVelocity start velocity of the animation
+ */
+// TODO: bring all tests on to `getVelocityFromNanos`
+internal fun FloatAnimationSpec.getVelocityFromMillis(
+ playTimeMillis: Long,
+ start: Float,
+ end: Float,
+ startVelocity: Float
+): Float = getVelocityFromNanos(playTimeMillis * MillisToNanos, start, end, startVelocity)
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt
index 07af20a..93c9d99 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt
@@ -35,29 +35,34 @@
val animWrapper = anim.createAnimation(startValue, startVelocity)
// Obtain finish value by passing in an absurdly large playtime.
- val finishValue = animWrapper.getValue(Int.MAX_VALUE.toLong())
- val finishTime = animWrapper.durationMillis
+ val finishValue = animWrapper.getValueFromNanos(Int.MAX_VALUE.toLong())
+ val finishTimeNanos = animWrapper.durationMillis * MillisToNanos
- for (playTime in 0L..4000L step 200L) {
- val value = anim.getValue(playTime, startValue, startVelocity)
- val velocity = anim.getVelocity(playTime, startValue, startVelocity)
- val finished = playTime >= finishTime
- assertTrue(finished == animWrapper.isFinished(playTime))
+ for (playTimeMillis in 0L..4000L step 200L) {
+ val playTimeNanos = playTimeMillis * MillisToNanos
+ val value = anim.getValueFromNanos(playTimeNanos, startValue, startVelocity)
+ val velocity = anim.getVelocityFromNanos(playTimeNanos, startValue, startVelocity)
+ val finished = playTimeNanos >= finishTimeNanos
+ assertTrue(finished == animWrapper.isFinishedFromNanos(playTimeNanos))
if (!finished) {
// Before the animation finishes, absolute velocity is above the threshold
assertTrue(Math.abs(velocity) >= 2.0f)
- assertEquals(value, animWrapper.getValue(playTime), epsilon)
- assertEquals(velocity, animWrapper.getVelocityVector(playTime).value, epsilon)
- assertTrue(playTime < finishTime)
+ assertEquals(value, animWrapper.getValueFromNanos(playTimeNanos), epsilon)
+ assertEquals(
+ velocity,
+ animWrapper.getVelocityVectorFromNanos(playTimeNanos).value,
+ epsilon
+ )
+ assertTrue(playTimeNanos < finishTimeNanos)
} else {
// When the animation is finished, expect absolute velocity < threshold
assertTrue(Math.abs(velocity) < 2.0f)
// Once the animation is finished, the value should not change any more
- assertEquals(finishValue, animWrapper.getValue(playTime), epsilon)
+ assertEquals(finishValue, animWrapper.getValueFromNanos(playTimeNanos), epsilon)
- assertTrue(playTime >= finishTime)
+ assertTrue(playTimeNanos >= finishTimeNanos)
}
}
}
@@ -80,20 +85,20 @@
startVelocity
)
- val finishValue = fullAnim.getValue(Int.MAX_VALUE.toLong())
+ val finishValue = fullAnim.getValueFromNanos(Int.MAX_VALUE.toLong())
val finishValue1 = anim1.createAnimation(startValue, startVelocity)
- .getValue(Int.MAX_VALUE.toLong())
+ .getValueFromNanos(Int.MAX_VALUE.toLong())
val finishVelocity1 = anim1.createAnimation(startValue, startVelocity)
- .getVelocityVector(Int.MAX_VALUE.toLong()).value
+ .getVelocityVectorFromNanos(Int.MAX_VALUE.toLong()).value
// Verify that the finish velocity is at the threshold
assertEquals(threshold, finishVelocity1, epsilon)
// Feed in the finish value from anim1 to anim2
val finishValue2 = anim2.createAnimation(finishValue1, finishVelocity1)
- .getValue(Int.MAX_VALUE.toLong())
+ .getValueFromNanos(Int.MAX_VALUE.toLong())
assertEquals(finishValue, finishValue2, 2f)
}
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt
index 67d848c..afbdcf3 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt
@@ -131,7 +131,7 @@
}
assertEquals(
AnimationVector(v1, v2),
- animation.getValue(
+ animation.getValueFromMillis(
time.toLong(), start, end,
AnimationVector(0f, 0f)
)
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt
index 1ad5905..0f93629 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt
@@ -33,7 +33,7 @@
val end = 500f
val playTime = 150L
- val velocity = animation.getVelocity(playTime, start, end, 0f)
+ val velocity = animation.getVelocityFromMillis(playTime, start, end, 0f)
val expectedVelocity = animation.toSpring(end).updateValues(start, 0f, playTime).velocity
assertThat(velocity).isEqualTo(expectedVelocity)
}
@@ -48,7 +48,7 @@
spring(), Int.VectorConverter, start, end, 0
)
- val velocity = animation.getVelocity(playTime)
+ val velocity = animation.getVelocityFromMillis(playTime)
val expectedVelocity = FloatSpringSpec().toSpring(end)
.updateValues(start.toFloat(), 0f, playTime).velocity.toInt()
@@ -67,8 +67,8 @@
spring(), Float.VectorConverter, start1, end1, 0f
)
- val interruptionValue = animation.getValue(interruptionTime)
- val interruptionVelocity = animation.getVelocity(interruptionTime)
+ val interruptionValue = animation.getValueFromMillis(interruptionTime)
+ val interruptionVelocity = animation.getVelocityFromMillis(interruptionTime)
// second animation will go from interruptionValue to interruptionValue with
// applying the velocity from the first interrupted animation.
@@ -81,8 +81,8 @@
)
// let's verify values after 15 ms of the second animation
val playTime = 15L
- val resultValue = animation2.getValue(playTime)
- val resultVelocity = animation2.getVelocity(playTime)
+ val resultValue = animation2.getValueFromMillis(playTime)
+ val resultVelocity = animation2.getVelocityFromMillis(playTime)
val motion = FloatSpringSpec().toSpring(end2).updateValues(
start2,
@@ -204,8 +204,8 @@
100f,
0f
).also { animation ->
- assertEquals(0f, animation.getVelocityVector(animation.durationMillis).value)
- assertEquals(100f, animation.getValue(animation.durationMillis))
+ assertEquals(0f, animation.getVelocityVectorFromMillis(animation.durationMillis).value)
+ assertEquals(100f, animation.getValueFromMillis(animation.durationMillis))
}
}
@@ -268,25 +268,25 @@
)
for (time in 0L until duration) {
- val springVector = springVectorAnimation.getValue(
+ val springVector = springVectorAnimation.getValueFromMillis(
time,
AnimationVector(100f, 100f, 100f),
AnimationVector(0f, 0f, 0f),
AnimationVector(0f, 0f, 0f)
)
- val float1 = floatAnimation1.getValue(
+ val float1 = floatAnimation1.getValueFromMillis(
time,
AnimationVector(100f),
AnimationVector(0f),
AnimationVector(0f)
)
- val float2 = floatAnimation2.getValue(
+ val float2 = floatAnimation2.getValueFromMillis(
time,
AnimationVector(100f),
AnimationVector(0f),
AnimationVector(0f)
)
- val float3 = floatAnimation3.getValue(
+ val float3 = floatAnimation3.getValueFromMillis(
time,
AnimationVector(100f),
AnimationVector(0f),
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
index b6873c4..30ccaf0 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
@@ -51,8 +51,8 @@
assertThat(repeat.at(Duration + 1)).isLessThan(0.1f)
assertThat(repeat.at(Duration * 2 - 1)).isGreaterThan(0.9f)
assertThat(repeat.at(Duration * 2)).isEqualTo(1f)
- assertThat(animationWrapper.isFinished(Duration * 2L - 1L)).isFalse()
- assertThat(animationWrapper.isFinished(Duration * 2L)).isTrue()
+ assertThat(animationWrapper.isFinishedFromMillis(Duration * 2L - 1L)).isFalse()
+ assertThat(animationWrapper.isFinishedFromMillis(Duration * 2L)).isTrue()
}
@Test
@@ -90,18 +90,18 @@
)
for (playtime in 0..100L) {
- assertEquals(playtime.toFloat(), repeatAnim.getValue(playtime), 0.01f)
+ assertEquals(playtime.toFloat(), repeatAnim.getValueFromMillis(playtime), 0.01f)
}
for (playtime in 100..200L) {
- assertEquals(200f - playtime.toFloat(), repeatAnim.getValue(playtime), 0.01f)
+ assertEquals(200f - playtime.toFloat(), repeatAnim.getValueFromMillis(playtime), 0.01f)
}
- assertEquals(100f, repeatAnim.getValue(100))
- assertEquals(99f, repeatAnim.getValue(101))
- assertEquals(0f, repeatAnim.getValue(200))
- assertEquals(100f, repeatAnim.getValue(300))
- assertEquals(80f, repeatAnim.getValue(880))
- assertEquals(100f, repeatAnim.getValue(900))
- assertEquals(100f, repeatAnim.getValue(901))
+ assertEquals(100f, repeatAnim.getValueFromMillis(100))
+ assertEquals(99f, repeatAnim.getValueFromMillis(101))
+ assertEquals(0f, repeatAnim.getValueFromMillis(200))
+ assertEquals(100f, repeatAnim.getValueFromMillis(300))
+ assertEquals(80f, repeatAnim.getValueFromMillis(880))
+ assertEquals(100f, repeatAnim.getValueFromMillis(900))
+ assertEquals(100f, repeatAnim.getValueFromMillis(901))
}
@Test
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt
index eb2f71d..8c1102e 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt
@@ -35,6 +35,6 @@
)
assertThat(animation.at(0)).isEqualTo(1f)
- assertThat(animationWrapper.isFinished(0)).isTrue()
+ assertThat(animationWrapper.isFinishedFromMillis(0)).isTrue()
}
}
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt
index a734bd1..72c6567 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt
@@ -51,8 +51,8 @@
0f, 1f, 0f,
spring(dampingRatio = Spring.DampingRatioMediumBouncy)
) { value, velocity ->
- assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
- assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+ assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+ assertEquals(anim.getVelocityFromMillis(playTimeMillis), velocity, 0.001f)
playTimeMillis += interval
}
}
@@ -84,7 +84,7 @@
from, to,
animationSpec = tween(500)
) { value, _ ->
- val expectedValue = anim.getValue(playTimeMillis)
+ val expectedValue = anim.getValueFromMillis(playTimeMillis)
assertEquals(expectedValue.x, value.x, 0.001f)
assertEquals(expectedValue.y, value.y, 0.001f)
playTimeMillis += interval
@@ -113,8 +113,8 @@
from, velocity,
animationSpec = FloatExponentialDecaySpec()
) { value, velocity ->
- assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
- assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+ assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+ assertEquals(anim.getVelocityFromMillis(playTimeMillis), velocity, 0.001f)
playTimeMillis += interval
}
}
@@ -154,7 +154,7 @@
) {
assertTrue(animationState.isRunning)
assertTrue(isRunning)
- val expectedValue = anim.getValue(playTimeMillis)
+ val expectedValue = anim.getValueFromMillis(playTimeMillis)
assertEquals(expectedValue.x, value.x, 0.001f)
assertEquals(expectedValue.y, value.y, 0.001f)
if (playTimeMillis == 0L) {
@@ -201,8 +201,8 @@
state.animateDecay(
FloatExponentialDecaySpec().generateDecayAnimationSpec()
) {
- assertEquals(anim.getValue(playTimeMillis), value, 0.001f)
- assertEquals(anim.getVelocity(playTimeMillis), velocity, 0.001f)
+ assertEquals(anim.getValueFromMillis(playTimeMillis), value, 0.001f)
+ assertEquals(anim.getVelocityFromMillis(playTimeMillis), velocity, 0.001f)
playTimeMillis += interval
assertEquals(value, state.value, 0.0001f)
assertEquals(velocity, state.velocity, 0.0001f)
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt
index ff7416e..a1666cb 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt
@@ -38,7 +38,7 @@
)
fun atPlaytime(playTime: Long) =
- animation.getValue(playTime, start, end, AnimationVector1D(0f)).value
+ animation.getValueFromMillis(playTime, start, end, AnimationVector1D(0f)).value
assertThat(atPlaytime(0L)).isZero()
assertThat(atPlaytime(testDelay / 2)).isZero()
diff --git a/compose/animation/animation/api/current.txt b/compose/animation/animation/api/current.txt
index 83672d1..8750af5 100644
--- a/compose/animation/animation/api/current.txt
+++ b/compose/animation/animation/api/current.txt
@@ -36,7 +36,7 @@
}
public final class CrossfadeKt {
- method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? current, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animation, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
}
public final class DisposableAnimationClock implements androidx.compose.animation.core.AnimationClockObservable {
diff --git a/compose/animation/animation/api/public_plus_experimental_current.txt b/compose/animation/animation/api/public_plus_experimental_current.txt
index 83672d1..8750af5 100644
--- a/compose/animation/animation/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation/api/public_plus_experimental_current.txt
@@ -36,7 +36,7 @@
}
public final class CrossfadeKt {
- method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? current, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animation, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
}
public final class DisposableAnimationClock implements androidx.compose.animation.core.AnimationClockObservable {
diff --git a/compose/animation/animation/api/restricted_current.txt b/compose/animation/animation/api/restricted_current.txt
index 83672d1..8750af5 100644
--- a/compose/animation/animation/api/restricted_current.txt
+++ b/compose/animation/animation/api/restricted_current.txt
@@ -36,7 +36,7 @@
}
public final class CrossfadeKt {
- method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? current, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animation, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static <T> void Crossfade(T? targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
}
public final class DisposableAnimationClock implements androidx.compose.animation.core.AnimationClockObservable {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt
index a1547ec..5133519 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt
@@ -57,7 +57,7 @@
}
}
val saveableStateHolder = rememberSaveableStateHolder()
- Crossfade(current = current) { current ->
+ Crossfade(targetState = current) { current ->
saveableStateHolder.SaveableStateProvider(current) {
val tab = tabs[current]
arrayOf<Any?>()
diff --git a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
index 2455d96..abf7f63 100644
--- a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
@@ -24,7 +24,7 @@
@Sampled
@Composable
fun CrossfadeSample() {
- Crossfade(current = "A") { screen ->
+ Crossfade(targetState = "A") { screen ->
when (screen) {
"A" -> Text("Page A")
"B" -> Text("Page B")
diff --git a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
index daf5be0..a73246b 100644
--- a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
+++ b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
@@ -97,7 +97,7 @@
rule.setContent {
Crossfade(
showFirst,
- animation = TweenSpec(durationMillis = duration)
+ animationSpec = TweenSpec(durationMillis = duration)
) {
BasicText(if (it) First else Second)
DisposableEffect(Unit) {
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
index ec97c0c..298142e 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
@@ -14,20 +14,19 @@
* limitations under the License.
*/
-@file:Suppress("DEPRECATION")
-
package androidx.compose.animation
-import androidx.compose.animation.core.AnimatedFloat
-import androidx.compose.animation.core.AnimationEndReason
import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.RecomposeScope
-import androidx.compose.runtime.currentRecomposeScope
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
-import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@@ -38,92 +37,57 @@
*
* @sample androidx.compose.animation.samples.CrossfadeSample
*
- * @param current is a key representing your current layout state. every time you change a key
+ * @param targetState is a key representing your target layout state. Every time you change a key
* the animation will be triggered. The [content] called with the old key will be faded out while
* the [content] called with the new key will be faded in.
* @param modifier Modifier to be applied to the animation container.
- * @param animation the [AnimationSpec] to configure the animation.
+ * @param animationSpec the [AnimationSpec] to configure the animation.
*/
@Composable
fun <T> Crossfade(
- current: T,
+ targetState: T,
modifier: Modifier = Modifier,
- animation: AnimationSpec<Float> = tween(),
+ animationSpec: FiniteAnimationSpec<Float> = tween(),
content: @Composable (T) -> Unit
) {
- val state = remember { CrossfadeState<T>() }
- if (current != state.current) {
- state.current = current
- val keys = state.items.map { it.key }.toMutableList()
- if (!keys.contains(current)) {
- keys.add(current)
+ val items = remember { mutableStateListOf<CrossfadeAnimationItem<T>>() }
+ val transitionState = remember { MutableTransitionState(targetState) }
+ val targetChanged = (targetState != transitionState.targetState)
+ transitionState.targetState = targetState
+ val transition = updateTransition(transitionState)
+ if (targetChanged || items.isEmpty()) {
+ // Only manipulate the list when the state is changed, or in the first run.
+ val keys = items.map { it.key }.run {
+ if (!contains(targetState)) {
+ toMutableList().also { it.add(targetState) }
+ } else {
+ this
+ }
}
- state.items.clear()
- keys.mapTo(state.items) { key ->
- CrossfadeAnimationItem(key) { children ->
- val alpha = animatedAlpha(
- animation = animation,
- visible = key == current,
- onAnimationFinish = {
- if (key == state.current) {
- // leave only the current in the list
- state.items.removeAll { it.key != state.current }
- state.scope?.invalidate()
- }
+ items.clear()
+ keys.mapTo(items) { key ->
+ CrossfadeAnimationItem(key) {
+ key(key) {
+ val alpha by transition.animateFloat(
+ transitionSpec = { animationSpec }
+ ) { if (it == key) 1f else 0f }
+ Box(Modifier.alpha(alpha = alpha)) {
+ content(key)
}
- )
- Box(Modifier.alpha(alpha.value)) {
- children()
}
}
}
+ } else if (transitionState.currentState == transitionState.targetState) {
+ // Remove all the intermediate items from the list once the animation is finished.
+ items.removeAll { it.key != transitionState.targetState }
}
- Box(modifier) {
- state.scope = currentRecomposeScope
- state.items.fastForEach { (item, alpha) ->
- key(item) {
- alpha {
- content(item)
- }
- }
- }
- }
-}
-private class CrossfadeState<T> {
- // we use Any here as something which will not be equals to the real initial value
- var current: Any? = Any()
- var items = mutableListOf<CrossfadeAnimationItem<T>>()
- var scope: RecomposeScope? = null
+ Box(modifier) {
+ items.fastForEach { it.content() }
+ }
}
private data class CrossfadeAnimationItem<T>(
val key: T,
- val transition: CrossfadeTransition
+ val content: @Composable () -> Unit
)
-
-private typealias CrossfadeTransition = @Composable (content: @Composable () -> Unit) -> Unit
-
-@Composable
-private fun animatedAlpha(
- animation: AnimationSpec<Float>,
- visible: Boolean,
- onAnimationFinish: () -> Unit = {}
-): AnimatedFloat {
- val animatedFloat = animatedFloat(if (!visible) 1f else 0f)
- DisposableEffect(visible) {
- animatedFloat.animateTo(
- if (visible) 1f else 0f,
- anim = animation,
- onEnd = { reason, _ ->
- if (reason == AnimationEndReason.TargetReached) {
- onAnimationFinish()
- }
- }
- )
- onDispose {
- animatedFloat.stop()
- }
- }
- return animatedFloat
-}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
index 6dcaf71..b022954 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
@@ -220,13 +220,16 @@
) {
"${it.groupValues[1]}<>"
}
- // composableLambdaInstance(<>, true)
+ // composableLambdaInstance(<>, true, )
.replace(
Regex(
- "(composableLambdaInstance\\()([-\\d]+)"
+ "(composableLambdaInstance\\()([-\\d]+, (true|false), (null|\"(.*)\")\\))"
)
) {
- "${it.groupValues[1]}<>"
+ val callStart = it.groupValues[1]
+ val tracked = it.groupValues[3]
+ val sourceInfo = it.groupValues[5]
+ "$callStart<>, $tracked, \"${generateSourceInfo(sourceInfo, source)}\")"
}
// composableLambda(%composer, <>, true)
.replace(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
index 90d06fb..834c976 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
@@ -753,7 +753,13 @@
@Composable
fun C(items: List<String>, %composer: Composer?, %changed: Int) {
%composer.startRestartGroup(<>, "C(C)<X(item...>:Test.kt")
- X(items, composableLambda(%composer, <>, true, "C<A(item...>,<A(Wrap...>:Test.kt") { item: String, %composer: Composer?, %changed: Int ->
+ X(items, ComposableSingletons%TestKt.lambda-1, %composer, 0b1000)
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ C(items, %composer, %changed or 0b0001)
+ }
+ }
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function3<String, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A(item...>,<A(Wrap...>:Test.kt") { item: String, %composer: Composer?, %changed: Int ->
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(item)) 0b0100 else 0b0010
@@ -764,9 +770,6 @@
} else {
%composer.skipToGroupEnd()
}
- }, %composer, 0b00111000)
- %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
- C(items, %composer, %changed or 0b0001)
}
}
"""
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
index 73b71ef..4591ad7 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
@@ -42,11 +42,27 @@
// and one for the content lambda passed into Foo. Importantly, there is no lambda for
// the content lambda's restart group because we are using the lambda itself.
"""
+ public final class ComposableSingletons%TestKt {
+ public final static LComposableSingletons%TestKt; INSTANCE
+ public <init>()V
+ public static Lkotlin/jvm/functions/Function2; lambda-1
+ public final getLambda-1%test_module()Lkotlin/jvm/functions/Function2;
+ final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+ static <clinit>()V
+ }
+ final class ComposableSingletons%TestKt%lambda-1%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>()V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public final static LComposableSingletons%TestKt%lambda-1%1; INSTANCE
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ static <clinit>()V
+ final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+ OUTERCLASS ComposableSingletons%TestKt <clinit> ()V
+ }
public final class TestKt {
final static INNERCLASS TestKt%Foo%1 null null
public final static Foo(Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
final static INNERCLASS TestKt%Bar%1 null null
- final static INNERCLASS TestKt%Bar%2 null null
public final static Bar(Landroidx/compose/runtime/Composer;I)V
}
final class TestKt%Foo%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
@@ -59,20 +75,11 @@
OUTERCLASS TestKt Foo (Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
}
final class TestKt%Bar%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
- <init>()V
- public final invoke(Landroidx/compose/runtime/Composer;I)V
- public final static LTestKt%Bar%1; INSTANCE
- public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
- static <clinit>()V
- final static INNERCLASS TestKt%Bar%1 null null
- OUTERCLASS TestKt Bar (Landroidx/compose/runtime/Composer;I)V
- }
- final class TestKt%Bar%2 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
<init>(I)V
public final invoke(Landroidx/compose/runtime/Composer;I)V
final synthetic I %%changed
public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
- final static INNERCLASS TestKt%Bar%2 null null
+ final static INNERCLASS TestKt%Bar%1 null null
OUTERCLASS TestKt Bar (Landroidx/compose/runtime/Composer;I)V
}
"""
@@ -636,12 +643,28 @@
}
""",
"""
+ public final class ComposableSingletons%TestKt {
+ public final static LComposableSingletons%TestKt; INSTANCE
+ public <init>()V
+ public static Lkotlin/jvm/functions/Function3; lambda-1
+ public final getLambda-1%test_module()Lkotlin/jvm/functions/Function3;
+ final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+ static <clinit>()V
+ }
+ final class ComposableSingletons%TestKt%lambda-1%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function3 {
+ <init>()V
+ public final invoke(ILandroidx/compose/runtime/Composer;I)V
+ public final static LComposableSingletons%TestKt%lambda-1%1; INSTANCE
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ static <clinit>()V
+ final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+ OUTERCLASS ComposableSingletons%TestKt <clinit> ()V
+ }
public final class TestKt {
private final static Lkotlin/jvm/functions/Function3; foo
public final static getFoo()Lkotlin/jvm/functions/Function3;
final static INNERCLASS TestKt%Bar%1 null null
public final static Bar(Landroidx/compose/runtime/Composer;I)V
- final static INNERCLASS TestKt%foo%1 null null
static <clinit>()V
}
final class TestKt%Bar%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
@@ -652,15 +675,6 @@
final static INNERCLASS TestKt%Bar%1 null null
OUTERCLASS TestKt Bar (Landroidx/compose/runtime/Composer;I)V
}
- final class TestKt%foo%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function3 {
- <init>()V
- public final invoke(ILandroidx/compose/runtime/Composer;I)V
- public final static LTestKt%foo%1; INSTANCE
- public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
- static <clinit>()V
- final static INNERCLASS TestKt%foo%1 null null
- OUTERCLASS TestKt <clinit> ()V
- }
"""
)
@@ -674,19 +688,26 @@
}
""",
"""
- public final class TestKt {
- final static INNERCLASS TestKt%Bar%foo%1 null null
- final static INNERCLASS TestKt%Bar%1 null null
- public final static Bar(Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
+ public final class ComposableSingletons%TestKt {
+ public final static LComposableSingletons%TestKt; INSTANCE
+ public <init>()V
+ public static Lkotlin/jvm/functions/Function3; lambda-1
+ public final getLambda-1%test_module()Lkotlin/jvm/functions/Function3;
+ final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+ static <clinit>()V
}
- final class TestKt%Bar%foo%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function3 {
+ final class ComposableSingletons%TestKt%lambda-1%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function3 {
<init>()V
public final invoke(ILandroidx/compose/runtime/Composer;I)V
- public final static LTestKt%Bar%foo%1; INSTANCE
+ public final static LComposableSingletons%TestKt%lambda-1%1; INSTANCE
public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
static <clinit>()V
- final static INNERCLASS TestKt%Bar%foo%1 null null
- OUTERCLASS TestKt Bar (Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
+ final static INNERCLASS ComposableSingletons%TestKt%lambda-1%1 null null
+ OUTERCLASS ComposableSingletons%TestKt <clinit> ()V
+ }
+ public final class TestKt {
+ final static INNERCLASS TestKt%Bar%1 null null
+ public final static Bar(Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
}
final class TestKt%Bar%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
<init>(Lkotlin/jvm/functions/Function2;I)V
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
index 68df589..99b592d 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
@@ -2311,13 +2311,7 @@
fun Test(%composer: Composer?, %changed: Int) {
%composer.startRestartGroup(<>, "C(Test)<W>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
- W(composableLambda(%composer, <>, true, "C<A()>:Test.kt") { %composer: Composer?, %changed: Int ->
- if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
- A(%composer, 0)
- } else {
- %composer.skipToGroupEnd()
- }
- }, %composer, 0b0110)
+ W(ComposableSingletons%TestKt.lambda-1, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
@@ -2325,6 +2319,15 @@
Test(%composer, %changed or 0b0001)
}
}
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A()>:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ A(%composer, 0)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
"""
)
@@ -2382,22 +2385,7 @@
fun Test(%composer: Composer?, %changed: Int) {
%composer.startRestartGroup(<>, "C(Test)<Wrap>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
- Wrap(composableLambda(%composer, <>, true, "C<effect>:Test.kt") { %composer: Composer?, %changed: Int ->
- if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
- %composer.startReplaceableGroup(<>, "*<effect>")
- repeat(number) { it: Int ->
- effects[it] = effect({
- 0
- }, %composer, 0)
- }
- %composer.endReplaceableGroup()
- outside = effect({
- "0"
- }, %composer, 0)
- } else {
- %composer.skipToGroupEnd()
- }
- }, %composer, 0b0110)
+ Wrap(ComposableSingletons%TestKt.lambda-1, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
@@ -2405,6 +2393,24 @@
Test(%composer, %changed or 0b0001)
}
}
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<effect>:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ %composer.startReplaceableGroup(<>, "*<effect>")
+ repeat(number) { it: Int ->
+ effects[it] = effect({
+ 0
+ }, %composer, 0)
+ }
+ %composer.endReplaceableGroup()
+ outside = effect({
+ "0"
+ }, %composer, 0)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
""",
"""
import androidx.compose.runtime.Composable
@@ -3158,27 +3164,7 @@
fun Test(%composer: Composer?, %changed: Int) {
%composer.startRestartGroup(<>, "C(Test)<W>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
- W(composableLambda(%composer, <>, true, "C<IW>:Test.kt") { %composer: Composer?, %changed: Int ->
- if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
- IW({ %composer: Composer?, %changed: Int ->
- %composer.startReplaceableGroup(<>, "C<T(2)>,<T(4)>:Test.kt")
- if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
- T(2, %composer, 0b0110)
- %composer.startReplaceableGroup(<>, "*<T(3)>")
- repeat(3) { it: Int ->
- T(3, %composer, 0b0110)
- }
- %composer.endReplaceableGroup()
- T(4, %composer, 0b0110)
- } else {
- %composer.skipToGroupEnd()
- }
- %composer.endReplaceableGroup()
- }, %composer, 0)
- } else {
- %composer.skipToGroupEnd()
- }
- }, %composer, 0b0110)
+ W(ComposableSingletons%TestKt.lambda-1, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
@@ -3186,6 +3172,29 @@
Test(%composer, %changed or 0b0001)
}
}
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<IW>:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ IW({ %composer: Composer?, %changed: Int ->
+ %composer.startReplaceableGroup(<>, "C<T(2)>,<T(4)>:Test.kt")
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ T(2, %composer, 0b0110)
+ %composer.startReplaceableGroup(<>, "*<T(3)>")
+ repeat(3) { it: Int ->
+ T(3, %composer, 0b0110)
+ }
+ %composer.endReplaceableGroup()
+ T(4, %composer, 0b0110)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endReplaceableGroup()
+ }, %composer, 0)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
"""
)
}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
index 4ec08fe..31c193f 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
@@ -88,13 +88,7 @@
fun Test(%composer: Composer?, %changed: Int) {
%composer.startRestartGroup(<>, "C(Test)")
if (%changed !== 0 || !%composer.skipping) {
- W(composableLambda(%composer, <>, true, null) { %composer: Composer?, %changed: Int ->
- if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
- A(%composer, 0)
- } else {
- %composer.skipToGroupEnd()
- }
- }, %composer, 0b0110)
+ W(ComposableSingletons%TestKt.lambda-1, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
@@ -102,6 +96,15 @@
Test(%composer, %changed or 0b0001)
}
}
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ A(%composer, 0)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
"""
)
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index fda6d65..8dcd32f0 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -693,22 +693,24 @@
} else if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(modifier)) 0b0100 else 0b0010
}
- if (%default and 0b0010 !== 0) {
- %dirty = %dirty or 0b00110000
- } else if (%changed and 0b01110000 === 0) {
- %dirty = %dirty or if (%composer.changed(content)) 0b00100000 else 0b00010000
+ if (%changed and 0b01110000 === 0) {
+ %dirty = %dirty or if (%default and 0b0010 === 0 && %composer.changed(content)) 0b00100000 else 0b00010000
}
if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
- if (%default and 0b0001 !== 0) {
- modifier = Companion
- }
- if (%default and 0b0010 !== 0) {
- content = composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
- if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
- Unit
- } else {
- %composer.skipToGroupEnd()
- }
+ if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) {
+ %composer.startDefaults()
+ if (%default and 0b0001 !== 0) {
+ modifier = Companion
+ }
+ if (%default and 0b0010 !== 0) {
+ content = ComposableSingletons%TestKt.lambda-1
+ %dirty = %dirty and 0b01110000.inv()
+ }
+ %composer.endDefaults()
+ } else {
+ %composer.skipCurrentGroup()
+ if (%default and 0b0010 !== 0) {
+ %dirty = %dirty and 0b01110000.inv()
}
}
println()
@@ -719,6 +721,15 @@
SimpleBox(modifier, content, %composer, %changed or 0b0001, %default)
}
}
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
"""
)
@@ -738,19 +749,22 @@
}
""",
"""
- val foo: Function4<Int, Foo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { x: Int, y: Foo, %composer: Composer?, %changed: Int ->
- val %dirty = %changed
- if (%changed and 0b1110 === 0) {
- %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
- }
- if (%changed and 0b01110000 === 0) {
- %dirty = %dirty or if (%composer.changed(y)) 0b00100000 else 0b00010000
- }
- if (%dirty and 0b001011011011 xor 0b10010010 !== 0 || !%composer.skipping) {
- A(x, %composer, 0b1110 and %dirty)
- B(y, %composer, 0b1110 and %dirty shr 0b0011)
- } else {
- %composer.skipToGroupEnd()
+ val foo: Function4<Int, Foo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function4<Int, Foo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A(x)>,<B(y)>:") { x: Int, y: Foo, %composer: Composer?, %changed: Int ->
+ val %dirty = %changed
+ if (%changed and 0b1110 === 0) {
+ %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
+ }
+ if (%changed and 0b01110000 === 0) {
+ %dirty = %dirty or if (%composer.changed(y)) 0b00100000 else 0b00010000
+ }
+ if (%dirty and 0b001011011011 xor 0b10010010 !== 0 || !%composer.skipping) {
+ A(x, %composer, 0b1110 and %dirty)
+ B(y, %composer, 0b1110 and %dirty shr 0b0011)
+ } else {
+ %composer.skipToGroupEnd()
+ }
}
}
"""
@@ -770,9 +784,12 @@
}
""",
"""
- val foo: Function4<Int, Foo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { x: Int, y: Foo, %composer: Composer?, %changed: Int ->
- A(x, %composer, 0b1110 and %changed)
- B(y, %composer, 0b1000)
+ val foo: Function4<Int, Foo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function4<Int, Foo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A(x)>,<B(y)>:") { x: Int, y: Foo, %composer: Composer?, %changed: Int ->
+ A(x, %composer, 0b1110 and %changed)
+ B(y, %composer, 0b1000)
+ }
}
"""
)
@@ -811,13 +828,7 @@
fun Example(%composer: Composer?, %changed: Int) {
%composer.startRestartGroup(<>, "C(Example)<SomeTh...>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
- SomeThing(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
- if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
- val id = object
- } else {
- %composer.skipToGroupEnd()
- }
- }, %composer, 0b0110)
+ SomeThing(ComposableSingletons%TestKt.lambda-1, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
@@ -825,6 +836,15 @@
Example(%composer, %changed or 0b0001)
}
}
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ val id = object
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
"""
)
@@ -1156,15 +1176,18 @@
}
""",
"""
- val test: Function3<Int, Composer, Int, Unit> = composableLambdaInstance(<>, true) { x: Int, %composer: Composer?, %changed: Int ->
- val %dirty = %changed
- if (%changed and 0b1110 === 0) {
- %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
- }
- if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
- A(x, 0, %composer, 0b1110 and %dirty, 0b0010)
- } else {
- %composer.skipToGroupEnd()
+ val test: Function3<Int, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function3<Int, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C<A(x)>:") { x: Int, %composer: Composer?, %changed: Int ->
+ val %dirty = %changed
+ if (%changed and 0b1110 === 0) {
+ %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
+ }
+ if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+ A(x, 0, %composer, 0b1110 and %dirty, 0b0010)
+ } else {
+ %composer.skipToGroupEnd()
+ }
}
}
"""
@@ -1492,13 +1515,7 @@
%composer.startRestartGroup(<>, "C(A)<D>,<C({})>,<C(stab...>,<C(16.d...>,<C(Dp(1...>,<C(16.d...>,<C(norm...>,<C(Int....>,<C(stab...>,<C(Modi...>,<C(Foo....>,<C(cons...>,<C(123)>,<C(123>,<C(x)>,<C(x>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
val x = 123
- D(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
- if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
- Unit
- } else {
- %composer.skipToGroupEnd()
- }
- }, %composer, 0b0110)
+ D(ComposableSingletons%TestKt.lambda-1, %composer, 0)
C({
}, %composer, 0)
C(stableFun(123), %composer, 0b0110)
@@ -1535,6 +1552,15 @@
B(%composer, %changed or 0b0001)
}
}
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
"""
)
@@ -1553,13 +1579,7 @@
fun Example(%composer: Composer?, %changed: Int) {
%composer.startRestartGroup(<>, "C(Example)<D>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
- D(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
- if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
- Unit
- } else {
- %composer.skipToGroupEnd()
- }
- }, %composer, 0b0110)
+ D(ComposableSingletons%TestKt.lambda-1, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
@@ -1567,7 +1587,15 @@
Example(%composer, %changed or 0b0001)
}
}
-
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
"""
)
@@ -1895,37 +1923,43 @@
}
""",
"""
- val unstableUnused: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
- val %dirty = %changed
- %dirty = %dirty or 0b0110
- if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
- Unit
- } else {
- %composer.skipToGroupEnd()
+ val unstableUnused: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+ val unstableUsed: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-2
+ val stableUnused: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-3
+ val stableUsed: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-4
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+ val %dirty = %changed
+ %dirty = %dirty or 0b0110
+ if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
}
- }
- val unstableUsed: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
- val %dirty = %changed
- print(x)
- }
- val stableUnused: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
- val %dirty = %changed
- %dirty = %dirty or 0b0110
- if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
- Unit
- } else {
- %composer.skipToGroupEnd()
- }
- }
- val stableUsed: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
- val %dirty = %changed
- if (%changed and 0b1110 === 0) {
- %dirty = %dirty or if (%composer.changed(<this>)) 0b0100 else 0b0010
- }
- if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+ val lambda-2: @[ExtensionFunctionType] Function3<Foo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+ val %dirty = %changed
print(x)
- } else {
- %composer.skipToGroupEnd()
+ }
+ val lambda-3: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+ val %dirty = %changed
+ %dirty = %dirty or 0b0110
+ if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ val lambda-4: @[ExtensionFunctionType] Function3<StableFoo, Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+ val %dirty = %changed
+ if (%changed and 0b1110 === 0) {
+ %dirty = %dirty or if (%composer.changed(<this>)) 0b0100 else 0b0010
+ }
+ if (%dirty and 0b01011011 xor 0b00010010 !== 0 || !%composer.skipping) {
+ print(x)
+ } else {
+ %composer.skipToGroupEnd()
+ }
}
}
"""
@@ -2649,13 +2683,12 @@
"""
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
- import androidx.compose.runtime.emptyContent
@Composable
fun Box2(
modifier: Modifier = Modifier,
paddingStart: Dp = Dp.Unspecified,
- content: @Composable () -> Unit = emptyContent()
+ content: @Composable () -> Unit = {}
) {
}
@@ -2676,10 +2709,8 @@
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%default and 0b0010 === 0 && %composer.changed(paddingStart.value)) 0b00100000 else 0b00010000
}
- if (%default and 0b0100 !== 0) {
- %dirty = %dirty or 0b000110000000
- } else if (%changed and 0b001110000000 === 0) {
- %dirty = %dirty or if (%composer.changed(content)) 0b000100000000 else 0b10000000
+ if (%changed and 0b001110000000 === 0) {
+ %dirty = %dirty or if (%default and 0b0100 === 0 && %composer.changed(content)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b001011011011 xor 0b10010010 !== 0 || !%composer.skipping) {
if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) {
@@ -2692,7 +2723,8 @@
%dirty = %dirty and 0b01110000.inv()
}
if (%default and 0b0100 !== 0) {
- content = emptyContent()
+ content = ComposableSingletons%TestKt.lambda-1
+ %dirty = %dirty and 0b001110000000.inv()
}
%composer.endDefaults()
} else {
@@ -2700,6 +2732,9 @@
if (%default and 0b0010 !== 0) {
%dirty = %dirty and 0b01110000.inv()
}
+ if (%default and 0b0100 !== 0) {
+ %dirty = %dirty and 0b001110000000.inv()
+ }
}
} else {
%composer.skipToGroupEnd()
@@ -2708,6 +2743,15 @@
Box2(modifier, paddingStart, content, %composer, %changed or 0b0001, %default)
}
}
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
"""
)
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
index 9b11630..1e4199a 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
@@ -20,6 +20,273 @@
class LambdaMemoizationTransformTests : ComposeIrTransformTest() {
+ @Test
+ fun testCapturedThisFromFieldInitializer(): Unit = verifyComposeIrTransform(
+ """
+ import androidx.compose.runtime.Composable
+
+ class A {
+ val b = ""
+ val c = @Composable {
+ print(b)
+ }
+ }
+ """,
+ """
+ @StabilityInferred(parameters = 0)
+ class A {
+ val b: String = ""
+ val c: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, true, "") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ print(b)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ static val %stable: Int = 0
+ }
+ """,
+ """
+ """
+ )
+
+ @Test
+ fun testLocalInALocal(): Unit = verifyComposeIrTransform(
+ """
+ import androidx.compose.runtime.Composable
+
+ @Composable fun Example() {
+ @Composable fun A() { }
+ @Composable fun B(content: @Composable () -> Unit) { }
+ @Composable fun C() {
+ B { A() }
+ }
+ }
+ """,
+ """
+ @Composable
+ fun Example(%composer: Composer?, %changed: Int) {
+ %composer.startRestartGroup(<>, "C(Example):Test.kt")
+ if (%changed !== 0 || !%composer.skipping) {
+ @Composable
+ fun A(%composer: Composer?, %changed: Int) {
+ %composer.startRestartGroup(<>, "C(A):Test.kt")
+ if (%changed !== 0 || !%composer.skipping) {
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ A(%composer, %changed or 0b0001)
+ }
+ }
+ @Composable
+ fun B(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
+ %composer.startRestartGroup(<>, "C(B):Test.kt")
+ val %dirty = %changed
+ if (%changed and 0b1110 === 0) {
+ %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+ }
+ if (%dirty and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ B(content, %composer, %changed or 0b0001)
+ }
+ }
+ @Composable
+ fun C(%composer: Composer?, %changed: Int) {
+ %composer.startRestartGroup(<>, "C(C)<B>:Test.kt")
+ if (%changed !== 0 || !%composer.skipping) {
+ B(composableLambda(%composer, <>, false, "C<A()>:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ A(%composer, 0)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }, %composer, 0b0110)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ C(%composer, %changed or 0b0001)
+ }
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ Example(%composer, %changed or 0b0001)
+ }
+ }
+ """,
+ """
+ """
+ )
+
+ @Test
+ fun testStateDelegateCapture(): Unit = verifyComposeIrTransform(
+ """
+ import androidx.compose.runtime.Composable
+ import androidx.compose.runtime.mutableStateOf
+ import androidx.compose.runtime.getValue
+
+ @Composable fun A() {
+ val x by mutableStateOf(123)
+ B {
+ print(x)
+ }
+ }
+ """,
+ """
+ @Composable
+ fun A(%composer: Composer?, %changed: Int) {
+ %composer.startRestartGroup(<>, "C(A)<B>:Test.kt")
+ if (%changed !== 0 || !%composer.skipping) {
+ <<LOCALDELPROP>>
+ B(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ print(<get-x>())
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }, %composer, 0b0110)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ A(%composer, %changed or 0b0001)
+ }
+ }
+ """,
+ """
+ import androidx.compose.runtime.Composable
+
+ @Composable fun B(content: @Composable () -> Unit) {}
+ """
+ )
+
+ @Test
+ fun testTopLevelComposableLambdaProperties(): Unit = verifyComposeIrTransform(
+ """
+ import androidx.compose.runtime.Composable
+
+ val foo = @Composable {}
+ val bar: @Composable () -> Unit = {}
+ """,
+ """
+ val foo: Function2<Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+ val bar: Function2<Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-2
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ val lambda-2: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
+ """,
+ """
+ """
+ )
+
+ @Test
+ fun testLocalVariableComposableLambdas(): Unit = verifyComposeIrTransform(
+ """
+ import androidx.compose.runtime.Composable
+
+ @Composable fun A() {
+ val foo = @Composable {}
+ val bar: @Composable () -> Unit = {}
+ B(foo)
+ B(bar)
+ }
+ """,
+ """
+ @Composable
+ fun A(%composer: Composer?, %changed: Int) {
+ %composer.startRestartGroup(<>, "C(A)<B(foo)>,<B(bar)>:Test.kt")
+ if (%changed !== 0 || !%composer.skipping) {
+ val foo = ComposableSingletons%TestKt.lambda-1
+ val bar = ComposableSingletons%TestKt.lambda-2
+ B(foo, %composer, 0)
+ B(bar, %composer, 0)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ A(%composer, %changed or 0b0001)
+ }
+ }
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ val lambda-2: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
+ """,
+ """
+ import androidx.compose.runtime.Composable
+ @Composable fun B(content: @Composable () -> Unit) {}
+ """
+ )
+
+ @Test
+ fun testParameterComposableLambdas(): Unit = verifyComposeIrTransform(
+ """
+ import androidx.compose.runtime.Composable
+
+ @Composable fun A() {
+ B {}
+ }
+ """,
+ """
+ @Composable
+ fun A(%composer: Composer?, %changed: Int) {
+ %composer.startRestartGroup(<>, "C(A)<B>:Test.kt")
+ if (%changed !== 0 || !%composer.skipping) {
+ B(ComposableSingletons%TestKt.lambda-1, %composer, 0)
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ A(%composer, %changed or 0b0001)
+ }
+ }
+ internal class ComposableSingletons%TestKt {
+ val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false, "C:Test.kt") { %composer: Composer?, %changed: Int ->
+ if (%changed and 0b1011 xor 0b0010 !== 0 || !%composer.skipping) {
+ Unit
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
+ """,
+ """
+ import androidx.compose.runtime.Composable
+ @Composable fun B(content: @Composable () -> Unit) {}
+ """
+ )
+
@Test // regression of b/162575428
fun testComposableInAFunctionParameter(): Unit = verifyComposeIrTransform(
"""
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt
index 6c63e91..93fbd5e 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt
@@ -98,7 +98,7 @@
doTest(
"""
import androidx.compose.runtime.*
- var globalContent = emptyContent()
+ var globalContent = @Composable {}
fun setContent(content: @Composable () -> Unit) {
globalContent = content
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt
index 4d9386f..9fe3c46 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt
@@ -19,4 +19,8 @@
BasicWritableSlice(RewritePolicy.DO_NOTHING)
val IS_SYNTHETIC_COMPOSABLE_CALL: WritableSlice<IrFunctionAccessExpression, Boolean> =
BasicWritableSlice(RewritePolicy.DO_NOTHING)
+ val IS_COMPOSABLE_SINGLETON: WritableSlice<IrAttributeContainer, Boolean> =
+ BasicWritableSlice(RewritePolicy.DO_NOTHING)
+ val IS_COMPOSABLE_SINGLETON_CLASS: WritableSlice<IrAttributeContainer, Boolean> =
+ BasicWritableSlice(RewritePolicy.DO_NOTHING)
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
index d852691..16f0e5a 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
@@ -331,6 +331,14 @@
return context.irTrace[ComposeWritableSlices.IS_SYNTHETIC_COMPOSABLE_CALL, this] == true
}
+ fun IrCall.isComposableSingletonGetter(): Boolean {
+ return context.irTrace[ComposeWritableSlices.IS_COMPOSABLE_SINGLETON, this] == true
+ }
+
+ fun IrClass.isComposableSingletonClass(): Boolean {
+ return context.irTrace[ComposeWritableSlices.IS_COMPOSABLE_SINGLETON_CLASS, this] == true
+ }
+
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun IrFunction.isInlinedLambda(): Boolean {
descriptor.findPsi()?.let { psi ->
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index ca17dd2..07950c0 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -653,10 +653,14 @@
private val collectSourceInformation = sourceInformationEnabled
- override fun visitClass(declaration: IrClass): IrStatement =
- inScope(Scope.ClassScope(declaration.name)) {
+ override fun visitClass(declaration: IrClass): IrStatement {
+ if (declaration.isComposableSingletonClass()) {
+ return declaration
+ }
+ return inScope(Scope.ClassScope(declaration.name)) {
super.visitDeclaration(declaration)
}
+ }
override fun visitFunction(declaration: IrFunction): IrStatement {
val scope = Scope.FunctionScope(declaration, this)
@@ -2522,6 +2526,23 @@
recordSourceParameter(expression, 3, composableLambdaScope)
return expression
}
+ expression.isComposableSingletonGetter() -> {
+ // This looks like `ComposableSingletonClass.lambda-123`, which is a static/saved
+ // call of composableLambdaInstance. We want to pass
+ // locations on the top level of the lambda as the startRestartGroup is in the
+ // composable lambda wrapper.
+ val getter = expression.symbol.owner
+ val property = getter.correspondingPropertySymbol?.owner
+ val fieldInitializer = property?.backingField?.initializer?.expression
+ val composableLambdaInstanceCall = fieldInitializer as IrCall
+ val composableLambdaScope = withScope(Scope.ComposableLambdaScope()) {
+ property.transformChildrenVoid()
+ }
+ if (collectSourceInformation) {
+ recordSourceParameter(composableLambdaInstanceCall, 2, composableLambdaScope)
+ }
+ return super.visitCall(expression)
+ }
else -> return super.visitCall(expression)
}
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
index 615237a..5d6c1b2 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
@@ -23,28 +23,47 @@
import androidx.compose.compiler.plugins.kotlin.irTrace
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
+import org.jetbrains.kotlin.backend.common.ir.addChild
+import org.jetbrains.kotlin.backend.common.ir.copyTo
+import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
+import org.jetbrains.kotlin.descriptors.ClassKind
+import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
+import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
+import org.jetbrains.kotlin.ir.builders.declarations.addGetter
+import org.jetbrains.kotlin.ir.builders.declarations.addProperty
+import org.jetbrains.kotlin.ir.builders.declarations.buildClass
+import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.irBlock
+import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irBoolean
import org.jetbrains.kotlin.ir.builders.irCall
+import org.jetbrains.kotlin.ir.builders.irDelegatingConstructorCall
+import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.builders.irGet
+import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.builders.irNull
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.builders.irTemporary
+import org.jetbrains.kotlin.ir.declarations.IrAttributeContainer
+import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
+import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
+import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.copyAttributes
+import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
@@ -53,14 +72,20 @@
import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
+import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
+import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
import org.jetbrains.kotlin.ir.util.defaultType
+import org.jetbrains.kotlin.ir.util.isLocal
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
+import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
+import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
+import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -69,10 +94,16 @@
private class CaptureCollector {
val captures = mutableSetOf<IrValueDeclaration>()
+ val capturedFunctions = mutableSetOf<IrFunction>()
+ val hasCaptures: Boolean get() = captures.isNotEmpty() || capturedFunctions.isNotEmpty()
fun recordCapture(local: IrValueDeclaration) {
captures.add(local)
}
+
+ fun recordCapture(local: IrFunction) {
+ capturedFunctions.add(local)
+ }
}
private abstract class DeclarationContext {
@@ -80,7 +111,9 @@
abstract val symbol: IrSymbol
abstract val functionContext: FunctionContext?
abstract fun declareLocal(local: IrValueDeclaration?)
+ abstract fun recordLocalFunction(local: FunctionContext)
abstract fun recordCapture(local: IrValueDeclaration?)
+ abstract fun recordCapture(local: IrFunction?)
abstract fun pushCollector(collector: CaptureCollector)
abstract fun popCollector(collector: CaptureCollector)
}
@@ -90,7 +123,9 @@
override val functionContext: FunctionContext? get() = null
override val symbol get() = declaration.symbol
override fun declareLocal(local: IrValueDeclaration?) { }
+ override fun recordLocalFunction(local: FunctionContext) { }
override fun recordCapture(local: IrValueDeclaration?) { }
+ override fun recordCapture(local: IrFunction?) { }
override fun pushCollector(collector: CaptureCollector) { }
override fun popCollector(collector: CaptureCollector) { }
}
@@ -102,7 +137,10 @@
override val composable: Boolean get() = functionContext.composable
override val symbol: IrSymbol get() = declaration.symbol
override fun declareLocal(local: IrValueDeclaration?) = functionContext.declareLocal(local)
+ override fun recordLocalFunction(local: FunctionContext) =
+ functionContext.recordLocalFunction(local)
override fun recordCapture(local: IrValueDeclaration?) = functionContext.recordCapture(local)
+ override fun recordCapture(local: IrFunction?) = functionContext.recordCapture(local)
override fun pushCollector(collector: CaptureCollector) =
functionContext.pushCollector(collector)
override fun popCollector(collector: CaptureCollector) =
@@ -117,7 +155,9 @@
override val symbol get() = declaration.symbol
override val functionContext: FunctionContext? get() = this
val locals = mutableSetOf<IrValueDeclaration>()
+ val captures = mutableSetOf<IrValueDeclaration>()
var collectors = mutableListOf<CaptureCollector>()
+ val localFunctionCaptures = mutableMapOf<IrFunction, Set<IrValueDeclaration>>()
init {
declaration.valueParameters.forEach {
@@ -133,12 +173,35 @@
}
}
+ override fun recordLocalFunction(local: FunctionContext) {
+ if (local.captures.isNotEmpty() && local.declaration.isLocal) {
+ localFunctionCaptures[local.declaration] = local.captures
+ }
+ }
+
override fun recordCapture(local: IrValueDeclaration?) {
if (local != null && collectors.isNotEmpty() && locals.contains(local)) {
for (collector in collectors) {
collector.recordCapture(local)
}
}
+ if (local != null && declaration.isLocal && !locals.contains(local)) {
+ captures.add(local)
+ }
+ }
+
+ override fun recordCapture(local: IrFunction?) {
+ if (local != null) {
+ val captures = localFunctionCaptures[local]
+ for (collector in collectors) {
+ collector.recordCapture(local)
+ if (captures != null) {
+ for (capture in captures) {
+ collector.recordCapture(capture)
+ }
+ }
+ }
+ }
}
override fun pushCollector(collector: CaptureCollector) {
@@ -151,6 +214,31 @@
}
}
+private class ClassContext(val declaration: IrClass) : DeclarationContext() {
+ override val composable: Boolean = false
+ override val symbol get() = declaration.symbol
+ override val functionContext: FunctionContext? = null
+ val thisParam: IrValueDeclaration? = declaration.thisReceiver!!
+ var collectors = mutableListOf<CaptureCollector>()
+ override fun declareLocal(local: IrValueDeclaration?) { }
+ override fun recordLocalFunction(local: FunctionContext) { }
+ override fun recordCapture(local: IrValueDeclaration?) {
+ if (local != null && collectors.isNotEmpty() && local == thisParam) {
+ for (collector in collectors) {
+ collector.recordCapture(local)
+ }
+ }
+ }
+ override fun recordCapture(local: IrFunction?) { }
+ override fun pushCollector(collector: CaptureCollector) {
+ collectors.add(collector)
+ }
+ override fun popCollector(collector: CaptureCollector) {
+ require(collectors.lastOrNull() == collector)
+ collectors.removeAt(collectors.size - 1)
+ }
+}
+
const val COMPOSABLE_LAMBDA = "composableLambda"
const val COMPOSABLE_LAMBDA_N = "composableLambdaN"
const val COMPOSABLE_LAMBDA_INSTANCE = "composableLambdaInstance"
@@ -170,6 +258,65 @@
private val currentFunctionContext: FunctionContext? get() =
declarationContextStack.peek()?.functionContext
+ private var composableSingletonsClass: IrClass? = null
+ private var currentFile: IrFile? = null
+
+ private fun getOrCreateComposableSingletonsClass(): IrClass {
+ if (composableSingletonsClass != null) return composableSingletonsClass!!
+ val declaration = currentFile!!
+ val filePath = declaration.fileEntry.name
+ val fileName = filePath.split('/').last()
+ val current = context.irFactory.buildClass {
+ kind = ClassKind.OBJECT
+ visibility = DescriptorVisibilities.INTERNAL
+ val shortName = PackagePartClassUtils.getFilePartShortName(fileName)
+ // the name of the LiveLiterals class is per-file, so we use the same name that
+ // the kotlin file class lowering produces, prefixed with `LiveLiterals$`.
+ name = Name.identifier("ComposableSingletons${"$"}$shortName")
+ }.also {
+ it.createParameterDeclarations()
+
+ // store the full file path to the file that this class is associated with in an
+ // annotation on the class. This will be used by tooling to associate the keys
+ // inside of this class with actual PSI in the editor.
+ it.addConstructor {
+ isPrimary = true
+ }.also { ctor ->
+ ctor.body = DeclarationIrBuilder(context, it.symbol).irBlockBody {
+ +irDelegatingConstructorCall(
+ context
+ .irBuiltIns
+ .anyClass
+ .owner
+ .primaryConstructor!!
+ )
+ }
+ }
+ }.markAsComposableSingletonClass()
+ composableSingletonsClass = current
+ return current
+ }
+
+ override fun visitFile(declaration: IrFile): IrFile {
+ val prevFile = currentFile
+ val prevClass = composableSingletonsClass
+ try {
+ currentFile = declaration
+ composableSingletonsClass = null
+ val file = super.visitFile(declaration)
+ // if there were no constants found in the entire file, then we don't need to
+ // create this class at all
+ val resultingClass = composableSingletonsClass
+ if (resultingClass != null && resultingClass.declarations.isNotEmpty()) {
+ file.addChild(resultingClass)
+ }
+ return file
+ } finally {
+ currentFile = prevFile
+ composableSingletonsClass = prevClass
+ }
+ }
+
override fun lower(module: IrModuleFragment) = module.transformChildrenVoid(this)
override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement {
@@ -214,9 +361,21 @@
// TODO(b/150390108): Consider allowing remember in effects
descriptor.returnType.let { it != null && it.isUnit() }
- declarationContextStack.push(FunctionContext(declaration, composable, canRemember))
+ val context = FunctionContext(declaration, composable, canRemember)
+ declarationContextStack.push(context)
val result = super.visitFunction(declaration)
declarationContextStack.pop()
+ if (declaration.isLocal) {
+ declarationContextStack.peek()?.recordLocalFunction(context)
+ }
+ return result
+ }
+
+ override fun visitClass(declaration: IrClass): IrStatement {
+ val context = ClassContext(declaration)
+ declarationContextStack.push(context)
+ val result = super.visitClass(declaration)
+ declarationContextStack.pop()
return result
}
@@ -226,7 +385,9 @@
}
override fun visitValueAccess(expression: IrValueAccessExpression): IrExpression {
- declarationContextStack.forEach { it.recordCapture(expression.symbol.owner) }
+ declarationContextStack.forEach {
+ it.recordCapture(expression.symbol.owner)
+ }
return super.visitValueAccess(expression)
}
@@ -334,12 +495,25 @@
)
}
+ override fun visitCall(expression: IrCall): IrExpression {
+ val fn = expression.symbol.owner
+ if (fn.isLocal) {
+ declarationContextStack.forEach {
+ it.recordCapture(fn)
+ }
+ }
+ return super.visitCall(expression)
+ }
+
@ObsoleteDescriptorBasedAPI
private fun visitComposableFunctionExpression(
expression: IrFunctionExpression,
declarationContext: DeclarationContext
): IrExpression {
+ val collector = CaptureCollector()
+ startCollector(collector)
val result = super.visitFunctionExpression(expression)
+ stopCollector(collector)
// If the ancestor converted this then return
val functionExpression = result as? IrFunctionExpression ?: return result
@@ -352,7 +526,61 @@
return functionExpression
}
- return wrapFunctionExpression(declarationContext, functionExpression)
+ val wrapped = wrapFunctionExpression(declarationContext, functionExpression, collector)
+
+ if (!collector.hasCaptures) {
+ return irGetComposableSingleton(
+ lambdaExpression = wrapped,
+ lambdaType = expression.type
+ )
+ } else {
+ return wrapped
+ }
+ }
+
+ private fun irGetComposableSingleton(
+ lambdaExpression: IrExpression,
+ lambdaType: IrType
+ ): IrExpression {
+ val clazz = getOrCreateComposableSingletonsClass()
+ val lambdaName = "lambda-${clazz.declarations.size}"
+ val lambdaProp = clazz.addProperty {
+ name = Name.identifier(lambdaName)
+ visibility = DescriptorVisibilities.INTERNAL
+ }.also { p ->
+ p.backingField = context.irFactory.buildField {
+ name = Name.identifier(lambdaName)
+ type = lambdaType
+ visibility = DescriptorVisibilities.INTERNAL
+ isStatic = true
+ }.also { f ->
+ f.correspondingPropertySymbol = p.symbol
+ f.parent = clazz
+ f.initializer = DeclarationIrBuilder(context, clazz.symbol)
+ .irExprBody(lambdaExpression)
+ }
+ p.addGetter {
+ returnType = lambdaType
+ visibility = DescriptorVisibilities.INTERNAL
+ origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
+ }.also { fn ->
+ val thisParam = clazz.thisReceiver!!.copyTo(fn)
+ fn.parent = clazz
+ fn.dispatchReceiverParameter = thisParam
+ fn.body = DeclarationIrBuilder(context, fn.symbol).irBlockBody {
+ +irReturn(irGetField(irGet(thisParam), p.backingField!!))
+ }
+ }
+ }
+ return irCall(
+ lambdaProp.getter!!.symbol,
+ dispatchReceiver = IrGetObjectValueImpl(
+ startOffset = UNDEFINED_OFFSET,
+ endOffset = UNDEFINED_OFFSET,
+ type = clazz.defaultType,
+ symbol = clazz.symbol
+ )
+ ).markAsComposableSingleton()
}
@ObsoleteDescriptorBasedAPI
@@ -380,13 +608,15 @@
@ObsoleteDescriptorBasedAPI
private fun wrapFunctionExpression(
declarationContext: DeclarationContext,
- expression: IrFunctionExpression
+ expression: IrFunctionExpression,
+ collector: CaptureCollector
): IrExpression {
val function = expression.function
val argumentCount = function.descriptor.valueParameters.size
val useComposableLambdaN = argumentCount > MAX_RESTART_ARGUMENT_COUNT
+ val useComposableFactory = collector.hasCaptures && declarationContext.composable
val restartFunctionFactory =
- if (declarationContext.composable)
+ if (useComposableFactory)
if (useComposableLambdaN)
COMPOSABLE_LAMBDA_N
else COMPOSABLE_LAMBDA
@@ -406,8 +636,8 @@
return irBuilder.irCall(restartFactorySymbol).apply {
var index = 0
- // first parameter is the composer parameter if we are in a composable context
- if (declarationContext.composable) {
+ // first parameter is the composer parameter if we are using the composable factory
+ if (useComposableFactory) {
putValueArgument(
index++,
irCurrentComposer()
@@ -424,12 +654,13 @@
)
// tracked parameter
- putValueArgument(index++, irBuilder.irBoolean(expression.isTracked()))
+ // If the lambda has no captures, then kotlin will turn it into a singleton instance,
+ // which means that it will never change, thus does not need to be tracked.
+ val shouldBeTracked = expression.isTracked() && collector.captures.isNotEmpty()
+ putValueArgument(index++, irBuilder.irBoolean(shouldBeTracked))
- if (declarationContext.composable) {
- // sourceInformation parameter
- putValueArgument(index++, irBuilder.irNull())
- }
+ // sourceInformation parameter
+ putValueArgument(index++, irBuilder.irNull())
// ComposableLambdaN requires the arity
if (useComposableLambdaN) {
@@ -551,6 +782,28 @@
return this
}
+ private fun <T : IrAttributeContainer> T.markAsComposableSingleton(): T {
+ // Mark it so the ComposableCallTransformer can insert the correct source information
+ // around this call
+ context.irTrace.record(
+ ComposeWritableSlices.IS_COMPOSABLE_SINGLETON,
+ this,
+ true
+ )
+ return this
+ }
+
+ private fun <T : IrAttributeContainer> T.markAsComposableSingletonClass(): T {
+ // Mark it so the ComposableCallTransformer can insert the correct source information
+ // around this call
+ context.irTrace.record(
+ ComposeWritableSlices.IS_COMPOSABLE_SINGLETON_CLASS,
+ this,
+ true
+ )
+ return this
+ }
+
@OptIn(ObsoleteDescriptorBasedAPI::class)
private fun IrFunctionExpression.isInlineArgument(): Boolean {
function.descriptor.findPsi()?.let { psi ->
diff --git a/compose/desktop/desktop/samples/build.gradle b/compose/desktop/desktop/samples/build.gradle
index 3e0e213..01e355e 100644
--- a/compose/desktop/desktop/samples/build.gradle
+++ b/compose/desktop/desktop/samples/build.gradle
@@ -48,6 +48,7 @@
task run1(type: JavaExec) {
dependsOn(":compose:desktop:desktop:jar")
main = 'androidx.compose.desktop.examples.example1.MainKt'
+ systemProperty("skiko.fps.enabled", "true")
def compilation = kotlin.jvm().compilations["main"]
classpath =
compilation.output.allOutputs +
@@ -81,6 +82,16 @@
compilation.runtimeDependencyFiles
}
+task runVsync(type: JavaExec) {
+ dependsOn(":compose:desktop:desktop:jar")
+ main = 'androidx.compose.desktop.examples.vsynctest.MainKt'
+ jvmArgs("-verbose:gc")
+ def compilation = kotlin.jvm().compilations["main"]
+ classpath =
+ compilation.output.allOutputs +
+ compilation.runtimeDependencyFiles
+}
+
task run {
dependsOn("run1")
}
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt
index 6e75ea9..26e0224 100644
--- a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/swingexample/Main.kt
@@ -48,13 +48,14 @@
import java.awt.event.ActionListener
import javax.swing.JButton
import javax.swing.JFrame
+import javax.swing.SwingUtilities
import javax.swing.WindowConstants
val northClicks = mutableStateOf(0)
val westClicks = mutableStateOf(0)
val eastClicks = mutableStateOf(0)
-fun main() {
+fun main() = SwingUtilities.invokeLater {
// explicitly clear the application events
AppManager.setEvents(
onAppStart = null,
diff --git a/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/vsynctest/Main.kt b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/vsynctest/Main.kt
new file mode 100644
index 0000000..59b3714
--- /dev/null
+++ b/compose/desktop/desktop/samples/src/jvmMain/kotlin/androidx/compose/desktop/examples/vsynctest/Main.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.compose.desktop.examples.vsynctest
+
+import androidx.compose.desktop.AppWindowAmbient
+import androidx.compose.desktop.Window
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.withFrameMillis
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.AmbientDensity
+import androidx.compose.ui.unit.IntSize
+
+private val frameLogCount = 1000
+private val red = Color(255, 128, 128)
+private val cyan = Color(128, 255, 255)
+
+fun main() {
+ window()
+ window()
+}
+
+fun window() {
+ var t1 = Long.MAX_VALUE
+ val frameDeltas = ArrayList<Long>(10000)
+ var heuristicExpectedFrameTime = -1L
+
+ fun logFrame() {
+ val t2 = System.nanoTime()
+ val dt = (t2 - t1).coerceAtLeast(0)
+ frameDeltas.add(dt)
+ t1 = t2
+
+ if (heuristicExpectedFrameTime > 0 && dt > heuristicExpectedFrameTime * 1.5) {
+ val dtMillis = dt / 1E6
+ val expectedMillis = heuristicExpectedFrameTime / 1E6
+ println("Too long frame %.2f (expected %.2f)".format(dtMillis, expectedMillis))
+ }
+
+ if (frameDeltas.size % frameLogCount == 0) {
+ val fps = 1E9 / frameDeltas.average()
+
+ // it is more precise than
+ // window.window.graphicsConfiguration.device.displayMode.refreshRate
+ // if vsync is supported
+ heuristicExpectedFrameTime = frameDeltas.median()
+
+ val actualFrameCount = frameDeltas.sum() / heuristicExpectedFrameTime
+ val missedFrames = (actualFrameCount - frameDeltas.size).coerceAtLeast(0)
+ val missedFrameCountPercent = 100.0 * missedFrames / frameDeltas.size
+ println("FPS %.2f, missed frames %.2f%%".format(fps, missedFrameCountPercent))
+ frameDeltas.clear()
+ }
+ }
+
+ Window(size = IntSize(800, 200)) {
+ val window = AppWindowAmbient.current!!
+ val width = (AmbientDensity.current.density * window.window.width).toInt()
+ val singleFrameMillis = remember {
+ 1000 / window.window.graphicsConfiguration.device.displayMode.refreshRate
+ }
+ var position1 by remember { mutableStateOf(0L) }
+ var position2 by remember { mutableStateOf(0L) }
+ var isOddFrame by remember { mutableStateOf(false) }
+
+ LaunchedEffect(Unit) {
+ while (true) {
+ withFrameMillis {
+ position1 = it % width
+ position2 = (it / 4) % width
+ }
+ }
+ }
+
+ Canvas(Modifier.fillMaxSize()) {
+ for (x in 0..width step singleFrameMillis) {
+ drawLine(Color.Black, Offset(x.toFloat(), 0f), Offset(x.toFloat(), 10f))
+ }
+
+ drawRect(Color.Red, Offset(position1.toFloat(), 10f), Size(32f, 32f))
+ drawRect(Color.Red, Offset(position2.toFloat(), 50f), Size(32f, 32f))
+
+ // test similar to https://www.vsynctester.com/
+ drawRect(if (isOddFrame) red else cyan, Offset(10f, 120f), Size(50f, 50f))
+ isOddFrame = !isOddFrame
+
+ logFrame()
+ }
+ }
+}
+
+private fun List<Long>.median() = sorted()[size / 2]
\ No newline at end of file
diff --git a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/WindowDraggableArea.kt b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/WindowDraggableArea.kt
index 60b07ce..a058795 100644
--- a/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/WindowDraggableArea.kt
+++ b/compose/desktop/desktop/src/jvmMain/kotlin/androidx/compose/ui/window/WindowDraggableArea.kt
@@ -19,7 +19,6 @@
import androidx.compose.desktop.AppManager
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -30,7 +29,7 @@
@Composable
fun WindowDraggableArea(
modifier: Modifier = Modifier,
- content: @Composable() () -> Unit = emptyContent()
+ content: @Composable() () -> Unit = {}
) {
Box(
modifier = modifier.dragGestureFilter(
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt
index 4b170d8..0584869 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.layout
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -483,7 +482,7 @@
maxIntrinsicHeight: Dp
) {
Layout(
- emptyContent(),
+ {},
minIntrinsicWidthMeasureBlock = { _, _ -> minIntrinsicWidth.toIntPx() },
minIntrinsicHeightMeasureBlock = { _, _ -> minIntrinsicHeight.toIntPx() },
maxIntrinsicWidthMeasureBlock = { _, _ -> maxIntrinsicWidth.toIntPx() },
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
index 392bf97..3326100 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
@@ -19,7 +19,6 @@
import androidx.compose.ui.layout.FirstBaseline
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Providers
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.Alignment
import androidx.compose.ui.layout.HorizontalAlignmentLine
import androidx.compose.ui.layout.Layout
@@ -1342,12 +1341,12 @@
Modifier.weight(1f),
width = sizeDp,
height = sizeDp,
- content = emptyContent()
+ content = {}
)
Container(
width = (sizeDp * 2),
height = (sizeDp * 2),
- content = emptyContent()
+ content = {}
)
}
}
@@ -1476,12 +1475,12 @@
Modifier.weight(1f),
width = sizeDp,
height = sizeDp,
- content = emptyContent()
+ content = {}
)
Container(
width = sizeDp * 2,
height = sizeDp * 2,
- content = emptyContent()
+ content = {}
)
}
}
@@ -1812,12 +1811,12 @@
Modifier.weight(1f),
width = sizeDp,
height = sizeDp,
- content = emptyContent()
+ content = {}
)
Container(
width = (sizeDp * 2),
height = (sizeDp * 2),
- content = emptyContent()
+ content = {}
)
}
}
@@ -1946,12 +1945,12 @@
Modifier.weight(1f),
width = sizeDp,
height = sizeDp,
- content = emptyContent()
+ content = {}
)
Container(
width = sizeDp * 2,
height = sizeDp * 2,
- content = emptyContent()
+ content = {}
)
}
}
@@ -2139,7 +2138,7 @@
assertEquals(Constraints(), constraints)
FixedSizeLayout(0, noWeightChildHeight.toIntPx(), mapOf())
}
- Layout(emptyContent(), Modifier.weight(1f)) { _, constraints ->
+ Layout({}, Modifier.weight(1f)) { _, constraints ->
assertEquals(
columnMinHeight.toIntPx() - noWeightChildHeight.toIntPx() * 2,
constraints.minHeight
@@ -3206,19 +3205,19 @@
testIntrinsics(
@Composable {
Row {
- Container(Modifier.aspectRatio(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@Composable {
Row(Modifier.fillMaxWidth()) {
- Container(Modifier.aspectRatio(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3227,12 +3226,12 @@
Container(
Modifier.aspectRatio(2f)
.align(Alignment.Top),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
Modifier.align(Alignment.CenterVertically),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3240,21 +3239,21 @@
Row {
Container(
Modifier.aspectRatio(2f).alignBy(FirstBaseline),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
Modifier.alignBy { it.width },
- content = emptyContent()
+ content = {}
)
}
},
@Composable {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Start) {
- Container(Modifier.aspectRatio(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3262,12 +3261,12 @@
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Container(
Modifier.align(Alignment.CenterVertically).aspectRatio(2f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
Modifier.align(Alignment.CenterVertically),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3275,40 +3274,40 @@
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
Container(
Modifier.align(Alignment.Bottom).aspectRatio(2f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
Modifier.align(Alignment.Bottom),
- content = emptyContent()
+ content = {}
)
}
},
@Composable {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround) {
- Container(Modifier.fillMaxHeight().aspectRatio(2f), content = emptyContent())
+ Container(Modifier.fillMaxHeight().aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
Modifier.fillMaxHeight(),
- content = emptyContent()
+ content = {}
)
}
},
@Composable {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
- Container(Modifier.aspectRatio(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@Composable {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
- Container(Modifier.aspectRatio(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
}
@@ -3346,17 +3345,17 @@
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
Modifier.weight(3f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(30.toDp(), 40.toDp()),
Modifier.weight(2f),
- content = emptyContent()
+ content = {}
)
- Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f).weight(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3365,18 +3364,18 @@
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
Modifier.weight(3f).align(Alignment.Top),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(30.toDp(), 40.toDp()),
Modifier.weight(2f).align(Alignment.CenterVertically),
- content = emptyContent()
+ content = {}
)
- Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f).weight(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
Modifier.align(Alignment.Bottom),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3385,17 +3384,17 @@
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
Modifier.weight(3f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(30.toDp(), 40.toDp()),
Modifier.weight(2f),
- content = emptyContent()
+ content = {}
)
- Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f).weight(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3404,21 +3403,21 @@
ConstrainedBox(
constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
modifier = Modifier.weight(3f).align(Alignment.CenterVertically),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
constraints = DpConstraints.fixed(30.toDp(), 40.toDp()),
modifier = Modifier.weight(2f).align(Alignment.CenterVertically),
- content = emptyContent()
+ content = {}
)
Container(
Modifier.aspectRatio(2f).weight(2f).align(Alignment.CenterVertically),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
modifier = Modifier.align(Alignment.CenterVertically),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3427,21 +3426,21 @@
ConstrainedBox(
constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
modifier = Modifier.weight(3f).align(Alignment.Bottom),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
constraints = DpConstraints.fixed(30.toDp(), 40.toDp()),
modifier = Modifier.weight(2f).align(Alignment.Bottom),
- content = emptyContent()
+ content = {}
)
Container(
Modifier.aspectRatio(2f).weight(2f).align(Alignment.Bottom),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
modifier = Modifier.align(Alignment.Bottom),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3450,21 +3449,21 @@
ConstrainedBox(
constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
modifier = Modifier.weight(3f).fillMaxHeight(),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
constraints = DpConstraints.fixed(30.toDp(), 40.toDp()),
modifier = Modifier.weight(2f).fillMaxHeight(),
- content = emptyContent()
+ content = {}
)
Container(
Modifier.aspectRatio(2f).weight(2f).fillMaxHeight(),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
constraints = DpConstraints.fixed(20.toDp(), 30.toDp()),
modifier = Modifier.fillMaxHeight(),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3473,17 +3472,17 @@
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
Modifier.weight(3f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(30.toDp(), 40.toDp()),
Modifier.weight(2f),
- content = emptyContent()
+ content = {}
)
- Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f).weight(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3492,17 +3491,17 @@
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
Modifier.weight(3f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(30.toDp(), 40.toDp()),
Modifier.weight(2f),
- content = emptyContent()
+ content = {}
)
- Container(Modifier.aspectRatio(2f).weight(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f).weight(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(20.toDp(), 30.toDp()),
- content = emptyContent()
+ content = {}
)
}
}
@@ -3559,10 +3558,10 @@
testIntrinsics(
@Composable {
Column {
- Container(Modifier.aspectRatio(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3570,12 +3569,12 @@
Column {
Container(
Modifier.aspectRatio(2f).align(Alignment.Start),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
Modifier.align(Alignment.End),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3583,30 +3582,30 @@
Column {
Container(
Modifier.aspectRatio(2f).alignBy { 0 },
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
Modifier.alignBy(TestVerticalLine),
- content = emptyContent()
+ content = {}
)
}
},
@Composable {
Column(Modifier.fillMaxHeight()) {
- Container(Modifier.aspectRatio(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@Composable {
Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Top) {
- Container(Modifier.aspectRatio(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3614,11 +3613,11 @@
Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Center) {
Container(
Modifier.align(Alignment.CenterHorizontally).aspectRatio(2f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3626,31 +3625,31 @@
Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.Bottom) {
Container(
Modifier.align(Alignment.End).aspectRatio(2f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
Modifier.align(Alignment.End),
- content = emptyContent()
+ content = {}
)
}
},
@Composable {
Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceAround) {
- Container(Modifier.fillMaxWidth().aspectRatio(2f), content = emptyContent())
+ Container(Modifier.fillMaxWidth().aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
Modifier.fillMaxWidth(),
- content = emptyContent()
+ content = {}
)
}
},
@Composable {
Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceBetween) {
- Container(Modifier.aspectRatio(2f), content = emptyContent())
+ Container(Modifier.aspectRatio(2f), content = {})
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3658,11 +3657,11 @@
Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceEvenly) {
Container(
Modifier.aspectRatio(2f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(50.toDp(), 40.toDp()),
- content = emptyContent()
+ content = {}
)
}
}
@@ -3700,20 +3699,20 @@
ConstrainedBox(
DpConstraints.fixed(30.toDp(), 20.toDp()),
Modifier.weight(3f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(40.toDp(), 30.toDp()),
Modifier.weight(2f),
- content = emptyContent()
+ content = {}
)
Container(
Modifier.aspectRatio(0.5f).weight(2f),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(30.toDp(), 20.toDp()),
- content = emptyContent()
+ content = {}
)
}
},
@@ -3722,12 +3721,12 @@
ConstrainedBox(
DpConstraints.fixed(30.toDp(), 20.toDp()),
Modifier.weight(3f).align(Alignment.Start),
- content = emptyContent()
+ content = {}
)
ConstrainedBox(
DpConstraints.fixed(40.toDp(), 30.toDp()),
Modifier.weight(2f).align(Alignment.CenterHorizontally),
- content = emptyContent()
+ content = {}
)
Container(Modifier.aspectRatio(0.5f).weight(2f)) { }
ConstrainedBox(
@@ -3964,9 +3963,9 @@
containerHeight.value = coordinates.size.height
positionedLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
- Container(Modifier.weight(1f), content = emptyContent())
+ Container(Modifier.weight(1f), content = {})
}
}
}
@@ -3990,7 +3989,7 @@
modifier = Modifier.alignBy { it.height },
width = size,
height = size,
- content = emptyContent()
+ content = {}
)
Container(
modifier = Modifier.alignBy { 0 }
@@ -4002,7 +4001,7 @@
},
width = size,
height = size,
- content = emptyContent()
+ content = {}
)
}
}
@@ -4085,7 +4084,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4141,7 +4140,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4196,7 +4195,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4249,7 +4248,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4521,7 +4520,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4575,7 +4574,7 @@
coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4628,7 +4627,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4688,7 +4687,7 @@
coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4747,7 +4746,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4817,7 +4816,7 @@
coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4886,7 +4885,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -4954,7 +4953,7 @@
coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -5020,7 +5019,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -5086,7 +5085,7 @@
coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -5149,7 +5148,7 @@
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
@@ -5218,7 +5217,7 @@
coordinates
drawLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
index 5f460cb..2eddbde 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/SizeTest.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.layout
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.Providers
import androidx.compose.ui.Alignment
import androidx.compose.ui.layout.Layout
@@ -1295,7 +1294,7 @@
// Clear contents before each test so that we don't recompose the BoxWithConstraints call;
// doing so would recompose the old subcomposition with old constraints in the presence of
// new content before the measurement performs explicit composition the new constraints.
- show(emptyContent())
+ show({})
show {
Layout({
BoxWithConstraints(modifier) {
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt
index 6db4f41..46170b4 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Spacer.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.layout
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.hasFixedHeight
@@ -33,7 +32,7 @@
*/
@Composable
fun Spacer(modifier: Modifier) {
- Layout(emptyContent(), modifier) { _, constraints ->
+ Layout({}, modifier) { _, constraints ->
with(constraints) {
val width = if (hasFixedWidth) maxWidth else 0
val height = if (hasFixedHeight) maxHeight else 0
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 2640b07..b61ab7e 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -193,10 +193,10 @@
public final class FloatAndroidFlingDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
ctor public FloatAndroidFlingDecaySpec(androidx.compose.ui.unit.Density density);
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(float start, float startVelocity);
- method public float getTarget(float start, float startVelocity);
- method public float getValue(long playTime, float start, float startVelocity);
- method public float getVelocity(long playTime, float start, float startVelocity);
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
property public float absVelocityThreshold;
}
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 2640b07..b61ab7e 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -193,10 +193,10 @@
public final class FloatAndroidFlingDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
ctor public FloatAndroidFlingDecaySpec(androidx.compose.ui.unit.Density density);
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(float start, float startVelocity);
- method public float getTarget(float start, float startVelocity);
- method public float getValue(long playTime, float start, float startVelocity);
- method public float getVelocity(long playTime, float start, float startVelocity);
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
property public float absVelocityThreshold;
}
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 2640b07..b61ab7e 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -193,10 +193,10 @@
public final class FloatAndroidFlingDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
ctor public FloatAndroidFlingDecaySpec(androidx.compose.ui.unit.Density density);
method public float getAbsVelocityThreshold();
- method public long getDurationMillis(float start, float startVelocity);
- method public float getTarget(float start, float startVelocity);
- method public float getValue(long playTime, float start, float startVelocity);
- method public float getVelocity(long playTime, float start, float startVelocity);
+ method public long getDurationNanos(float initialValue, float initialVelocity);
+ method public float getTargetValue(float initialValue, float initialVelocity);
+ method public float getValueFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
+ method public float getVelocityFromNanos(long playTimeNanos, float initialValue, float initialVelocity);
property public float absVelocityThreshold;
}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingDecaySpec.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingDecaySpec.kt
index 8ef4e52..d07bf8b 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingDecaySpec.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/animation/AndroidFlingDecaySpec.kt
@@ -23,12 +23,6 @@
import androidx.compose.ui.unit.Density
import kotlin.math.sign
-@Deprecated(
- "AndroidFlingDecaySpec has been renamed to FloatAndroidFlingDecaySpec",
- replaceWith = ReplaceWith("FloatAndroidFlingDecaySpec")
-)
-typealias AndroidFlingDecaySpec = FloatAndroidFlingDecaySpec
-
/**
* A native Android fling curve decay.
*
@@ -46,17 +40,32 @@
private fun flingDistance(startVelocity: Float): Float =
flingCalculator.flingDistance(startVelocity) * sign(startVelocity)
- override fun getTarget(start: Float, startVelocity: Float): Float =
- start + flingDistance(startVelocity)
+ override fun getTargetValue(initialValue: Float, initialVelocity: Float): Float =
+ initialValue + flingDistance(initialVelocity)
- override fun getValue(playTime: Long, start: Float, startVelocity: Float): Float =
- start + flingCalculator.flingInfo(startVelocity).position(playTime)
+ @Suppress("MethodNameUnits")
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ initialVelocity: Float
+ ): Float {
+ val playTimeMillis = playTimeNanos / 1_000_000L
+ return initialValue + flingCalculator.flingInfo(initialVelocity).position(playTimeMillis)
+ }
- override fun getDurationMillis(start: Float, startVelocity: Float): Long =
- flingCalculator.flingDuration(startVelocity)
+ @Suppress("MethodNameUnits")
+ override fun getDurationNanos(initialValue: Float, initialVelocity: Float): Long =
+ flingCalculator.flingDuration(initialVelocity) * 1_000_000L
- override fun getVelocity(playTime: Long, start: Float, startVelocity: Float): Float =
- flingCalculator.flingInfo(startVelocity).velocity(playTime)
+ @Suppress("MethodNameUnits")
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ initialVelocity: Float
+ ): Float {
+ val playTimeMillis = playTimeNanos / 1_000_000L
+ return flingCalculator.flingInfo(initialVelocity).velocity(playTimeMillis)
+ }
}
/**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt
index 2f8d50d..a97afc2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Image.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.layout.Layout
@@ -184,7 +183,7 @@
// Explicitly use a simple Layout implementation here as Spacer squashes any non fixed
// constraint with zero
Layout(
- emptyContent(),
+ {},
modifier.then(semantics).clipToBounds().paint(
painter,
alignment = alignment,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt
index 5eb7e0d..c1763ec 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt
@@ -20,7 +20,7 @@
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.spring
import androidx.compose.foundation.gestures.Scrollable
-import androidx.compose.runtime.withFrameMillis
+import androidx.compose.runtime.withFrameNanos
/**
* Smooth scroll by [value] pixels.
@@ -44,16 +44,16 @@
var previousValue = 0f
scroll {
- val startTimeMillis = withFrameMillis { it }
+ val startTimeNanos = withFrameNanos { it }
do {
- val finished = withFrameMillis { frameTimeMillis ->
+ val finished = withFrameNanos { frameTimeNanos ->
val newValue = conv.convertFromVector(
- animSpec.getValue(
- playTime = frameTimeMillis - startTimeMillis,
- start = zeroVector,
- end = targetVector,
+ animSpec.getValueFromNanos(
+ playTimeNanos = frameTimeNanos - startTimeNanos,
+ initialValue = zeroVector,
+ targetValue = targetVector,
// TODO: figure out if/how we should incorporate existing velocity
- startVelocity = zeroVector
+ initialVelocity = zeroVector
)
)
val delta = newValue - previousValue
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
index f847c51..9ff8fdb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
@@ -22,7 +22,6 @@
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.DisposableEffectResult
import androidx.compose.runtime.DisposableEffectScope
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -148,7 +147,7 @@
Layout(
content = if (inlineComposables.isEmpty()) {
- emptyContent()
+ {}
} else {
{ InlineChildren(text, inlineComposables) }
},
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index c7801fc..6fb1477 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -26,7 +26,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.currentRecomposeScope
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -460,7 +459,7 @@
.textFieldKeyboardModifier(manager)
SimpleLayout(coreTextFieldModifier) {
- Layout(emptyContent()) { _, constraints ->
+ Layout({ }) { _, constraints ->
TextFieldDelegate.layout(
state.textDelegate,
constraints,
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/animation/DesktopFlingDecaySpec.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/animation/DesktopFlingDecaySpec.kt
index 06a4d1b..f55f526 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/animation/DesktopFlingDecaySpec.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/animation/DesktopFlingDecaySpec.kt
@@ -34,15 +34,30 @@
private fun flingDistance(startVelocity: Float): Float =
flingCalculator.flingDistance(startVelocity) * sign(startVelocity)
- override fun getTarget(start: Float, startVelocity: Float): Float =
- start + flingDistance(startVelocity)
+ override fun getTargetValue(initialValue: Float, initialVelocity: Float): Float =
+ initialValue + flingDistance(initialVelocity)
- override fun getValue(playTime: Long, start: Float, startVelocity: Float): Float =
- start + flingCalculator.flingInfo(startVelocity).position(playTime)
+ @Suppress("MethodNameUnits")
+ override fun getValueFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ initialVelocity: Float
+ ): Float {
+ val playTimeMillis = playTimeNanos / 1_000_000L
+ return initialValue + flingCalculator.flingInfo(initialVelocity).position(playTimeMillis)
+ }
- override fun getDurationMillis(start: Float, startVelocity: Float): Long =
- flingCalculator.flingDuration(startVelocity)
+ @Suppress("MethodNameUnits")
+ override fun getDurationNanos(initialValue: Float, initialVelocity: Float): Long =
+ flingCalculator.flingDuration(initialVelocity)
- override fun getVelocity(playTime: Long, start: Float, startVelocity: Float): Float =
- flingCalculator.flingInfo(startVelocity).velocity(playTime)
+ @Suppress("MethodNameUnits")
+ override fun getVelocityFromNanos(
+ playTimeNanos: Long,
+ initialValue: Float,
+ initialVelocity: Float
+ ): Float {
+ val playTimeMillis = playTimeNanos / 1_000_000L
+ return flingCalculator.flingInfo(initialVelocity).velocity(playTimeMillis)
+ }
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
index a29320c..01dd0b6 100644
--- a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
+++ b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
@@ -68,7 +68,7 @@
val rule = createComposeRule()
// don't inline, surface controls canvas life time
- private val surface = Surface.makeRasterN32Premul(100, 100)!!
+ private val surface = Surface.makeRasterN32Premul(100, 100)
private val canvas = surface.canvas
@Test
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt
index a61b285..696e0be 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/compose/ui/lazy/LazyListScrollingBenchmark.kt
@@ -29,7 +29,6 @@
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.testutils.assertNoPendingChanges
import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
import androidx.compose.testutils.doFramesUntilNoChangesPending
@@ -237,7 +236,7 @@
@Composable
fun RemeasurableItem() {
Layout(
- emptyContent(),
+ {},
modifier = object : RemeasurementModifier {
override fun onRemeasurementAvailable(remeasurement: Remeasurement) {
this@ListRemeasureTestCase.remeasurement = remeasurement
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/AnimationBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/AnimationBenchmark.kt
index 75a6481..3911015 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/AnimationBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/AnimationBenchmark.kt
@@ -51,7 +51,7 @@
benchmarkRule.measureRepeated {
for (time in 0..1000 step 20) {
- anim.getValue(time.toLong(), start, end, start)
+ anim.getValueFromNanos(time * 1_000_000L, start, end, start)
}
}
}
@@ -65,7 +65,7 @@
benchmarkRule.measureRepeated {
for (time in 0..1000 step 20) {
- fixedAnimation.getValue(time.toLong())
+ fixedAnimation.getValueFromNanos(time * 1_000_000L)
}
}
}
@@ -78,7 +78,7 @@
benchmarkRule.measureRepeated {
for (time in 0..1000 step 20) {
- anim.getValue(time.toLong(), start, end, start)
+ anim.getValueFromNanos(time * 1_000_000L, start, end, start)
}
}
}
@@ -92,7 +92,7 @@
benchmarkRule.measureRepeated {
for (time in 0..1000 step 20) {
- fixedAnimation.getValue(time.toLong())
+ fixedAnimation.getValueFromNanos(time * 1_000_000L)
}
}
}
@@ -105,7 +105,7 @@
benchmarkRule.measureRepeated {
for (time in 0..1) {
- anim.getValue(time.toLong(), start, end, start)
+ anim.getValueFromNanos(time * 1_000_000L, start, end, start)
}
}
}
@@ -119,7 +119,7 @@
benchmarkRule.measureRepeated {
for (time in 0..1) {
- fixedAnimation.getValue(time.toLong())
+ fixedAnimation.getValueFromNanos(time * 1_000_000L)
}
}
}
@@ -138,7 +138,7 @@
benchmarkRule.measureRepeated {
for (time in 0..1000 step 20) {
- anim.getValue(time.toLong(), start, end, start)
+ anim.getValueFromNanos(time * 1_000_000L, start, end, start)
}
}
}
@@ -158,7 +158,7 @@
benchmarkRule.measureRepeated {
for (time in 0..1000 step 20) {
- fixedAnimation.getValue(time.toLong())
+ fixedAnimation.getValueFromNanos(time * 1_000_000L)
}
}
}
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
index 9d74add..91d0050 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
@@ -44,7 +44,6 @@
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -267,7 +266,7 @@
elevation = 4.dp,
color = color,
border = BorderStroke(2.dp, SolidColor(Color.Black.copy(alpha = 0.75f))),
- content = emptyContent()
+ content = {}
)
}
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
index e2cba15..2389196 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
@@ -22,7 +22,6 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Providers
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.AmbientLayoutDirection
@@ -72,7 +71,7 @@
drawerContent = {
Box(Modifier.fillMaxSize().testTag("content"))
},
- bodyContent = emptyContent()
+ bodyContent = {}
)
}
@@ -89,7 +88,7 @@
drawerContent = {
Box(Modifier.fillMaxSize().testTag("content"))
},
- bodyContent = emptyContent()
+ bodyContent = {}
)
}
@@ -107,7 +106,7 @@
drawerContent = {
Box(Modifier.fillMaxSize().testTag("content"))
},
- bodyContent = emptyContent()
+ bodyContent = {}
)
}
@@ -124,7 +123,7 @@
drawerContent = {
Box(Modifier.fillMaxSize().testTag("content"))
},
- bodyContent = emptyContent()
+ bodyContent = {}
)
}
@@ -144,7 +143,7 @@
drawerContent = {
Box(Modifier.fillMaxSize().testTag("content"))
},
- bodyContent = emptyContent()
+ bodyContent = {}
)
}
@@ -164,7 +163,7 @@
drawerContent = {
Box(Modifier.fillMaxSize().testTag("drawer"))
},
- bodyContent = emptyContent()
+ bodyContent = {}
)
}
@@ -326,7 +325,7 @@
drawerContent = {
Box(Modifier.fillMaxSize().testTag("drawer"))
},
- bodyContent = emptyContent()
+ bodyContent = {}
)
}
@@ -515,7 +514,7 @@
drawerContent = {
Box(Modifier.fillMaxSize().testTag("drawer"))
},
- bodyContent = emptyContent()
+ bodyContent = {}
)
}
@@ -554,7 +553,7 @@
drawerContent = {
Box(Modifier.fillMaxSize().testTag("drawer"))
},
- bodyContent = emptyContent()
+ bodyContent = {}
)
}
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
index f6e3fab..0858323 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
@@ -21,7 +21,6 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.preferredHeight
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
@@ -66,7 +65,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
@@ -88,7 +87,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Expanded),
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
@@ -110,7 +109,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
@@ -131,7 +130,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Expanded),
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
@@ -151,7 +150,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.HalfExpanded),
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
@@ -173,7 +172,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = sheetState,
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
@@ -215,7 +214,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = sheetState,
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
@@ -256,7 +255,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = sheetState,
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
@@ -288,7 +287,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = sheetState,
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
@@ -319,7 +318,7 @@
rule.setMaterialContent {
ModalBottomSheetLayout(
sheetState = sheetState,
- content = emptyContent(),
+ content = {},
sheetContent = {
Box(
Modifier
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
index 94b26c2..59074a7 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
@@ -22,7 +22,6 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.preferredSize
-import androidx.compose.runtime.emptyContent
import androidx.compose.testutils.assertPixels
import androidx.compose.testutils.assertShape
import androidx.compose.ui.Modifier
@@ -115,7 +114,7 @@
Modifier.fillMaxSize().padding(2.dp),
elevation = 2.dp,
color = Color.Blue,
- content = emptyContent()
+ content = {}
)
}
@@ -132,7 +131,7 @@
Modifier.fillMaxSize().padding(2.dp),
elevation = 2.dp,
color = Color.Blue,
- content = emptyContent()
+ content = {}
)
}
}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
index 8d9a5a9..3144396 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
@@ -22,7 +22,6 @@
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Providers
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticAmbientOf
import androidx.compose.ui.Modifier
@@ -148,10 +147,10 @@
fun Scaffold(
modifier: Modifier = Modifier,
scaffoldState: ScaffoldState = rememberScaffoldState(),
- topBar: @Composable () -> Unit = emptyContent(),
- bottomBar: @Composable () -> Unit = emptyContent(),
+ topBar: @Composable () -> Unit = {},
+ bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
- floatingActionButton: @Composable () -> Unit = emptyContent(),
+ floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
isFloatingActionButtonDocked: Boolean = false,
drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
index 4b7e40a..79e179e 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
@@ -35,7 +35,6 @@
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
@@ -180,7 +179,7 @@
indication = rememberRipple(bounded = false, radius = ThumbRippleRadius)
)
.size(ThumbDiameter),
- content = emptyContent()
+ content = {}
)
}
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 833d1e5..9a7f3d7 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -79,8 +79,8 @@
}
public final class ComposeKt {
- method @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
- method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
+ method @Deprecated @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+ method @Deprecated public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
}
public final class ComposeNodeKt {
@@ -597,7 +597,7 @@
public final class ComposableLambdaKt {
method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambda(androidx.compose.runtime.Composer composer, int key, boolean tracked, String? sourceInformation, Object block);
- method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, Object block);
+ method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, String? sourceInformation, Object block);
}
@androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.Stable public final class ComposableLambdaN<R> implements kotlin.jvm.functions.FunctionN<R> {
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index 833d1e5..9a7f3d7 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -79,8 +79,8 @@
}
public final class ComposeKt {
- method @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
- method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
+ method @Deprecated @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+ method @Deprecated public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
}
public final class ComposeNodeKt {
@@ -597,7 +597,7 @@
public final class ComposableLambdaKt {
method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambda(androidx.compose.runtime.Composer composer, int key, boolean tracked, String? sourceInformation, Object block);
- method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, Object block);
+ method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, String? sourceInformation, Object block);
}
@androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.Stable public final class ComposableLambdaN<R> implements kotlin.jvm.functions.FunctionN<R> {
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 07e1499..fe8de7f 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -80,8 +80,8 @@
}
public final class ComposeKt {
- method @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
- method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
+ method @Deprecated @androidx.compose.runtime.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+ method @Deprecated public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
}
public final class ComposeNodeKt {
@@ -624,7 +624,7 @@
public final class ComposableLambdaKt {
method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambda(androidx.compose.runtime.Composer composer, int key, boolean tracked, String? sourceInformation, Object block);
- method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, Object block);
+ method @androidx.compose.runtime.ComposeCompilerApi public static androidx.compose.runtime.internal.ComposableLambda<java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object> composableLambdaInstance(int key, boolean tracked, String? sourceInformation, Object block);
}
@androidx.compose.runtime.ComposeCompilerApi @androidx.compose.runtime.Stable public final class ComposableLambdaN<R> implements kotlin.jvm.functions.FunctionN<R> {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt
index c042129..68f5550 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt
@@ -24,10 +24,18 @@
* See [orEmpty] for handling nullable Composable lambdas using empty content.
*/
@Stable
+@Deprecated(
+ "An empty lambda literal is preferred",
+ replaceWith = ReplaceWith("{}")
+)
fun emptyContent() = EmptyComposable
/**
* @return this Composable if not null, else [emptyContent].
*/
-@Suppress("NOTHING_TO_INLINE")
+@Suppress("NOTHING_TO_INLINE", "DEPRECATION")
+@Deprecated(
+ "Use the null coalescing operator instead",
+ replaceWith = ReplaceWith("this ?: {}")
+)
inline fun (@Composable (() -> Unit))?.orEmpty() = this ?: emptyContent()
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 b7b078a..8435ff1 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
@@ -21,6 +21,7 @@
)
package androidx.compose.runtime
+import androidx.compose.runtime.collection.IdentityScopeMap
import androidx.compose.runtime.tooling.InspectionTables
import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.persistentHashMapOf
@@ -959,8 +960,8 @@
private var collectKeySources = false
private var collectParameterInformation = false
private var nodeExpected = false
- private val observations: MutableList<Any> = mutableListOf()
- private val observationsProcessed: MutableList<Any> = mutableListOf()
+ private val observations = IdentityScopeMap<RecomposeScopeImpl>()
+ private val observationsProcessed = IdentityScopeMap<RecomposeScopeImpl>()
private val invalidations: MutableList<Invalidation> = mutableListOf()
internal var pendingInvalidScopes = false
private val entersStack = IntStack()
@@ -1231,7 +1232,7 @@
if (childrenComposing == 0) {
currentRecomposeScope?.let {
it.used = true
- observations.insertIfMissing(value, it)
+ observations.add(value, it)
}
}
}
@@ -1248,7 +1249,7 @@
observations.forEachScopeOf(value) { scope ->
if (scope.invalidateForResult() == InvalidationResult.IMMINENT) {
// If we process this during recordWriteOf, ignore it when recording modifications
- observationsProcessed.insertIfMissing(value, scope)
+ observationsProcessed.add(value, scope)
}
}
}
@@ -1287,7 +1288,7 @@
var invalidated: HashSet<RecomposeScopeImpl>? = null
for (value in values) {
observations.forEachScopeOf(value) { scope ->
- if (!observationsProcessed.removeValueScope(value, scope) &&
+ if (!observationsProcessed.remove(value, scope) &&
scope.invalidateForResult() != InvalidationResult.IGNORED
) {
(
@@ -1301,7 +1302,7 @@
}
}
invalidated?.let {
- observations.removeValueIf { _, scope -> scope in it }
+ observations.removeValueIf { scope -> scope in it }
}
}
@@ -1429,7 +1430,7 @@
if (pendingInvalidScopes) {
pendingInvalidScopes = false
- observations.removeValueIf { _, scope -> !scope.valid }
+ observations.removeValueIf { scope -> !scope.valid }
}
} finally {
manager.dispatchAbandons()
@@ -3377,55 +3378,6 @@
return false
}
-private inline fun MutableList<Any>.removeValueIf(
- predicate: (value: Any, scope: RecomposeScopeImpl) -> Boolean
-) {
- var copyLocation = 0
- for (index in 0 until size / 2) {
- val slot = index * 2
- val value = get(slot)
- val scope = get(slot + 1) as RecomposeScopeImpl
- if (!predicate(value, scope)) {
- if (copyLocation != slot) {
- // Keep the value by copying over a value that has been moved or removed.
- set(copyLocation++, value)
- set(copyLocation++, scope)
- } else {
- // No slots have been removed yet, just update the copy location
- copyLocation += 2
- }
- }
- }
- if (copyLocation < size) {
- // Delete any left-over slots.
- subList(copyLocation, size).clear()
- }
-}
-
-/**
- * Iterate through all the scopes associated with [value]. Returns `false` if [value] has no scopes
- * associated with it.
- */
-private inline fun MutableList<Any>.forEachScopeOf(
- value: Any,
- block: (scope: RecomposeScopeImpl) -> Unit
-): Boolean {
- val valueHash = identityHashCode(value)
- var index = findFirst(valueHash)
- var result = false
- while (index < size) {
- val storedValue = get(index)
- if (identityHashCode(storedValue) != valueHash) break
- if (storedValue === value) {
- val storedScope = get(index + 1) as RecomposeScopeImpl
- block(storedScope)
- result = true
- }
- index += 2
- }
- return result
-}
-
private fun MutableList<Any>.find(value: Any, scope: RecomposeScopeImpl): Int {
val valueHash = identityHashCode(value)
val scopeHash = identityHashCode(scope)
@@ -3445,9 +3397,6 @@
return -(index + 1)
}
-private fun MutableList<Any>.findFirst(valueHash: Int) =
- find(valueHash, 0).let { if (it < 0) -(it + 1) else it }
-
private fun MutableList<Any>.find(valueHash: Int, scopeHash: Int): Int {
var low = 0
var high = (size / 2) - 1
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 2d3cbee..7152d2a 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
@@ -258,7 +258,7 @@
private var disposed = false
- var composable: @Composable () -> Unit = emptyContent()
+ var composable: @Composable () -> Unit = {}
override val isComposing: Boolean
get() = composer.isComposing
@@ -282,7 +282,7 @@
override fun dispose() {
if (!disposed) {
disposed = true
- composable = emptyContent()
+ composable = {}
composer.dispose()
parent.unregisterComposition(this)
onDispose?.invoke()
@@ -370,7 +370,7 @@
state.clear()
val holders = Compositions.collectAll()
holders.mapTo(state) { it to it.composable }
- holders.filter { it.isRoot }.forEach { it.setContent(emptyContent()) }
+ holders.filter { it.isRoot }.forEach { it.setContent({}) }
}
// Called after Dex Code Swap
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
index 5e564eb..eca6421 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
@@ -107,7 +107,7 @@
/**
* Remove [value] from the set.
*/
- fun remove(value: T) {
+ fun remove(value: T): Boolean {
val index = find(value)
if (index >= 0) {
if (index < size - 1) {
@@ -120,7 +120,9 @@
}
size--
values[size] = null
+ return true
}
+ return false
}
/**
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
index 6c8ec16..8a9cec8 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
@@ -174,10 +174,44 @@
}
/**
+ * Remove [scope] from the scope set for [value]. If the scope set is empty after [scope] has
+ * been remove the reference to [value] is removed as well.
+ *
+ * @param value the key of the scope map
+ * @param scope the scope being removed
+ * @return true if the value was removed from the scope
+ */
+ fun remove(value: Any, scope: T): Boolean {
+ val index = find(value)
+ if (index >= 0) {
+ val valueOrderIndex = valueOrder[index]
+ val set = scopeSets[valueOrderIndex] ?: return false
+ val removed = set.remove(scope)
+ if (set.size == 0) {
+ val startIndex = index + 1
+ val endIndex = size
+ if (startIndex < endIndex) {
+ valueOrder.copyInto(
+ destination = valueOrder,
+ destinationOffset = index,
+ startIndex = startIndex,
+ endIndex = endIndex
+ )
+ }
+ valueOrder[size - 1] = valueOrderIndex
+ values[valueOrderIndex] = null
+ size--
+ }
+ return removed
+ }
+ return false
+ }
+
+ /**
* Removes all scopes that match [predicate]. If all scopes for a given value have been
* removed, that value is removed also.
*/
- inline fun removeValueIf(predicate: (scope: Any) -> Boolean) {
+ inline fun removeValueIf(predicate: (scope: T) -> Boolean) {
var destinationIndex = 0
for (i in 0 until size) {
val valueIndex = valueOrder[i]
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt
index 562802f..d1583aa 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt
@@ -1210,5 +1210,5 @@
@Suppress("unused")
@ComposeCompilerApi
-fun composableLambdaInstance(key: Int, tracked: Boolean, block: Any) =
- CLambda(key, tracked, null).apply { update(block) }
+fun composableLambdaInstance(key: Int, tracked: Boolean, sourceInformation: String?, block: Any) =
+ CLambda(key, tracked, sourceInformation).apply { update(block) }
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
index 6984a2d..8397eda 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
@@ -19,6 +19,7 @@
import androidx.compose.runtime.identityHashCode
import kotlin.test.Test
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertNotSame
import kotlin.test.assertNull
@@ -89,8 +90,9 @@
list.forEach { set.add(it) }
// remove a value that doesn't exist:
- set.remove(Stuff(10))
+ val removed = set.remove(Stuff(10))
assertEquals(list.size, set.size)
+ assertFalse(removed)
// remove a value in the middle:
testRemoveValueAtIndex(set.size / 2)
@@ -131,8 +133,9 @@
private fun testRemoveValueAtIndex(index: Int) {
val value = set[index]
val initialSize = set.size
- set.remove(value)
+ val removed = set.remove(value)
assertEquals(initialSize - 1, set.size)
+ assertTrue(removed)
assertNull(set.values[set.size])
set.forEach { assertNotSame(value, it) }
}
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
index 4e451ad..391a571 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/IdentityScopeMapTest.kt
@@ -16,8 +16,12 @@
package androidx.compose.runtime.collection
+import org.junit.After
import kotlin.test.Test
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.test.fail
@@ -84,6 +88,25 @@
}
@Test
+ fun removeScope() {
+ val valueC = "C"
+ map.add(valueList[0], scopeList[0])
+ map.add(valueList[0], scopeList[1])
+ map.add(valueList[1], scopeList[2])
+ map.add(valueC, scopeList[3])
+
+ // remove a non existent value, should leave the map unmodified
+ val removed1 = map.remove(valueC, scopeList[0])
+ assertFalse(removed1)
+ assertEquals(3, map.size)
+
+ // Remove a reference which should remove the value
+ val removed2 = map.remove(valueList[1], scopeList[2])
+ assertTrue(removed2)
+ assertEquals(2, map.size)
+ }
+
+ @Test
fun removeValueIf() {
val valueC = "C"
map.add(valueList[0], scopeList[0])
@@ -109,5 +132,38 @@
}
}
+ /**
+ * Validate the test maintains the internal assumptions of the map.
+ */
+ @After
+ fun validateMap() {
+ // Ensure that no duplicates exist in value-order and all indexes are represented
+ val pendingRepresentation = mutableSetOf(*map.values.indices.toList().toTypedArray())
+ map.valueOrder.forEach {
+ assertTrue(it in pendingRepresentation, "Index $it was duplicated")
+ pendingRepresentation.remove(it)
+ }
+ assertTrue(pendingRepresentation.isEmpty(), "Not all indexes are in the valueOrder map")
+
+ // Ensure values are non-null and sets are not empty for index < size and values are
+ // null and sets are empty or missing for >= size
+ val size = map.size
+ map.valueOrder.forEachIndexed { index, order ->
+ val value = map.values[order]
+ val set = map.scopeSets[order]
+ if (index < size) {
+ assertNotNull(value, "A value was unexpectedly null")
+ assertNotNull(set, "A set was unexpectedly null")
+ assertTrue(set.size > 0, "An empty set wasn't collected")
+ } else {
+ assertNull(value, "A reference to a removed value was retained")
+ assertTrue(
+ actual = set == null || set.size == 0,
+ message = "A non-empty set was dropped"
+ )
+ }
+ }
+ }
+
data class Scope(val item: Int)
}
\ No newline at end of file
diff --git a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt
index 59e82e4..6aae00c 100644
--- a/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt
+++ b/compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/Rects.kt
@@ -24,7 +24,7 @@
top,
right,
bottom
- )!!
+ )
}
fun org.jetbrains.skija.Rect.toComposeRect() =
diff --git a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
index 462a2ee..bf5d4bf 100644
--- a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
+++ b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.kt
@@ -164,7 +164,7 @@
}
private fun performSetContent(composable: @Composable() () -> Unit) {
- val surface = Surface.makeRasterN32Premul(testDisplaySize.width, testDisplaySize.height)!!
+ val surface = Surface.makeRasterN32Premul(testDisplaySize.width, testDisplaySize.height)
val canvas = surface.canvas
val owners = DesktopOwners(invalidate = {}).also {
owners = it
diff --git a/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/TestComposeWindow.kt b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
index 1f05602..3c47455 100644
--- a/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
+++ b/compose/ui/ui-test/src/desktopMain/kotlin/androidx/compose/ui/test/TestComposeWindow.kt
@@ -32,7 +32,7 @@
val density: Density = Density(1f, 1f),
var desktopPlatform: DesktopPlatform = DesktopPlatform.Linux
) {
- val surface = Surface.makeRasterN32Premul(width, height)!!
+ val surface = Surface.makeRasterN32Premul(width, height)
val canvas = surface.canvas
val owners = DesktopOwners(invalidate = {})
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt
index b4a14e6..aec5e08 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/BoundsTest.kt
@@ -124,7 +124,7 @@
}
}.flatten().groupBy { it.location }
- Assert.assertTrue(boundingBoxes.size >= 6)
+ Assert.assertTrue(boundingBoxes.size >= 4)
}
}
diff --git a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt
index e096248..1bdff85 100644
--- a/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt
+++ b/compose/ui/ui-tooling/src/androidTest/java/androidx/compose/ui/tooling/inspector/ParameterFactoryTest.kt
@@ -239,7 +239,9 @@
@Test
fun testComposableLambda() = runBlocking {
- val c: @Composable () -> Unit = { Text(text = "Hello World") }
+ // capture here to force the lambda to not be created as a singleton.
+ val capture = "Hello World"
+ val c: @Composable () -> Unit = { Text(text = capture) }
val result = lookup(c as Any) ?: error("Lookup of ComposableLambda failed")
val array = result.second as Array<*>
assertThat(result.first).isEqualTo(ParameterType.Lambda)
diff --git a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt
index 23d854c..92237c6 100644
--- a/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt
+++ b/compose/ui/ui-tooling/src/main/java/androidx/compose/ui/tooling/preview/ComposeViewAdapter.kt
@@ -33,7 +33,6 @@
import androidx.compose.runtime.Providers
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.currentComposer
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.graphics.Color
@@ -60,6 +59,8 @@
const val TOOLS_NS_URI = "http://schemas.android.com/tools"
+private val emptyContent: @Composable () -> Unit = @Composable {}
+
/**
* Class containing the minimum information needed by the Preview to map components to the
* source code and render boundaries.
@@ -146,14 +147,14 @@
* The [Composable] to be rendered in the preview. It is initialized when this adapter
* is initialized.
*/
- private var previewComposition: @Composable () -> Unit = emptyContent()
+ private var previewComposition: @Composable () -> Unit = {}
- // Note: the call to emptyContent() below instead of a literal {} works around
+ // Note: the constant emptyContent below instead of a literal {} works around
// https://youtrack.jetbrains.com/issue/KT-17467, which causes the compiler to emit classes
// named `content` and `Content` (from the Content method's composable update scope)
// which causes compilation problems on case-insensitive filesystems.
@Suppress("RemoveExplicitTypeArguments")
- private val content = mutableStateOf<@Composable () -> Unit>(emptyContent())
+ private val content = mutableStateOf<@Composable () -> Unit>(emptyContent)
/**
* When true, the composition will be immediately invalidated after being drawn. This will
@@ -334,7 +335,7 @@
private fun invalidateComposition() {
// Invalidate the full composition by setting it to empty and back to the actual value
- content.value = emptyContent()
+ content.value = {}
content.value = previewComposition
// Invalidate the state of the view so it gets redrawn
invalidate()
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt
index f0834bc..127a50b 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/NestedPressDemo.kt
@@ -26,7 +26,6 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -62,7 +61,7 @@
@Composable
private fun PressableContainer(
modifier: Modifier = Modifier,
- content: @Composable () -> Unit = emptyContent()
+ content: @Composable () -> Unit = {}
) {
val defaultColor = DefaultBackgroundColor
val pressedColor = PressedColor
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
index 29765cf..e63b42f 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
@@ -25,7 +25,6 @@
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -65,7 +64,7 @@
orientation: Orientation,
activeColor: Color,
idleColor: Color,
- content: @Composable () -> Unit = emptyContent()
+ content: @Composable () -> Unit = {}
) {
val color = remember { mutableStateOf(idleColor) }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index 1a02253..4eca29f 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -41,7 +41,6 @@
import androidx.compose.runtime.Providers
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -534,7 +533,7 @@
assertEquals(childConstraints[0], constraints)
layout(0, 0) {}
},
- content = emptyContent(), modifier = Modifier.layoutId("header")
+ content = {}, modifier = Modifier.layoutId("header")
)
}
val footer = @Composable {
@@ -543,14 +542,14 @@
assertEquals(childConstraints[1], constraints)
layout(0, 0) {}
},
- content = emptyContent(), modifier = Modifier.layoutId("footer")
+ content = {}, modifier = Modifier.layoutId("footer")
)
Layout(
measureBlock = { _, constraints ->
assertEquals(childConstraints[2], constraints)
layout(0, 0) {}
},
- content = emptyContent(), modifier = Modifier.layoutId("footer")
+ content = {}, modifier = Modifier.layoutId("footer")
)
}
@@ -1812,7 +1811,7 @@
activity.setContent {
Layout(
content = {
- Layout(content = emptyContent()) { _, _ ->
+ Layout(content = {}) { _, _ ->
latch.countDown()
layout(model.size, model.size) {}
}
@@ -1844,7 +1843,7 @@
content = {
Layout(
modifier = Modifier.graphicsLayer(),
- content = emptyContent()
+ content = {}
) { _, _ ->
latch.countDown()
layout(model.size, model.size) {}
@@ -2379,7 +2378,7 @@
}
layout(100, 100) {}
}
- FixedSize(30, content = emptyContent())
+ FixedSize(30, content = {})
}
) { measurables, constraints ->
val (first, second) = measurables
@@ -3599,7 +3598,7 @@
fun AtLeastSize(
size: Int,
modifier: Modifier = Modifier,
- content: @Composable () -> Unit = emptyContent()
+ content: @Composable () -> Unit = {}
) {
Layout(
measureBlock = { measurables, constraints ->
@@ -3641,7 +3640,7 @@
fun FixedSize(
size: Int,
modifier: Modifier = Modifier,
- content: @Composable () -> Unit = emptyContent()
+ content: @Composable () -> Unit = {}
) {
Layout(content = content, modifier = modifier) { measurables, _ ->
val newConstraints = Constraints.fixed(size, size)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/OpenComposeView.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/OpenComposeView.kt
index 9f9e5a6..ece2f94 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/OpenComposeView.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/OpenComposeView.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.util.AttributeSet
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.AbstractComposeView
@@ -31,7 +30,7 @@
) : AbstractComposeView(context, attrs, defStyleAttr) {
@Suppress("RemoveExplicitTypeArguments")
- private val content = mutableStateOf<@Composable () -> Unit>(emptyContent())
+ private val content = mutableStateOf<@Composable () -> Unit>({})
@Composable
override fun Content() {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
index 20e1323..6eb6703 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/ParentDataModifierTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.ui
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
@@ -116,13 +115,13 @@
val header = @Composable {
Layout(
modifier = Modifier.layoutId(0),
- content = emptyContent()
+ content = {}
) { _, _ -> layout(0, 0) {} }
}
val footer = @Composable {
Layout(
modifier = Modifier.layoutId(1),
- content = emptyContent()
+ content = {}
) { _, _ -> layout(0, 0) {} }
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt
index 4cf87b1..7d21101 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/DrawReorderingTest.kt
@@ -22,7 +22,6 @@
import android.view.ViewTreeObserver
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -1046,7 +1045,7 @@
private fun FixedSize(
size: State<Int>,
modifier: Modifier = Modifier,
- content: @Composable () -> Unit = emptyContent()
+ content: @Composable () -> Unit = {}
) {
Layout(content = content, modifier = modifier) { measurables, _ ->
val newConstraints = Constraints.fixed(size.value, size.value)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt
index 9e12c1b..11f062a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/LongPressDragGestureFilterTest.kt
@@ -19,7 +19,6 @@
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.Layout
@@ -85,7 +84,7 @@
setupLatch.countDown()
}
},
- content = emptyContent()
+ content = {}
)
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
index 8a987ae..3ff7e74 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
@@ -23,7 +23,6 @@
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -591,7 +590,7 @@
@Suppress("TestFunctionName")
@Composable
private fun FillLayout(modifier: Modifier = Modifier) {
- Layout(emptyContent(), modifier) { _, constraints ->
+ Layout({}, modifier) { _, constraints ->
layout(constraints.maxWidth, constraints.maxHeight) {}
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/ClipPointerInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/ClipPointerInputTest.kt
index fa5a9be..3d6a2c6 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/ClipPointerInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/ClipPointerInputTest.kt
@@ -21,7 +21,6 @@
import android.view.ViewGroup
import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset
@@ -316,7 +315,7 @@
@Composable
fun Child(modifier: Modifier) {
- Layout(content = emptyContent(), modifier = modifier) { _, _ ->
+ Layout(content = {}, modifier = modifier) { _, _ ->
layout(2, 2) {}
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt
index 2579115..d492eac 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/LayerTouchTransformTest.kt
@@ -23,7 +23,6 @@
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.preferredSize
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -151,7 +150,7 @@
}
@Composable
-fun SimpleLayout(modifier: Modifier, content: @Composable () -> Unit = emptyContent()) {
+fun SimpleLayout(modifier: Modifier, content: @Composable () -> Unit = {}) {
Layout(
content,
modifier
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
index bac20cd..8d0b574 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
@@ -26,7 +26,6 @@
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -661,7 +660,7 @@
fun DelayedMeasure(
size: Int,
modifier: Modifier = Modifier,
- content: @Composable () -> Unit = emptyContent()
+ content: @Composable () -> Unit = {}
) {
Layout(content = content, modifier = modifier) { measurables, _ ->
layout(size, size) {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
index 06f92fe..7ea1393 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RtlLayoutTest.kt
@@ -22,7 +22,6 @@
import androidx.compose.foundation.layout.preferredWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Providers
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.FixedSize
import androidx.compose.ui.Modifier
@@ -261,7 +260,7 @@
Providers(AmbientLayoutDirection provides LayoutDirection.Rtl) {
Box {
Providers(AmbientLayoutDirection provides initialLayoutDirection) {
- Layout(emptyContent()) { _, _ ->
+ Layout({}) { _, _ ->
resultLayoutDirection.value = layoutDirection
latch.countDown()
layout(0, 0) {}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/LayoutIdTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/LayoutIdTest.kt
index 459bc94..f1311c7 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/LayoutIdTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/LayoutIdTest.kt
@@ -17,7 +17,6 @@
package androidx.compose.ui.platform
import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.AtLeastSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
@@ -63,15 +62,15 @@
activity.setContent {
Layout(
{
- AtLeastSize(0, Modifier.layoutId("first"), content = emptyContent())
+ AtLeastSize(0, Modifier.layoutId("first"), content = {})
Box(Modifier.layoutId("second")) {
AtLeastSize(
0,
- content = emptyContent()
+ content = {}
)
}
Box(Modifier.layoutId("third")) {
- AtLeastSize(0, content = emptyContent())
+ AtLeastSize(0, content = {})
}
}
) { measurables, _ ->
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
index 0a89378..0e2c070 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/DialogSecureFlagTest.kt
@@ -21,7 +21,6 @@
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.preferredSize
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -116,7 +115,7 @@
onDismissRequest = { },
properties = dialogProperties
) {
- SimpleContainer(Modifier.preferredSize(50.dp), content = emptyContent())
+ SimpleContainer(Modifier.preferredSize(50.dp), content = {})
}
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupAlignmentTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupAlignmentTest.kt
index 755f8fe..a0de7cc 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupAlignmentTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupAlignmentTest.kt
@@ -19,7 +19,6 @@
import android.view.View
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Providers
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
@@ -332,7 +331,7 @@
modifier = Modifier.onGloballyPositioned {
measureLatch.countDown()
},
- content = emptyContent()
+ content = {}
)
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupSecureFlagTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupSecureFlagTest.kt
index 8e4fdd6..95d7e63 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupSecureFlagTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupSecureFlagTest.kt
@@ -20,7 +20,6 @@
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.preferredSize
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -123,7 +122,7 @@
alignment = Alignment.Center,
properties = popupProperties
) {
- SimpleContainer(Modifier.preferredSize(50.dp), content = emptyContent())
+ SimpleContainer(Modifier.preferredSize(50.dp), content = {})
}
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupTest.kt
index 7568a99..207aed8 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PopupTest.kt
@@ -23,7 +23,6 @@
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Providers
import androidx.compose.runtime.ambientOf
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -81,7 +80,7 @@
SimpleContainer {
PopupTestTag(testTag) {
Popup(alignment = Alignment.Center) {
- SimpleContainer(Modifier.preferredSize(50.dp), content = emptyContent())
+ SimpleContainer(Modifier.preferredSize(50.dp), content = {})
}
}
}
@@ -106,7 +105,7 @@
SimpleContainer(
width = popupWidthDp,
height = popupHeightDp,
- content = emptyContent()
+ content = {}
)
}
}
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 ad973be..988c6f0 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
@@ -31,7 +31,6 @@
import androidx.compose.runtime.Providers
import androidx.compose.runtime.Recomposer
import androidx.compose.runtime.currentComposer
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.tooling.InspectionTables
import androidx.compose.ui.R
import androidx.compose.ui.node.LayoutNode
@@ -166,7 +165,7 @@
private var disposed = false
private var addedToLifecycle: Lifecycle? = null
- private var lastContent: @Composable () -> Unit = emptyContent()
+ private var lastContent: @Composable () -> Unit = {}
@OptIn(InternalComposeApi::class)
override fun setContent(content: @Composable () -> Unit) {
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 ceb4cf9..6779455 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
@@ -31,7 +31,6 @@
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -161,7 +160,7 @@
override val window: Window
) : AbstractComposeView(context), DialogWindowProvider {
- private var content: @Composable () -> Unit by mutableStateOf(emptyContent())
+ private var content: @Composable () -> Unit by mutableStateOf({})
protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
private set
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 a600e95..050c347 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
@@ -33,7 +33,6 @@
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -176,7 +175,7 @@
// used instead of this custom Layout
// Get the parent's position, size and layout direction
Layout(
- content = emptyContent(),
+ content = {},
modifier = Modifier.onGloballyPositioned { childCoordinates ->
val coordinates = childCoordinates.parentLayoutCoordinates!!
val layoutSize = coordinates.size
@@ -289,7 +288,7 @@
windowManager.addView(this, params)
}
- private var content: @Composable () -> Unit by mutableStateOf(emptyContent())
+ private var content: @Composable () -> Unit by mutableStateOf({})
override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
private set
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 73c2797..d2777f9 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
@@ -24,7 +24,6 @@
import androidx.compose.runtime.RememberObserver
import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.ComposeNode
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCompositionReference
import androidx.compose.ui.Modifier
@@ -148,7 +147,7 @@
currentIndex++
val nodeState = nodeToNodeState.getOrPut(node) {
- NodeState(slotId, emptyContent())
+ NodeState(slotId, {})
}
val hasPendingChanges = nodeState.composition?.hasInvalidations ?: true
if (nodeState.content !== content || hasPendingChanges) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionHandles.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionHandles.kt
index 5a98035..67ff734 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionHandles.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/selection/SelectionHandles.kt
@@ -17,7 +17,6 @@
package androidx.compose.ui.selection
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.emptyContent
import androidx.compose.runtime.remember
import androidx.compose.ui.AbsoluteAlignment
import androidx.compose.ui.Alignment
@@ -187,7 +186,7 @@
height: Dp,
onCanvas: DrawScope.() -> Unit
) {
- Layout(emptyContent(), modifier.drawBehind(onCanvas)) { _, _ ->
+ Layout({}, modifier.drawBehind(onCanvas)) { _, _ ->
// take width and height space of the screen and allow draw modifier to draw inside of it
layout(width.toIntPx(), height.toIntPx()) {
// this layout has no children, only draw modifier.
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt
index 210fa44..a7adf1f 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/AppWindow.kt
@@ -19,7 +19,6 @@
import androidx.compose.runtime.CompositionReference
import androidx.compose.runtime.Providers
import androidx.compose.runtime.ambientOf
-import androidx.compose.runtime.emptyContent
import androidx.compose.ui.platform.Keyboard
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
@@ -69,7 +68,7 @@
resizable: Boolean = true,
events: WindowEvents = WindowEvents(),
onDismissRequest: (() -> Unit)? = null,
- content: @Composable () -> Unit = emptyContent()
+ content: @Composable () -> Unit = { }
) = SwingUtilities.invokeLater {
AppWindow(
title = title,
@@ -429,7 +428,7 @@
}
onCreate(parentComposition) {
- window.layer.owners?.keyboard = keyboard
+ window.layer.owners.keyboard = keyboard
content()
}
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt
index ff98afd..43f7cdc 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.kt
@@ -25,17 +25,12 @@
import androidx.compose.ui.platform.DesktopComponent
import androidx.compose.ui.platform.DesktopOwner
import androidx.compose.ui.platform.DesktopOwners
-import androidx.compose.ui.platform.FrameDispatcher
import androidx.compose.ui.platform.setContent
import androidx.compose.ui.unit.Density
import org.jetbrains.skija.Canvas
-import org.jetbrains.skija.Picture
-import org.jetbrains.skija.PictureRecorder
-import org.jetbrains.skija.Rect
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaRenderer
-import java.awt.DisplayMode
import java.awt.Point
import java.awt.event.FocusEvent
import java.awt.event.InputMethodEvent
@@ -49,49 +44,47 @@
import java.awt.im.InputMethodRequests
internal class ComposeLayer {
+ private var isDisposed = false
- private var composition: Composition? = null
private val events = AWTDebounceEventQueue()
- var owners: DesktopOwners? = null
- set(value) {
- field = value
- renderer = value?.let(::OwnersRenderer)
- }
-
- var renderer: Renderer? = null
-
- private var isDisposed = false
- private var frameNanoTime = 0L
- private val frameDispatcher = FrameDispatcher(
- onFrame = { onFrame(it) },
- framesPerSecond = ::getFramesPerSecond
+ internal val wrapped = Wrapped()
+ internal val owners: DesktopOwners = DesktopOwners(
+ wrapped,
+ wrapped::needRedraw
)
- private val picture = MutableResource<Picture>()
- private val pictureRecorder = PictureRecorder()
+ private var owner: DesktopOwner? = null
+ private var composition: Composition? = null
- private suspend fun onFrame(nanoTime: Long) {
- this.frameNanoTime = nanoTime
- preparePicture(frameNanoTime)
- wrapped.redrawLayer()
- }
+ private var content: (@Composable () -> Unit)? = null
+ private var parentComposition: CompositionReference? = null
- var onDensityChanged: ((Density) -> Unit)? = null
-
- fun onDensityChanged(action: ((Density) -> Unit)?) {
- onDensityChanged = action
- }
-
- private var _density: Density? = null
- val density
- get() = _density ?: detectCurrentDensity().also {
- _density = it
- }
+ private lateinit var density: Density
inner class Wrapped : SkiaLayer(), DesktopComponent {
var currentInputMethodRequests: InputMethodRequests? = null
+ var isInit = false
+ private set
+
+ override fun init() {
+ super.init()
+ isInit = true
+ resetDensity()
+ initOwner()
+ }
+
+ override fun contentScaleChanged() {
+ super.contentScaleChanged()
+ resetDensity()
+ }
+
+ private fun resetDensity() {
+ this@ComposeLayer.density = detectCurrentDensity()
+ owner?.density = density
+ }
+
override fun getInputMethodRequests() = currentInputMethodRequests
override fun enableInput(inputMethodRequests: InputMethodRequests) {
@@ -110,33 +103,22 @@
override val density: Density
get() = this@ComposeLayer.density
-
- override fun scaleCanvas(dpi: Float) {}
}
- internal val wrapped = Wrapped()
-
val component: HardwareLayer
get() = wrapped
init {
wrapped.renderer = object : SkiaRenderer {
- override fun onRender(canvas: Canvas, width: Int, height: Int) {
+ override suspend fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
try {
- picture.useWithoutClosing {
- it?.also(canvas::drawPicture)
- }
+ owners.onFrame(canvas, width, height, nanoTime)
} catch (e: Throwable) {
- e.printStackTrace(System.err)
if (System.getProperty("compose.desktop.render.ignore.errors") == null) {
- System.exit(1)
+ throw e
}
}
}
-
- override fun onDispose() = Unit
- override fun onInit() = Unit
- override fun onReshape(width: Int, height: Int) = Unit
}
initCanvas()
}
@@ -145,12 +127,12 @@
wrapped.addInputMethodListener(object : InputMethodListener {
override fun caretPositionChanged(event: InputMethodEvent?) {
if (event != null) {
- owners?.onInputMethodEvent(event)
+ owners.onInputMethodEvent(event)
}
}
override fun inputMethodTextChanged(event: InputMethodEvent) = events.post {
- owners?.onInputMethodEvent(event)
+ owners.onInputMethodEvent(event)
}
})
@@ -158,7 +140,7 @@
override fun mouseClicked(event: MouseEvent) = Unit
override fun mousePressed(event: MouseEvent) = events.post {
- owners?.onMousePressed(
+ owners.onMousePressed(
(event.x * density.density).toInt(),
(event.y * density.density).toInt(),
event
@@ -166,7 +148,7 @@
}
override fun mouseReleased(event: MouseEvent) = events.post {
- owners?.onMouseReleased(
+ owners.onMouseReleased(
(event.x * density.density).toInt(),
(event.y * density.density).toInt(),
event
@@ -175,7 +157,7 @@
})
wrapped.addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseDragged(event: MouseEvent) = events.post {
- owners?.onMouseDragged(
+ owners.onMouseDragged(
(event.x * density.density).toInt(),
(event.y * density.density).toInt(),
event
@@ -183,7 +165,7 @@
}
override fun mouseMoved(event: MouseEvent) = events.post {
- owners?.onMouseMoved(
+ owners.onMouseMoved(
(event.x * density.density).toInt(),
(event.y * density.density).toInt()
)
@@ -191,7 +173,7 @@
})
wrapped.addMouseWheelListener { event ->
events.post {
- owners?.onMouseScroll(
+ owners.onMouseScroll(
(event.x * density.density).toInt(),
(event.y * density.density).toInt(),
event.toComposeEvent()
@@ -200,25 +182,19 @@
}
wrapped.addKeyListener(object : KeyAdapter() {
override fun keyPressed(event: KeyEvent) = events.post {
- owners?.onKeyPressed(event)
+ owners.onKeyPressed(event)
}
override fun keyReleased(event: KeyEvent) = events.post {
- owners?.onKeyReleased(event)
+ owners.onKeyReleased(event)
}
override fun keyTyped(event: KeyEvent) = events.post {
- owners?.onKeyTyped(event)
+ owners.onKeyTyped(event)
}
})
}
- private class OwnersRenderer(private val owners: DesktopOwners) : ComposeLayer.Renderer {
- override suspend fun onFrame(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
- owners.onFrame(canvas, width, height, nanoTime)
- }
- }
-
private fun MouseWheelEvent.toComposeEvent() = MouseScrollEvent(
delta = if (scrollType == MouseWheelEvent.WHEEL_BLOCK_SCROLL) {
MouseScrollUnit.Page((scrollAmount * preciseWheelRotation).toFloat())
@@ -234,90 +210,41 @@
}
)
- // We draw into picture, because SkiaLayer.draw can be called from the other thread,
- // but onRender should be called in AWT thread. Picture doesn't add any visible overhead on
- // CPU/RAM.
- private suspend fun preparePicture(frameTimeNanos: Long) {
- val bounds = Rect.makeWH(wrapped.width * density.density, wrapped.height * density.density)
- val pictureCanvas = pictureRecorder.beginRecording(bounds)
- renderer?.onFrame(
- pictureCanvas,
- (wrapped.width * density.density).toInt(),
- (wrapped.height * density.density).toInt(),
- frameTimeNanos
- )
- picture.set(pictureRecorder.finishRecordingAsPicture())
- }
-
- fun reinit() {
- val currentDensity = detectCurrentDensity()
- if (_density != currentDensity) {
- _density = currentDensity
- onDensityChanged?.invoke(density)
- }
- check(!isDisposed)
- wrapped.reinit()
- }
-
// TODO(demin): detect OS fontScale
// font size can be changed on Windows 10 in Settings - Ease of Access,
// on Ubuntu in Settings - Universal Access
// on macOS there is no such setting
private fun detectCurrentDensity(): Density {
- val density = wrapped.graphicsConfiguration.defaultTransform.scaleX.toFloat()
- return Density(density, 1f)
- }
-
- private fun getFramesPerSecond(): Float {
- val refreshRate = wrapped.graphicsConfiguration.device.displayMode.refreshRate
- return if (refreshRate != DisplayMode.REFRESH_RATE_UNKNOWN) refreshRate.toFloat() else 60f
- }
-
- fun updateLayer() {
- check(!isDisposed)
- wrapped.updateLayer()
+ return Density(wrapped.contentScale, 1f)
}
fun dispose() {
- composition?.dispose()
- events.cancel()
check(!isDisposed)
- frameDispatcher.cancel()
- wrapped.disposeLayer()
- picture.close()
- pictureRecorder.close()
+ composition?.dispose()
+ owner?.dispose()
+ events.cancel()
+ wrapped.dispose()
isDisposed = true
}
- internal fun needRedrawLayer() {
- check(!isDisposed)
- frameDispatcher.scheduleFrame()
- }
-
- interface Renderer {
- suspend fun onFrame(canvas: Canvas, width: Int, height: Int, nanoTime: Long)
- }
-
internal fun setContent(
- parent: Any? = null,
- invalidate: () -> Unit = this::needRedrawLayer,
parentComposition: CompositionReference? = null,
content: @Composable () -> Unit
) {
- check(owners == null) {
- "Cannot setContent twice."
- }
- val desktopOwners = DesktopOwners(wrapped, invalidate)
- val desktopOwner = DesktopOwner(desktopOwners, density)
+ check(!isDisposed)
+ check(this.content == null) { "Cannot set content twice" }
+ this.content = content
+ this.parentComposition = parentComposition
+ // We can't create DesktopOwner now, because we don't know density yet.
+ // We will know density only after SkiaLayer will be visible.
+ initOwner()
+ }
- owners = desktopOwners
- composition = desktopOwner.setContent(parent = parentComposition, content = content)
-
- onDensityChanged(desktopOwner::density::set)
-
- when (parent) {
- is AppFrame -> parent.onDispose = desktopOwner::dispose
- is ComposePanel -> parent.onDispose = desktopOwner::dispose
+ private fun initOwner() {
+ check(!isDisposed)
+ if (wrapped.isInit && owner == null && content != null) {
+ owner = DesktopOwner(owners, density)
+ composition = owner!!.setContent(parent = parentComposition, content = content!!)
}
}
}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.kt
index 57d1f10..bf993ee 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.kt
@@ -16,23 +16,23 @@
package androidx.compose.desktop
import androidx.compose.runtime.Composable
-import androidx.compose.ui.unit.Density
-import java.awt.Graphics
import java.awt.GridLayout
-import java.awt.event.ComponentAdapter
-import java.awt.event.ComponentEvent
import javax.swing.JPanel
+import javax.swing.SwingUtilities.isEventDispatchThread
/**
* ComposePanel is panel for building UI using Compose for Desktop.
*/
-class ComposePanel : JPanel {
- constructor() : super() {
- setLayout(GridLayout(1, 1))
+class ComposePanel : JPanel() {
+ init {
+ check(isEventDispatchThread()) {
+ "ComposePanel should be created inside AWT Event Dispatch Thread" +
+ " (use SwingUtilities.invokeLater).\n" +
+ "Creating from another thread isn't supported."
+ }
+ layout = GridLayout(1, 1)
}
- private var init: Boolean = false
-
private var layer: ComposeLayer? = null
private var content: (@Composable () -> Unit)? = null
@@ -53,69 +53,34 @@
private fun initContent() {
if (layer != null && content != null) {
layer!!.setContent(
- parent = this,
- invalidate = this::needRedrawLayer,
content = content!!
)
}
}
- val density: Density
- get() = if (layer == null) {
- Density(graphicsConfiguration.defaultTransform.scaleX.toFloat(), 1f)
- } else {
- layer!!.density
- }
-
- internal var onDispose: (() -> Unit)? = null
-
- private fun needRedrawLayer() {
- if (isShowing) {
- if (!init) {
- layer!!.updateLayer()
- init = true
- }
- layer!!.needRedrawLayer()
- }
- }
-
override fun addNotify() {
super.addNotify()
// After [super.addNotify] is called we can safely initialize the layer and composable
// content.
layer = ComposeLayer()
- add(layer!!.wrapped)
- addComponentListener(object : ComponentAdapter() {
- override fun componentResized(e: ComponentEvent) {
- layer?.reinit()
- needRedrawLayer()
- }
- })
+ add(layer!!.component)
initContent()
}
override fun removeNotify() {
- super.removeNotify()
-
- onDispose?.invoke()
if (layer != null) {
- remove(layer!!.wrapped)
layer!!.dispose()
+ remove(layer!!.component)
}
- init = false
+
+ super.removeNotify()
}
override fun requestFocus() {
if (layer != null) {
- layer!!.wrapped.requestFocus()
+ layer!!.component.requestFocus()
}
}
-
- override fun paint(g: Graphics?) {
- super.paint(g)
- layer?.reinit()
- needRedrawLayer()
- }
}
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
index 677caf8..8f21b60 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.kt
@@ -17,26 +17,13 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionReference
-import java.awt.event.ComponentAdapter
-import java.awt.event.ComponentEvent
import javax.swing.JFrame
-class ComposeWindow : JFrame {
- val parent: AppFrame
+class ComposeWindow(val parent: AppFrame) : JFrame() {
internal val layer = ComposeLayer()
- val density get() = layer.density
-
- constructor(parent: AppFrame) : super() {
- this.parent = parent
- contentPane.add(layer.wrapped)
-
- addComponentListener(object : ComponentAdapter() {
- override fun componentResized(e: ComponentEvent) {
- layer.reinit()
- needRedrawLayer()
- }
- })
+ init {
+ contentPane.add(layer.component)
}
/**
@@ -52,27 +39,11 @@
content: @Composable () -> Unit
) {
layer.setContent(
- parent = parent,
- invalidate = this::needRedrawLayer,
parentComposition = parentComposition,
content = content
)
}
- private fun updateLayer() {
- if (!isVisible) {
- return
- }
- layer.updateLayer()
- }
-
- internal fun needRedrawLayer() {
- if (!isVisible) {
- return
- }
- layer.needRedrawLayer()
- }
-
override fun dispose() {
layer.dispose()
super.dispose()
@@ -81,24 +52,7 @@
override fun setVisible(value: Boolean) {
if (value != isVisible) {
super.setVisible(value)
- layer.wrapped.requestFocus()
- updateLayer()
- needRedrawLayer()
+ layer.component.requestFocus()
}
}
}
-
-// Simple FPS tracker for debug purposes
-internal class FPSTracker {
- private var t0 = 0L
- private val times = DoubleArray(155)
- private var timesIdx = 0
-
- fun track() {
- val t1 = System.nanoTime()
- times[timesIdx] = (t1 - t0) / 1000000.0
- t0 = t1
- timesIdx = (timesIdx + 1) % times.size
- println("FPS: ${1000 / times.takeWhile { it > 0 }.average()}")
- }
-}
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/MutableResource.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/MutableResource.kt
deleted file mode 100644
index 6567d9e..0000000
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/MutableResource.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.desktop
-
-/**
- * Resource that can be used multiple times in parallel threads.
- */
-internal class MutableResource<T : AutoCloseable> : AutoCloseable {
- @Volatile
- private var resource: T? = null
- @Volatile
- private var resourceToClose: T? = null
- @Volatile
- private var usingResource: T? = null
-
- /**
- * Close internal resource.
- *
- * If we close resource when it is using in [useWithoutClosing], we defer it's closing.
- *
- * If resource isn't using we close it immediately.
- *
- * After close we can use [set] (for set new internal resource)
- * and [useWithoutClosing] (it will be called with null resource)
- */
- override fun close() = set(null)
-
- /**
- * Change internal resource and close previous.
- *
- * If we set resource when previous is using in [useWithoutClosing], we defer it's closing.
- *
- * If previous isn't using we close it immediately.
- */
- fun set(resource: T?): Unit = synchronized(this) {
- val oldResource = this.resource
- this.resource = resource
- if (oldResource === usingResource) {
- resourceToClose = oldResource
- } else {
- oldResource?.close()
- }
- }
-
- /**
- * Can be called from the other thread.
- *
- * If we [set] new resource when we using previous, we close previous after using.
- */
- fun useWithoutClosing(use: (T?) -> Unit) {
- synchronized(this) {
- usingResource = resource
- }
- try {
- use(usingResource)
- } finally {
- synchronized(this) {
- usingResource = null
- resourceToClose?.close()
- resourceToClose = null
- }
- }
- }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/FrameDispatcher.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/FrameDispatcher.kt
deleted file mode 100644
index e824a52..0000000
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/FrameDispatcher.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.platform
-
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.swing.Swing
-import kotlinx.coroutines.yield
-import kotlin.coroutines.CoroutineContext
-
-/**
- * Dispatch frame after call of [scheduleFrame]
- *
- * Frames executed not more frequent than [framesPerSecond]
- */
-class FrameDispatcher(
- private val onFrame: suspend (nanoTime: Long) -> Unit,
- private val framesPerSecond: () -> Float,
- private val nanoTime: () -> Long = System::nanoTime,
- context: CoroutineContext = Dispatchers.Swing
-) {
- private var needFrame = CompletableDeferred<Unit>()
-
- private val job = GlobalScope.launch(context) {
- while (true) {
- needFrame.await()
- needFrame = CompletableDeferred()
-
- val frameNanoTime = nanoTime()
- onFrame(frameNanoTime)
-
- val elapsed = nanoTime() - frameNanoTime
- val refreshRate = framesPerSecond()
- val singleFrameNanos = (1_000_000_000 / refreshRate).toLong()
- val needToWaitMillis = maxOf(0, singleFrameNanos - elapsed) / 1_000_000
- delayOrYield(needToWaitMillis)
- }
- }
-
- private suspend fun delayOrYield(millis: Long) {
- if (millis > 0) {
- delay(millis)
- } else {
- yield()
- }
- }
-
- fun cancel() {
- job.cancel()
- }
-
- fun scheduleFrame() {
- needFrame.complete(Unit)
- }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.kt
index d817830..5aa7f3d 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.kt
@@ -16,7 +16,7 @@
package androidx.compose.ui.platform
-import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.DesktopCanvas
@@ -24,9 +24,9 @@
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.asDesktopPath
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.graphics.toArgb
@@ -174,7 +174,7 @@
canvas.save()
canvas.concat(matrix)
canvas.translate(position.x.toFloat(), position.y.toFloat())
- canvas.nativeCanvas.drawPicture(picture, null, null)
+ canvas.nativeCanvas.drawPicture(picture!!, null, null)
canvas.restore()
}
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/FrameDispatcherTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/FrameDispatcherTest.kt
deleted file mode 100644
index 25dddc3..0000000
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/FrameDispatcherTest.kt
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.desktop
-
-import androidx.compose.ui.platform.FrameDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.test.TestCoroutineScope
-import kotlinx.coroutines.test.runBlockingTest
-import org.junit.Assert.assertEquals
-import org.junit.Test
-
-@OptIn(ExperimentalCoroutinesApi::class)
-internal class FrameDispatcherTest {
- var frameIndex = 0
- var frameTime = 0L
-
- var frameDuration = 0L
-
- suspend fun onFrame(nanoTime: Long) {
- frameIndex++
- frameTime = nanoTime / 1_000_000
- delay(frameDuration)
- }
-
- fun TestCoroutineScope.testFrameDispatcher() = FrameDispatcher(
- ::onFrame,
- framesPerSecond = { 100f }, // one frame is 10 milliseconds
- nanoTime = { currentTime * 1_000_000 },
- coroutineContext
- )
-
- @Test
- fun `don't schedule`() = runBlockingTest {
- val timer = testFrameDispatcher()
-
- runCurrent()
- assertEquals(0, currentTime)
- assertEquals(0, frameIndex)
- assertEquals(0, frameTime)
-
- advanceTimeBy(10_000)
- assertEquals(0, frameIndex)
- assertEquals(0, frameTime)
-
- timer.cancel()
- }
-
- @Test
- fun `schedule one time`() = runBlockingTest {
- val timer = testFrameDispatcher()
-
- advanceTimeBy(1234)
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(1234, frameTime)
-
- advanceTimeBy(10_000)
- assertEquals(1, frameIndex)
- assertEquals(1234, frameTime)
-
- timer.cancel()
- }
-
- @Test
- fun `schedule multiple times`() = runBlockingTest {
- val timer = testFrameDispatcher()
-
- advanceTimeBy(10_000)
- timer.scheduleFrame()
-
- timer.scheduleFrame()
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- advanceTimeBy(9)
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- advanceTimeBy(1)
- assertEquals(2, frameIndex)
- assertEquals(10_010, frameTime)
-
- advanceTimeBy(10_000)
- assertEquals(2, frameIndex)
- assertEquals(10_010, frameTime)
-
- timer.cancel()
- }
-
- @Test
- fun `schedule after short delay`() = runBlockingTest {
- val timer = testFrameDispatcher()
-
- advanceTimeBy(10_000)
- timer.scheduleFrame()
-
- advanceTimeBy(5)
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- advanceTimeBy(4)
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- advanceTimeBy(1)
- timer.scheduleFrame()
- assertEquals(2, frameIndex)
- assertEquals(10_010, frameTime)
-
- timer.cancel()
- }
-
- @Test
- fun `schedule after long delay`() = runBlockingTest {
- val timer = testFrameDispatcher()
-
- advanceTimeBy(10_000)
- timer.scheduleFrame()
-
- advanceTimeBy(10_000)
- timer.scheduleFrame()
- assertEquals(2, frameIndex)
- assertEquals(20_000, frameTime)
-
- timer.cancel()
- }
-
- @Test
- fun `schedule after short frame`() = runBlockingTest {
- val timer = testFrameDispatcher()
- frameDuration = 7
-
- advanceTimeBy(10_000)
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- advanceTimeBy(9)
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- advanceTimeBy(1)
- assertEquals(2, frameIndex)
- assertEquals(10_010, frameTime)
-
- advanceTimeBy(10_000)
- assertEquals(2, frameIndex)
- assertEquals(10_010, frameTime)
-
- timer.cancel()
- }
-
- @Test
- fun `schedule after long frame`() = runBlockingTest {
- val timer = testFrameDispatcher()
- frameDuration = 13
-
- advanceTimeBy(10_000)
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- advanceTimeBy(12)
- timer.scheduleFrame()
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
-
- advanceTimeBy(1)
- assertEquals(2, frameIndex)
- assertEquals(10_013, frameTime)
-
- advanceTimeBy(10_000)
- assertEquals(2, frameIndex)
- assertEquals(10_013, frameTime)
-
- timer.cancel()
- }
-
- @Test
- fun cancel() = runBlockingTest {
- val timer = testFrameDispatcher()
-
- advanceTimeBy(10_000)
- timer.scheduleFrame()
-
- timer.scheduleFrame()
- timer.scheduleFrame()
- timer.cancel()
- advanceTimeBy(10_000)
- assertEquals(1, frameIndex)
- assertEquals(10_000, frameTime)
- }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/MutableResourceTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/MutableResourceTest.kt
deleted file mode 100644
index 078976f..0000000
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/desktop/MutableResourceTest.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.desktop
-
-import org.junit.Assert.assertTrue
-import org.junit.Test
-
-internal class MutableResourceTest {
- private val allResources = ArrayList<TestResource>()
-
- private inner class TestResource : AutoCloseable {
- var isClosed = false
- private set
-
- init {
- allResources.add(this)
- }
-
- fun checkIsAvailable() {
- for (i in 0 until 10) {
- check(!isClosed)
- Thread.sleep(0)
- }
- }
-
- override fun close() {
- isClosed = true
- }
- }
-
- @Test(timeout = 5000)
- fun `set and use in parallel threads`() {
- val resource = MutableResource<TestResource>()
-
- val usingThread = TestThread {
- for (i in 0 until 10000) {
- resource.useWithoutClosing {
- it?.checkIsAvailable()
- }
- }
- }
-
- val swappingTread = TestThread {
- while (true) {
- resource.set(TestResource())
- Thread.sleep(0)
- resource.set(TestResource())
- Thread.sleep(0)
- resource.close()
- Thread.sleep(0)
- }
- }
-
- usingThread.start()
- swappingTread.start()
-
- usingThread.joinAndThrow()
- swappingTread.interrupt()
- swappingTread.joinAndThrow()
-
- resource.close()
- assertTrue(allResources.all(TestResource::isClosed))
- }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt
index 9f966fa..7d0b1c1 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt
@@ -23,9 +23,11 @@
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.swing.Swing
import kotlinx.coroutines.yield
import org.jetbrains.skija.Canvas
import org.jetbrains.skija.Surface
+import org.jetbrains.skiko.FrameDispatcher
internal fun renderingTest(
width: Int,
@@ -49,13 +51,11 @@
) {
var currentTimeMillis = 0L
- val frameDispatcher = FrameDispatcher(
- onFrame = { onRender(it) },
- framesPerSecond = { Float.POSITIVE_INFINITY },
- nanoTime = { currentTimeMillis * 1_000_000 }
- )
+ val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
+ onRender(currentTimeMillis * 1_000_000)
+ }
- val surface: Surface = Surface.makeRasterN32Premul(width, height)!!
+ val surface: Surface = Surface.makeRasterN32Premul(width, height)
val canvas: Canvas = surface.canvas
val owners = DesktopOwners(
invalidate = frameDispatcher::scheduleFrame
diff --git a/core/core/api/res-1.5.0-beta01.txt b/core/core/api/res-1.5.0-beta01.txt
index a609e0a..43cbfa4 100644
--- a/core/core/api/res-1.5.0-beta01.txt
+++ b/core/core/api/res-1.5.0-beta01.txt
@@ -6,6 +6,7 @@
attr fontProviderFetchTimeout
attr fontProviderPackage
attr fontProviderQuery
+attr fontProviderSystemFontFamily
attr fontStyle
attr fontVariationSettings
attr fontWeight
diff --git a/core/core/api/res-current.txt b/core/core/api/res-current.txt
index a609e0a..43cbfa4 100644
--- a/core/core/api/res-current.txt
+++ b/core/core/api/res-current.txt
@@ -6,6 +6,7 @@
attr fontProviderFetchTimeout
attr fontProviderPackage
attr fontProviderQuery
+attr fontProviderSystemFontFamily
attr fontStyle
attr fontVariationSettings
attr fontWeight
diff --git a/core/core/src/androidTest/java/androidx/core/content/res/FontResourcesParserCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/res/FontResourcesParserCompatTest.java
index cc09f91..b98fa33 100644
--- a/core/core/src/androidTest/java/androidx/core/content/res/FontResourcesParserCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/res/FontResourcesParserCompatTest.java
@@ -155,6 +155,7 @@
request.getProviderAuthority());
assertEquals("androidx.core.test", request.getProviderPackage());
assertEquals("singleFontFamily", request.getQuery());
+ assertEquals("serif", providerEntry.getSystemFontFamilyName());
}
@Test
diff --git a/core/core/src/androidTest/java/androidx/core/content/res/ResourcesCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/res/ResourcesCompatTest.java
index 5fdc9ff..f218c86 100644
--- a/core/core/src/androidTest/java/androidx/core/content/res/ResourcesCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/res/ResourcesCompatTest.java
@@ -455,6 +455,13 @@
}
@Test
+ public void testSystemFontFamilyReturnsSystemFont() {
+ Typeface typeface = ResourcesCompat.getFont(mContext, R.font.samplexmldownloadedfont);
+ assertNotNull(typeface);
+ assertEquals(typeface, Typeface.create("serif", Typeface.NORMAL));
+ }
+
+ @Test
public void getFloatForFloat() {
float value = ResourcesCompat.getFloat(mResources, R.dimen.twelve_point_five);
assertEquals(12.5f, value, 0.01f);
diff --git a/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java b/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java
index 684dd8b..9c00ab6 100644
--- a/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java
@@ -141,7 +141,8 @@
final FontRequest parsedRequest = entry.getRequest();
final FontRequest request = new FontRequest(parsedRequest.getProviderAuthority(),
parsedRequest.getProviderPackage(), parsedRequest.getQuery(), SIGNATURE);
- return new ProviderResourceEntry(request, entry.getFetchStrategy(), entry.getTimeout());
+ return new ProviderResourceEntry(request, entry.getFetchStrategy(), entry.getTimeout(),
+ entry.getSystemFontFamilyName());
}
public static class FontCallback extends ResourcesCompat.FontCallback {
diff --git a/core/core/src/androidTest/res/font/samplexmldownloadedfont.xml b/core/core/src/androidTest/res/font/samplexmldownloadedfont.xml
index cba386f..4e397aa 100644
--- a/core/core/src/androidTest/res/font/samplexmldownloadedfont.xml
+++ b/core/core/src/androidTest/res/font/samplexmldownloadedfont.xml
@@ -3,5 +3,6 @@
app:fontProviderAuthority="androidx.core.provider.fonts.font"
app:fontProviderPackage="androidx.core.test"
app:fontProviderQuery="singleFontFamily"
- app:fontProviderCerts="@array/mock_provider_certs">
+ app:fontProviderCerts="@array/mock_provider_certs"
+ app:fontProviderSystemFontFamily="serif">
</font-family>
diff --git a/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java b/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
index 79c1625..04ec6b1 100644
--- a/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
+++ b/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
@@ -16,6 +16,7 @@
package androidx.core.content.res;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.content.res.Resources;
@@ -77,12 +78,21 @@
private final @NonNull FontRequest mRequest;
private final int mTimeoutMs;
private final @FetchStrategy int mStrategy;
+ private final @Nullable String mSystemFontFamilyName;
+ /** @hide */
+ @RestrictTo(LIBRARY)
public ProviderResourceEntry(@NonNull FontRequest request, @FetchStrategy int strategy,
- int timeoutMs) {
+ int timeoutMs, @Nullable String systemFontFamilyName) {
mRequest = request;
mStrategy = strategy;
mTimeoutMs = timeoutMs;
+ mSystemFontFamilyName = systemFontFamilyName;
+ }
+
+ public ProviderResourceEntry(@NonNull FontRequest request, @FetchStrategy int strategy,
+ int timeoutMs) {
+ this(request, strategy, timeoutMs, null /*systemFontFamilyName*/);
}
public @NonNull FontRequest getRequest() {
@@ -96,6 +106,12 @@
public int getTimeout() {
return mTimeoutMs;
}
+
+ /** @hide */
+ @RestrictTo(LIBRARY)
+ public @Nullable String getSystemFontFamilyName() {
+ return mSystemFontFamilyName;
+ }
}
/**
@@ -200,6 +216,9 @@
FETCH_STRATEGY_ASYNC);
int timeoutMs = array.getInteger(R.styleable.FontFamily_fontProviderFetchTimeout,
DEFAULT_TIMEOUT_MILLIS);
+ String systemFontFamilyName = array
+ .getString(R.styleable.FontFamily_fontProviderSystemFontFamily);
+
array.recycle();
if (authority != null && providerPackage != null && query != null) {
while (parser.next() != XmlPullParser.END_TAG) {
@@ -207,7 +226,11 @@
}
List<List<byte[]>> certs = readCerts(resources, certsId);
return new ProviderResourceEntry(
- new FontRequest(authority, providerPackage, query, certs), strategy, timeoutMs);
+ new FontRequest(authority, providerPackage, query, certs),
+ strategy,
+ timeoutMs,
+ systemFontFamilyName
+ );
}
List<FontFileResourceEntry> fonts = new ArrayList<>();
while (parser.next() != XmlPullParser.END_TAG) {
diff --git a/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java b/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
index c29f838..f91741c7 100644
--- a/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
@@ -94,6 +94,20 @@
}
/**
+ * Returns Typeface if the system has the font family with the name [familyName]. For example
+ * querying with "sans-serif" would check if the "sans-serif" family is defined in the system
+ * and return the Typeface if so.
+ *
+ * @param familyName The name of the font family.
+ */
+ private static Typeface getSystemFontFamily(@Nullable String familyName) {
+ if (familyName == null || familyName.isEmpty()) return null;
+ Typeface typeface = Typeface.create(familyName, Typeface.NORMAL);
+ Typeface defaultTypeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
+ return typeface != null && !typeface.equals(defaultTypeface) ? typeface : null;
+ }
+
+ /**
* Create Typeface from XML resource which root node is font-family.
*
* @return null if failed to create.
@@ -109,6 +123,16 @@
Typeface typeface;
if (entry instanceof ProviderResourceEntry) {
ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
+
+ Typeface fontFamilyTypeface = getSystemFontFamily(
+ providerEntry.getSystemFontFamilyName());
+ if (fontFamilyTypeface != null) {
+ if (fontCallback != null) {
+ fontCallback.callbackSuccessAsync(fontFamilyTypeface, handler);
+ }
+ return fontFamilyTypeface;
+ }
+
final boolean isBlocking = isRequestFromLayoutInflator
? providerEntry.getFetchStrategy()
== FontResourcesParserCompat.FETCH_STRATEGY_BLOCKING
diff --git a/core/core/src/main/res/values/attrs.xml b/core/core/src/main/res/values/attrs.xml
index d1ff95c..ce64ab9 100644
--- a/core/core/src/main/res/values/attrs.xml
+++ b/core/core/src/main/res/values/attrs.xml
@@ -58,6 +58,11 @@
timeout and wait until a reply is received from the font provider. -->
<enum name="forever" value="-1" />
</attr>
+ <!-- Provides the system font family name to check before downloading the font. For
+ example if the fontProviderQuery asked for "Sans Serif", it is possible to define
+ fontProviderSystemFontFamily as "sans-serif" to tell the system to use "sans-serif" font
+ family if it exists on the system. -->
+ <attr name="fontProviderSystemFontFamily" format="string" />
</declare-styleable>
<!-- Attributes that are read when parsing a <font> tag, which is a child of
diff --git a/core/core/src/main/res/values/public.xml b/core/core/src/main/res/values/public.xml
index 64049c8..b27ee48 100644
--- a/core/core/src/main/res/values/public.xml
+++ b/core/core/src/main/res/values/public.xml
@@ -24,6 +24,7 @@
<public type="attr" name="fontProviderCerts"/>
<public type="attr" name="fontProviderFetchStrategy"/>
<public type="attr" name="fontProviderFetchTimeout"/>
+ <public type="attr" name="fontProviderSystemFontFamily"/>
<public type="attr" name="fontStyle"/>
<public type="attr" name="font"/>
<public type="attr" name="fontWeight"/>
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 33e3fd2..907933a 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -129,6 +129,7 @@
docs("androidx.lifecycle:lifecycle-reactivestreams-ktx:2.3.0")
docs("androidx.lifecycle:lifecycle-runtime:2.3.0")
docs("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0")
+ docs("androidx.lifecycle:lifecycle-runtime-testing:2.3.0")
docs("androidx.lifecycle:lifecycle-service:2.3.0")
docs("androidx.lifecycle:lifecycle-viewmodel:2.3.0")
docs("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
diff --git a/fragment/fragment-ktx/build.gradle b/fragment/fragment-ktx/build.gradle
index c6a9da6..a5b8a4a 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -36,10 +36,10 @@
api("androidx.collection:collection-ktx:1.1.0") {
because 'Mirror fragment dependency graph for -ktx artifacts'
}
- api(projectOrArtifact(":lifecycle:lifecycle-livedata-core-ktx")) {
+ api("androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.0") {
because 'Mirror fragment dependency graph for -ktx artifacts'
}
- api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-ktx"))
+ api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
api("androidx.savedstate:savedstate-ktx:1.1.0") {
because 'Mirror fragment dependency graph for -ktx artifacts'
}
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index db758e9..a31aa62 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -38,9 +38,9 @@
api("androidx.viewpager:viewpager:1.0.0")
api("androidx.loader:loader:1.0.0")
api(projectOrArtifact(":activity:activity"))
- api(projectOrArtifact(":lifecycle:lifecycle-livedata-core"))
- api(projectOrArtifact(":lifecycle:lifecycle-viewmodel"))
- api(projectOrArtifact(":lifecycle:lifecycle-viewmodel-savedstate"))
+ api("androidx.lifecycle:lifecycle-livedata-core:2.3.0")
+ api("androidx.lifecycle:lifecycle-viewmodel:2.3.0")
+ api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0")
api("androidx.savedstate:savedstate:1.1.0")
api("androidx.annotation:annotation-experimental:1.0.0")
diff --git a/navigation/navigation-ui/api/current.ignore b/navigation/navigation-ui/api/current.ignore
new file mode 100644
index 0000000..ab1aa64
--- /dev/null
+++ b/navigation/navigation-ui/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+ChangedType: androidx.navigation.ui.AppBarConfiguration#getTopLevelDestinations():
+ Method androidx.navigation.ui.AppBarConfiguration.getTopLevelDestinations has changed return type from java.util.Set<java.lang.Integer!> to java.util.Set<java.lang.Integer>
diff --git a/navigation/navigation-ui/api/current.txt b/navigation/navigation-ui/api/current.txt
index 6c7ec57..771ac62 100644
--- a/navigation/navigation-ui/api/current.txt
+++ b/navigation/navigation-ui/api/current.txt
@@ -5,39 +5,44 @@
method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
method public androidx.customview.widget.Openable? getOpenableLayout();
- method public java.util.Set<java.lang.Integer!> getTopLevelDestinations();
+ method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+ property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+ property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+ property public final androidx.customview.widget.Openable? openableLayout;
+ property public final java.util.Set<java.lang.Integer> topLevelDestinations;
}
public static final class AppBarConfiguration.Builder {
- ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph);
- ctor public AppBarConfiguration.Builder(android.view.Menu);
- ctor public AppBarConfiguration.Builder(int...);
- ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer!>);
+ ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+ ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+ ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+ ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
method public androidx.navigation.ui.AppBarConfiguration build();
- method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout?);
- method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener?);
- method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable?);
+ method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
}
- public static interface AppBarConfiguration.OnNavigateUpListener {
+ public static fun interface AppBarConfiguration.OnNavigateUpListener {
method public boolean onNavigateUp();
}
public final class NavigationUI {
- method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController);
- method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController);
- method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
- method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
- method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController);
- method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView, androidx.navigation.NavController);
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView bottomNavigationView, androidx.navigation.NavController navController);
+ field public static final androidx.navigation.ui.NavigationUI INSTANCE;
}
}
diff --git a/navigation/navigation-ui/api/public_plus_experimental_current.txt b/navigation/navigation-ui/api/public_plus_experimental_current.txt
index 6c7ec57..29b9020e 100644
--- a/navigation/navigation-ui/api/public_plus_experimental_current.txt
+++ b/navigation/navigation-ui/api/public_plus_experimental_current.txt
@@ -5,39 +5,48 @@
method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
method public androidx.customview.widget.Openable? getOpenableLayout();
- method public java.util.Set<java.lang.Integer!> getTopLevelDestinations();
+ method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+ property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+ property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+ property public final androidx.customview.widget.Openable? openableLayout;
+ property public final java.util.Set<java.lang.Integer> topLevelDestinations;
}
public static final class AppBarConfiguration.Builder {
- ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph);
- ctor public AppBarConfiguration.Builder(android.view.Menu);
- ctor public AppBarConfiguration.Builder(int...);
- ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer!>);
+ ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+ ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+ ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+ ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
method public androidx.navigation.ui.AppBarConfiguration build();
- method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout?);
- method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener?);
- method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable?);
+ method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
}
- public static interface AppBarConfiguration.OnNavigateUpListener {
+ public static fun interface AppBarConfiguration.OnNavigateUpListener {
method public boolean onNavigateUp();
}
public final class NavigationUI {
- method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController);
- method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController);
- method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
- method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
- method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController);
- method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView, androidx.navigation.NavController);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static com.google.android.material.bottomsheet.BottomSheetBehavior<?>? findBottomSheetBehavior(android.view.View view);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph graph);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean matchDestination(androidx.navigation.NavDestination destination, @IdRes int destId);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean matchDestinations(androidx.navigation.NavDestination destination, java.util.Set<java.lang.Integer> destinationIds);
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView bottomNavigationView, androidx.navigation.NavController navController);
+ field public static final androidx.navigation.ui.NavigationUI INSTANCE;
}
}
diff --git a/navigation/navigation-ui/api/restricted_current.ignore b/navigation/navigation-ui/api/restricted_current.ignore
new file mode 100644
index 0000000..ab1aa64
--- /dev/null
+++ b/navigation/navigation-ui/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+ChangedType: androidx.navigation.ui.AppBarConfiguration#getTopLevelDestinations():
+ Method androidx.navigation.ui.AppBarConfiguration.getTopLevelDestinations has changed return type from java.util.Set<java.lang.Integer!> to java.util.Set<java.lang.Integer>
diff --git a/navigation/navigation-ui/api/restricted_current.txt b/navigation/navigation-ui/api/restricted_current.txt
index 6c7ec57..771ac62 100644
--- a/navigation/navigation-ui/api/restricted_current.txt
+++ b/navigation/navigation-ui/api/restricted_current.txt
@@ -5,39 +5,44 @@
method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
method public androidx.customview.widget.Openable? getOpenableLayout();
- method public java.util.Set<java.lang.Integer!> getTopLevelDestinations();
+ method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+ property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+ property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+ property public final androidx.customview.widget.Openable? openableLayout;
+ property public final java.util.Set<java.lang.Integer> topLevelDestinations;
}
public static final class AppBarConfiguration.Builder {
- ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph);
- ctor public AppBarConfiguration.Builder(android.view.Menu);
- ctor public AppBarConfiguration.Builder(int...);
- ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer!>);
+ ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+ ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+ ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+ ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
method public androidx.navigation.ui.AppBarConfiguration build();
- method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout?);
- method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener?);
- method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable?);
+ method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+ method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
}
- public static interface AppBarConfiguration.OnNavigateUpListener {
+ public static fun interface AppBarConfiguration.OnNavigateUpListener {
method public boolean onNavigateUp();
}
public final class NavigationUI {
- method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController);
- method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController);
- method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
- method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController);
- method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.customview.widget.Openable?);
- method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar, androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration);
- method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController);
- method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView, androidx.navigation.NavController);
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+ method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+ method public static void setupWithNavController(com.google.android.material.bottomnavigation.BottomNavigationView bottomNavigationView, androidx.navigation.NavController navController);
+ field public static final androidx.navigation.ui.NavigationUI INSTANCE;
}
}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.java b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.java
deleted file mode 100644
index 59518aa..0000000
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.navigation.ui;
-
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringRes;
-import androidx.appcompat.graphics.drawable.DrawerArrowDrawable;
-import androidx.customview.widget.Openable;
-import androidx.navigation.FloatingWindow;
-import androidx.navigation.NavController;
-import androidx.navigation.NavDestination;
-
-import java.lang.ref.WeakReference;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * The abstract OnDestinationChangedListener for keeping any type of app bar updated.
- * This handles both updating the title and updating the Up Indicator, transitioning between
- * the drawer icon and up arrow as needed.
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-abstract class AbstractAppBarOnDestinationChangedListener
- implements NavController.OnDestinationChangedListener {
- private final Context mContext;
- private final Set<Integer> mTopLevelDestinations;
- @Nullable
- private final WeakReference<Openable> mOpenableLayoutWeakReference;
- private DrawerArrowDrawable mArrowDrawable;
- private ValueAnimator mAnimator;
-
- AbstractAppBarOnDestinationChangedListener(@NonNull Context context,
- @NonNull AppBarConfiguration configuration) {
- mContext = context;
- mTopLevelDestinations = configuration.getTopLevelDestinations();
- Openable openableLayout = configuration.getOpenableLayout();
- if (openableLayout != null) {
- mOpenableLayoutWeakReference = new WeakReference<>(openableLayout);
- } else {
- mOpenableLayoutWeakReference = null;
- }
- }
-
- protected abstract void setTitle(CharSequence title);
-
- protected abstract void setNavigationIcon(Drawable icon, @StringRes int contentDescription);
-
- @Override
- public void onDestinationChanged(@NonNull NavController controller,
- @NonNull NavDestination destination, @Nullable Bundle arguments) {
- if (destination instanceof FloatingWindow) {
- return;
- }
- Openable openableLayout = mOpenableLayoutWeakReference != null
- ? mOpenableLayoutWeakReference.get()
- : null;
- if (mOpenableLayoutWeakReference != null && openableLayout == null) {
- controller.removeOnDestinationChangedListener(this);
- return;
- }
- CharSequence label = destination.getLabel();
- if (label != null) {
- // Fill in the data pattern with the args to build a valid URI
- StringBuffer title = new StringBuffer();
- Pattern fillInPattern = Pattern.compile("\\{(.+?)\\}");
- Matcher matcher = fillInPattern.matcher(label);
- while (matcher.find()) {
- String argName = matcher.group(1);
- if (arguments != null && arguments.containsKey(argName)) {
- matcher.appendReplacement(title, "");
- //noinspection ConstantConditions
- title.append(arguments.get(argName).toString());
- } else {
- throw new IllegalArgumentException("Could not find " + argName + " in "
- + arguments + " to fill label " + label);
- }
- }
- matcher.appendTail(title);
- setTitle(title);
- }
- boolean isTopLevelDestination = NavigationUI.matchDestinations(destination,
- mTopLevelDestinations);
- if (openableLayout == null && isTopLevelDestination) {
- setNavigationIcon(null, 0);
- } else {
- setActionBarUpIndicator(openableLayout != null && isTopLevelDestination);
- }
- }
-
- private void setActionBarUpIndicator(boolean showAsDrawerIndicator) {
- boolean animate = true;
- if (mArrowDrawable == null) {
- mArrowDrawable = new DrawerArrowDrawable(mContext);
- // We're setting the initial state, so skip the animation
- animate = false;
- }
- setNavigationIcon(mArrowDrawable, showAsDrawerIndicator
- ? R.string.nav_app_bar_open_drawer_description
- : R.string.nav_app_bar_navigate_up_description);
- float endValue = showAsDrawerIndicator ? 0f : 1f;
- if (animate) {
- float startValue = mArrowDrawable.getProgress();
- if (mAnimator != null) {
- mAnimator.cancel();
- }
- mAnimator = ObjectAnimator.ofFloat(mArrowDrawable, "progress",
- startValue, endValue);
- mAnimator.start();
- } else {
- mArrowDrawable.setProgress(endValue);
- }
- }
-}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.kt
new file mode 100644
index 0000000..4886464
--- /dev/null
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AbstractAppBarOnDestinationChangedListener.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.navigation.ui
+
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import androidx.annotation.RestrictTo
+import androidx.annotation.StringRes
+import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
+import androidx.navigation.FloatingWindow
+import androidx.navigation.NavController
+import androidx.navigation.NavDestination
+import java.lang.ref.WeakReference
+import java.util.regex.Pattern
+
+/**
+ * The abstract OnDestinationChangedListener for keeping any type of app bar updated.
+ * This handles both updating the title and updating the Up Indicator, transitioning between
+ * the drawer icon and up arrow as needed.
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal abstract class AbstractAppBarOnDestinationChangedListener(
+ private val context: Context,
+ configuration: AppBarConfiguration
+) : NavController.OnDestinationChangedListener {
+ private val topLevelDestinations: Set<Int> = configuration.topLevelDestinations
+ private val openableLayoutWeakReference = configuration.openableLayout?.run {
+ WeakReference(this)
+ }
+ private var arrowDrawable: DrawerArrowDrawable? = null
+ private var animator: ValueAnimator? = null
+
+ protected abstract fun setTitle(title: CharSequence?)
+
+ protected abstract fun setNavigationIcon(icon: Drawable?, @StringRes contentDescription: Int)
+
+ override fun onDestinationChanged(
+ controller: NavController,
+ destination: NavDestination,
+ arguments: Bundle?
+ ) {
+ if (destination is FloatingWindow) {
+ return
+ }
+ val openableLayout = openableLayoutWeakReference?.get()
+ if (openableLayoutWeakReference != null && openableLayout == null) {
+ controller.removeOnDestinationChangedListener(this)
+ return
+ }
+ val label = destination.label
+ if (label != null) {
+ // Fill in the data pattern with the args to build a valid URI
+ val title = StringBuffer()
+ val fillInPattern = Pattern.compile("\\{(.+?)\\}")
+ val matcher = fillInPattern.matcher(label)
+ while (matcher.find()) {
+ val argName = matcher.group(1)
+ if (arguments != null && arguments.containsKey(argName)) {
+ matcher.appendReplacement(title, "")
+ title.append(arguments[argName].toString())
+ } else {
+ throw IllegalArgumentException(
+ "Could not find $argName in $arguments to fill label $label"
+ )
+ }
+ }
+ matcher.appendTail(title)
+ setTitle(title)
+ }
+ val isTopLevelDestination = NavigationUI.matchDestinations(
+ destination,
+ topLevelDestinations
+ )
+ if (openableLayout == null && isTopLevelDestination) {
+ setNavigationIcon(null, 0)
+ } else {
+ setActionBarUpIndicator(openableLayout != null && isTopLevelDestination)
+ }
+ }
+
+ @SuppressLint("ObjectAnimatorBinding")
+ private fun setActionBarUpIndicator(showAsDrawerIndicator: Boolean) {
+ val (arrow, animate) = arrowDrawable?.run {
+ this to true
+ } ?: DrawerArrowDrawable(context).also { arrowDrawable = it } to false
+
+ setNavigationIcon(
+ arrow,
+ if (showAsDrawerIndicator) R.string.nav_app_bar_open_drawer_description
+ else R.string.nav_app_bar_navigate_up_description
+ )
+
+ val endValue = if (showAsDrawerIndicator) 0f else 1f
+ if (animate) {
+ val startValue = arrow.progress
+ animator?.cancel()
+ animator = ObjectAnimator.ofFloat(arrow, "progress", startValue, endValue)
+ (animator as ObjectAnimator).start()
+ } else {
+ arrow.progress = endValue
+ }
+ }
+}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt
index c23f619..6d1d8ad 100644
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt
@@ -34,7 +34,7 @@
activity.drawerToggleDelegate!!.actionBarThemedContext,
configuration
) {
- override fun setTitle(title: CharSequence) {
+ override fun setTitle(title: CharSequence?) {
val actionBar = activity.supportActionBar
require(actionBar != null) {
"Activity $activity does not have an ActionBar set via setSupportActionBar()"
@@ -42,7 +42,7 @@
actionBar.setTitle(title)
}
- override fun setNavigationIcon(icon: Drawable, @StringRes contentDescription: Int) {
+ override fun setNavigationIcon(icon: Drawable?, @StringRes contentDescription: Int) {
val actionBar = activity.supportActionBar
require(actionBar != null) {
"Activity $activity does not have an ActionBar set via setSupportActionBar()"
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.java b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.java
deleted file mode 100644
index 8117495..0000000
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.navigation.ui;
-
-import android.annotation.SuppressLint;
-import android.view.Menu;
-import android.view.MenuItem;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.customview.widget.Openable;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.navigation.NavGraph;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Configuration options for {@link NavigationUI} methods that interact with implementations of the
- * app bar pattern such as {@link androidx.appcompat.widget.Toolbar},
- * {@link com.google.android.material.appbar.CollapsingToolbarLayout}, and
- * {@link androidx.appcompat.app.ActionBar}.
- */
-public final class AppBarConfiguration {
- /**
- * Interface for providing custom 'up' behavior beyond what is provided by
- * {@link androidx.navigation.NavController#navigateUp()}.
- *
- * @see Builder#setFallbackOnNavigateUpListener(OnNavigateUpListener)
- * @see NavigationUI#navigateUp(androidx.navigation.NavController, AppBarConfiguration)
- */
- public interface OnNavigateUpListener {
- /**
- * Callback for handling the Up button.
- *
- * @return true if the listener successfully navigated 'up'
- */
- boolean onNavigateUp();
- }
-
- @NonNull
- private final Set<Integer> mTopLevelDestinations;
- @Nullable
- private final Openable mOpenableLayout;
- @Nullable
- private final OnNavigateUpListener mFallbackOnNavigateUpListener;
-
- private AppBarConfiguration(@NonNull Set<Integer> topLevelDestinations,
- @Nullable Openable openableLayout,
- @Nullable OnNavigateUpListener fallbackOnNavigateUpListener) {
- mTopLevelDestinations = topLevelDestinations;
- mOpenableLayout = openableLayout;
- mFallbackOnNavigateUpListener = fallbackOnNavigateUpListener;
- }
-
- /**
- * The set of destinations by id considered at the top level of your information hierarchy.
- * The Up button will not be displayed when on these destinations.
- *
- * @return The set of top level destinations by id.
- */
- @NonNull
- public Set<Integer> getTopLevelDestinations() {
- return mTopLevelDestinations;
- }
-
- /**
- * The {@link Openable} layout indicating that the Navigation button should be displayed as
- * a drawer symbol when it is not being shown as an Up button.
- * @return The Openable layout that should be toggled from the Navigation button
- */
- @Nullable
- public Openable getOpenableLayout() {
- return mOpenableLayout;
- }
-
- /**
- * The {@link DrawerLayout} indicating that the Navigation button should be displayed as
- * a drawer symbol when it is not being shown as an Up button.
- * @return The DrawerLayout that should be toggled from the Navigation button
- * @deprecated Use {@link #getOpenableLayout()}.
- */
- @Deprecated
- @Nullable
- public DrawerLayout getDrawerLayout() {
- if (mOpenableLayout instanceof DrawerLayout) {
- return (DrawerLayout) mOpenableLayout;
- }
- return null;
- }
-
- /**
- * The {@link OnNavigateUpListener} that should be invoked if
- * {@link androidx.navigation.NavController#navigateUp} returns <code>false</code>.
- * @return a {@link OnNavigateUpListener} for providing custom up navigation logic,
- * if one was set.
- */
- @Nullable
- public OnNavigateUpListener getFallbackOnNavigateUpListener() {
- return mFallbackOnNavigateUpListener;
- }
-
- /**
- * The Builder class for constructing new {@link AppBarConfiguration} instances.
- */
- public static final class Builder {
- @NonNull
- private final Set<Integer> mTopLevelDestinations = new HashSet<>();
-
- @Nullable
- private Openable mOpenableLayout;
-
- @Nullable
- private OnNavigateUpListener mFallbackOnNavigateUpListener;
-
- /**
- * Create a new Builder whose only top level destination is the start destination
- * of the given {@link NavGraph}. The Up button will not be displayed when on the
- * start destination of the graph.
- *
- * @param navGraph The NavGraph whose start destination should be considered the only
- * top level destination. The Up button will not be displayed when on the
- * start destination of the graph.
- */
- public Builder(@NonNull NavGraph navGraph) {
- mTopLevelDestinations.add(NavigationUI.findStartDestination(navGraph).getId());
- }
-
- /**
- * Create a new Builder using a {@link Menu} containing all top level destinations. It is
- * expected that the {@link MenuItem#getItemId() menu item id} of each item corresponds
- * with a destination in your navigation graph. The Up button will not be displayed when
- * on these destinations.
- *
- * @param topLevelMenu A Menu containing MenuItems corresponding with the destinations
- * considered at the top level of your information hierarchy.
- * The Up button will not be displayed when on these destinations.
- */
- public Builder(@NonNull Menu topLevelMenu) {
- int size = topLevelMenu.size();
- for (int index = 0; index < size; index++) {
- MenuItem item = topLevelMenu.getItem(index);
- mTopLevelDestinations.add(item.getItemId());
- }
- }
-
- /**
- * Create a new Builder with a specific set of top level destinations. The Up button will
- * not be displayed when on these destinations.
- *
- * @param topLevelDestinationIds The set of destinations by id considered at the top level
- * of your information hierarchy. The Up button will not be
- * displayed when on these destinations.
- */
- public Builder(@NonNull int... topLevelDestinationIds) {
- for (int destinationId : topLevelDestinationIds) {
- mTopLevelDestinations.add(destinationId);
- }
- }
-
- /**
- * Create a new Builder with a specific set of top level destinations. The Up button will
- * not be displayed when on these destinations.
- *
- * @param topLevelDestinationIds The set of destinations by id considered at the top level
- * of your information hierarchy. The Up button will not be
- * displayed when on these destinations.
- */
- public Builder(@NonNull Set<Integer> topLevelDestinationIds) {
- mTopLevelDestinations.addAll(topLevelDestinationIds);
- }
-
- /**
- * Display the Navigation button as a drawer symbol when it is not being shown as an
- * Up button.
- * @param drawerLayout The DrawerLayout that should be toggled from the Navigation button
- * @return this {@link Builder}
- * @deprecated Use {@link #setOpenableLayout(Openable)}.
- */
- @Deprecated
- @NonNull
- public Builder setDrawerLayout(@Nullable DrawerLayout drawerLayout) {
- mOpenableLayout = drawerLayout;
- return this;
- }
-
- /**
- * Display the Navigation button as a drawer symbol when it is not being shown as an
- * Up button.
- * @param openableLayout The Openable layout that should be toggled from the Navigation
- * button
- * @return this {@link Builder}
- */
- @NonNull
- public Builder setOpenableLayout(@Nullable Openable openableLayout) {
- mOpenableLayout = openableLayout;
- return this;
- }
-
- /**
- * Adds a {@link OnNavigateUpListener} that will be called as a fallback if the default
- * behavior of {@link androidx.navigation.NavController#navigateUp}
- * returns <code>false</code>.
- *
- * @param fallbackOnNavigateUpListener Listener that will be invoked if
- * {@link androidx.navigation.NavController#navigateUp}
- * returns <code>false</code>.
- * @return this {@link Builder}
- */
- @NonNull
- public Builder setFallbackOnNavigateUpListener(
- @Nullable OnNavigateUpListener fallbackOnNavigateUpListener) {
- mFallbackOnNavigateUpListener = fallbackOnNavigateUpListener;
- return this;
- }
-
- /**
- * Construct the {@link AppBarConfiguration} instance.
- *
- * @return a valid {@link AppBarConfiguration}
- */
- @SuppressLint("SyntheticAccessor") /* new AppBarConfiguration() must be private to avoid
- conflicting with the public AppBarConfiguration.kt */
- @NonNull
- public AppBarConfiguration build() {
- return new AppBarConfiguration(mTopLevelDestinations, mOpenableLayout,
- mFallbackOnNavigateUpListener);
- }
- }
-}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.kt
new file mode 100644
index 0000000..d9ed493
--- /dev/null
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/AppBarConfiguration.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.navigation.ui
+
+import android.annotation.SuppressLint
+import android.view.Menu
+import androidx.customview.widget.Openable
+import androidx.drawerlayout.widget.DrawerLayout
+import androidx.navigation.NavGraph
+import androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener
+import java.util.HashSet
+
+/**
+ * Configuration options for [NavigationUI] methods that interact with implementations of the
+ * app bar pattern such as [androidx.appcompat.widget.Toolbar],
+ * [com.google.android.material.appbar.CollapsingToolbarLayout], and
+ * [androidx.appcompat.app.ActionBar].
+ */
+public class AppBarConfiguration private constructor(
+ /**
+ * The set of destinations by id considered at the top level of your information hierarchy.
+ * The Up button will not be displayed when on these destinations.
+ *
+ * @return The set of top level destinations by id.
+ */
+ public val topLevelDestinations: Set<Int>,
+ /**
+ * The [Openable] layout indicating that the Navigation button should be displayed as
+ * a drawer symbol when it is not being shown as an Up button.
+ * @return The Openable layout that should be toggled from the Navigation button
+ */
+ public val openableLayout: Openable?,
+ /**
+ * The [OnNavigateUpListener] that should be invoked if
+ * [androidx.navigation.NavController.navigateUp] returns `false`.
+ * @return a [OnNavigateUpListener] for providing custom up navigation logic,
+ * if one was set.
+ */
+ public val fallbackOnNavigateUpListener: OnNavigateUpListener?
+) {
+ /**
+ * Interface for providing custom 'up' behavior beyond what is provided by
+ * [androidx.navigation.NavController.navigateUp].
+ *
+ * @see Builder.setFallbackOnNavigateUpListener
+ * @see NavigationUI.navigateUp
+ */
+ public fun interface OnNavigateUpListener {
+ /**
+ * Callback for handling the Up button.
+ *
+ * @return true if the listener successfully navigated 'up'
+ */
+ public fun onNavigateUp(): Boolean
+ }
+
+ /**
+ * The [DrawerLayout] indicating that the Navigation button should be displayed as
+ * a drawer symbol when it is not being shown as an Up button.
+ * @return The DrawerLayout that should be toggled from the Navigation button
+ */
+ @get:Deprecated("Use {@link #getOpenableLayout()}.")
+ public val drawerLayout: DrawerLayout?
+ get() = if (openableLayout is DrawerLayout) {
+ openableLayout
+ } else null
+
+ /**
+ * The Builder class for constructing new [AppBarConfiguration] instances.
+ */
+ public class Builder {
+ private val topLevelDestinations: MutableSet<Int> = HashSet()
+ private var openableLayout: Openable? = null
+ private var fallbackOnNavigateUpListener: OnNavigateUpListener? = null
+
+ /**
+ * Create a new Builder whose only top level destination is the start destination
+ * of the given [NavGraph]. The Up button will not be displayed when on the
+ * start destination of the graph.
+ *
+ * @param navGraph The NavGraph whose start destination should be considered the only
+ * top level destination. The Up button will not be displayed when on the
+ * start destination of the graph.
+ */
+ public constructor(navGraph: NavGraph) {
+ topLevelDestinations.add(NavigationUI.findStartDestination(navGraph).id)
+ }
+
+ /**
+ * Create a new Builder using a [Menu] containing all top level destinations. It is
+ * expected that the [menu item id][MenuItem.getItemId] of each item corresponds
+ * with a destination in your navigation graph. The Up button will not be displayed when
+ * on these destinations.
+ *
+ * @param topLevelMenu A Menu containing MenuItems corresponding with the destinations
+ * considered at the top level of your information hierarchy.
+ * The Up button will not be displayed when on these destinations.
+ */
+ public constructor(topLevelMenu: Menu) {
+ val size = topLevelMenu.size()
+ for (index in 0 until size) {
+ val item = topLevelMenu.getItem(index)
+ topLevelDestinations.add(item.itemId)
+ }
+ }
+
+ /**
+ * Create a new Builder with a specific set of top level destinations. The Up button will
+ * not be displayed when on these destinations.
+ *
+ * @param topLevelDestinationIds The set of destinations by id considered at the top level
+ * of your information hierarchy. The Up button will not be
+ * displayed when on these destinations.
+ */
+ public constructor(vararg topLevelDestinationIds: Int) {
+ for (destinationId in topLevelDestinationIds) {
+ topLevelDestinations.add(destinationId)
+ }
+ }
+
+ /**
+ * Create a new Builder with a specific set of top level destinations. The Up button will
+ * not be displayed when on these destinations.
+ *
+ * @param topLevelDestinationIds The set of destinations by id considered at the top level
+ * of your information hierarchy. The Up button will not be
+ * displayed when on these destinations.
+ */
+ public constructor(topLevelDestinationIds: Set<Int>) {
+ topLevelDestinations.addAll(topLevelDestinationIds)
+ }
+
+ /**
+ * Display the Navigation button as a drawer symbol when it is not being shown as an
+ * Up button.
+ * @param drawerLayout The DrawerLayout that should be toggled from the Navigation button
+ * @return this [Builder]
+ */
+ @Deprecated("Use {@link #setOpenableLayout(Openable)}.")
+ public fun setDrawerLayout(drawerLayout: DrawerLayout?): Builder {
+ openableLayout = drawerLayout
+ return this
+ }
+
+ /**
+ * Display the Navigation button as a drawer symbol when it is not being shown as an
+ * Up button.
+ * @param openableLayout The Openable layout that should be toggled from the Navigation
+ * button
+ * @return this [Builder]
+ */
+ public fun setOpenableLayout(openableLayout: Openable?): Builder {
+ this.openableLayout = openableLayout
+ return this
+ }
+
+ /**
+ * Adds a [OnNavigateUpListener] that will be called as a fallback if the default
+ * behavior of [androidx.navigation.NavController.navigateUp]
+ * returns `false`.
+ *
+ * @param fallbackOnNavigateUpListener Listener that will be invoked if
+ * [androidx.navigation.NavController.navigateUp]
+ * returns `false`.
+ * @return this [Builder]
+ */
+ public fun setFallbackOnNavigateUpListener(
+ fallbackOnNavigateUpListener: OnNavigateUpListener?
+ ): Builder {
+ this.fallbackOnNavigateUpListener = fallbackOnNavigateUpListener
+ return this
+ }
+
+ /**
+ * Construct the [AppBarConfiguration] instance.
+ *
+ * @return a valid [AppBarConfiguration]
+ */
+ @SuppressLint("SyntheticAccessor") /* new AppBarConfiguration() must be private to avoid
+ conflicting with the public AppBarConfiguration.kt */
+ public fun build(): AppBarConfiguration {
+ return AppBarConfiguration(
+ topLevelDestinations,
+ openableLayout,
+ fallbackOnNavigateUpListener
+ )
+ }
+ }
+}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/CollapsingToolbarOnDestinationChangedListener.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/CollapsingToolbarOnDestinationChangedListener.kt
index faa449d..f878edb 100644
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/CollapsingToolbarOnDestinationChangedListener.kt
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/CollapsingToolbarOnDestinationChangedListener.kt
@@ -58,7 +58,7 @@
super.onDestinationChanged(controller, destination, arguments)
}
- override fun setTitle(title: CharSequence) {
+ override fun setTitle(title: CharSequence?) {
val collapsingToolbarLayout = mCollapsingToolbarLayoutWeakReference.get()
if (collapsingToolbarLayout != null) {
collapsingToolbarLayout.title = title
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java
deleted file mode 100644
index 4609bef..0000000
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.java
+++ /dev/null
@@ -1,603 +0,0 @@
-/*
- * Copyright (C) 2017 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.ui;
-
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-import androidx.annotation.IdRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.customview.widget.Openable;
-import androidx.navigation.ActivityNavigator;
-import androidx.navigation.NavController;
-import androidx.navigation.NavDestination;
-import androidx.navigation.NavGraph;
-import androidx.navigation.NavOptions;
-
-import com.google.android.material.appbar.CollapsingToolbarLayout;
-import com.google.android.material.bottomnavigation.BottomNavigationView;
-import com.google.android.material.bottomsheet.BottomSheetBehavior;
-import com.google.android.material.navigation.NavigationView;
-
-import java.lang.ref.WeakReference;
-import java.util.Set;
-
-/**
- * Class which hooks up elements typically in the 'chrome' of your application such as global
- * navigation patterns like a navigation drawer or bottom nav bar with your {@link NavController}.
- */
-public final class NavigationUI {
-
- // No instances. Static utilities only.
- private NavigationUI() {
- }
-
- /**
- * Attempt to navigate to the {@link NavDestination} associated with the given MenuItem. This
- * MenuItem should have been added via one of the helper methods in this class.
- *
- * <p>Importantly, it assumes the {@link MenuItem#getItemId() menu item id} matches a valid
- * {@link NavDestination#getAction(int) action id} or
- * {@link NavDestination#getId() destination id} to be navigated to.</p>
- * <p>
- * By default, the back stack will be popped back to the navigation graph's start destination.
- * Menu items that have <code>android:menuCategory="secondary"</code> will not pop the back
- * stack.
- *
- * @param item The selected MenuItem.
- * @param navController The NavController that hosts the destination.
- * @return True if the {@link NavController} was able to navigate to the destination
- * associated with the given MenuItem.
- */
- public static boolean onNavDestinationSelected(@NonNull MenuItem item,
- @NonNull NavController navController) {
- NavOptions.Builder builder = new NavOptions.Builder()
- .setLaunchSingleTop(true);
- if (navController.getCurrentDestination().getParent().findNode(item.getItemId())
- instanceof ActivityNavigator.Destination) {
- builder.setEnterAnim(R.anim.nav_default_enter_anim)
- .setExitAnim(R.anim.nav_default_exit_anim)
- .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
- .setPopExitAnim(R.anim.nav_default_pop_exit_anim);
-
- } else {
- builder.setEnterAnim(R.animator.nav_default_enter_anim)
- .setExitAnim(R.animator.nav_default_exit_anim)
- .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
- .setPopExitAnim(R.animator.nav_default_pop_exit_anim);
- }
- if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
- builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
- }
- NavOptions options = builder.build();
- try {
- //TODO provide proper API instead of using Exceptions as Control-Flow.
- navController.navigate(item.getItemId(), null, options);
- return true;
- } catch (IllegalArgumentException e) {
- return false;
- }
- }
-
- /**
- * Handles the Up button by delegating its behavior to the given NavController. This should
- * generally be called from {@link AppCompatActivity#onSupportNavigateUp()}.
- * <p>If you do not have a {@link Openable} layout, you should call
- * {@link NavController#navigateUp()} directly.
- *
- * @param navController The NavController that hosts your content.
- * @param openableLayout The Openable layout that should be opened if you are on the topmost
- * level of the app.
- * @return True if the {@link NavController} was able to navigate up.
- */
- public static boolean navigateUp(@NonNull NavController navController,
- @Nullable Openable openableLayout) {
- return navigateUp(navController, new AppBarConfiguration.Builder(navController.getGraph())
- .setOpenableLayout(openableLayout)
- .build());
- }
-
- /**
- * Handles the Up button by delegating its behavior to the given NavController. This is
- * an alternative to using {@link NavController#navigateUp()} directly when the given
- * {@link AppBarConfiguration} needs to be considered when determining what should happen
- * when the Up button is pressed.
- * <p>
- * In cases where no Up action is available, the
- * {@link AppBarConfiguration#getFallbackOnNavigateUpListener()} will be called to provide
- * additional control.
- *
- * @param navController The NavController that hosts your content.
- * @param configuration Additional configuration options for determining what should happen
- * when the Up button is pressed.
- * @return True if the {@link NavController} was able to navigate up.
- */
- public static boolean navigateUp(@NonNull NavController navController,
- @NonNull AppBarConfiguration configuration) {
- Openable openableLayout = configuration.getOpenableLayout();
- NavDestination currentDestination = navController.getCurrentDestination();
- Set<Integer> topLevelDestinations = configuration.getTopLevelDestinations();
- if (openableLayout != null && currentDestination != null
- && matchDestinations(currentDestination, topLevelDestinations)) {
- openableLayout.open();
- return true;
- } else {
- if (navController.navigateUp()) {
- return true;
- } else if (configuration.getFallbackOnNavigateUpListener() != null) {
- return configuration.getFallbackOnNavigateUpListener().onNavigateUp();
- } else {
- return false;
- }
- }
- }
-
- /**
- * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
- * with a {@link NavController}.
- *
- * <p>By calling this method, the title in the action bar will automatically be updated when
- * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
- *
- * <p>The start destination of your navigation graph is considered the only top level
- * destination. On all other destinations, the ActionBar will show the Up button.
- * Call {@link NavController#navigateUp()} to handle the Up button.
- *
- * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
- *
- * @param activity The activity hosting the action bar that should be kept in sync with changes
- * to the NavController.
- * @param navController The NavController that supplies the secondary menu. Navigation actions
- * on this NavController will be reflected in the title of the action bar.
- * @see #setupActionBarWithNavController(AppCompatActivity, NavController, AppBarConfiguration)
- */
- public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
- @NonNull NavController navController) {
- setupActionBarWithNavController(activity, navController,
- new AppBarConfiguration.Builder(navController.getGraph())
- .build());
- }
-
- /**
- * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
- * with a {@link NavController}.
- *
- * <p>By calling this method, the title in the action bar will automatically be updated when
- * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
- *
- * <p>The start destination of your navigation graph is considered the only top level
- * destination. On the start destination of your navigation graph, the ActionBar will show
- * the drawer icon if the given Openable layout is non null. On all other destinations,
- * the ActionBar will show the Up button.
- * Call {@link #navigateUp(NavController, Openable)} to handle the Up button.
- *
- * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
- *
- * @param activity The activity hosting the action bar that should be kept in sync with changes
- * to the NavController.
- * @param navController The NavController whose navigation actions will be reflected
- * in the title of the action bar.
- * @param openableLayout The Openable layout that should be toggled from the home button
- * @see #setupActionBarWithNavController(AppCompatActivity, NavController, AppBarConfiguration)
- */
- public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
- @NonNull NavController navController,
- @Nullable Openable openableLayout) {
- setupActionBarWithNavController(activity, navController,
- new AppBarConfiguration.Builder(navController.getGraph())
- .setOpenableLayout(openableLayout)
- .build());
- }
-
- /**
- * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
- * with a {@link NavController}.
- *
- * <p>By calling this method, the title in the action bar will automatically be updated when
- * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
- *
- * <p>The {@link AppBarConfiguration} you provide controls how the Navigation button is
- * displayed.
- * Call {@link #navigateUp(NavController, AppBarConfiguration)} to handle the Up button.
- *
- * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
- *
- * @param activity The activity hosting the action bar that should be kept in sync with changes
- * to the NavController.
- * @param navController The NavController whose navigation actions will be reflected
- * in the title of the action bar.
- * @param configuration Additional configuration options for customizing the behavior of the
- * ActionBar
- */
- public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
- @NonNull NavController navController,
- @NonNull AppBarConfiguration configuration) {
- navController.addOnDestinationChangedListener(
- new ActionBarOnDestinationChangedListener(activity, configuration));
- }
-
- /**
- * Sets up a {@link Toolbar} for use with a {@link NavController}.
- *
- * <p>By calling this method, the title in the Toolbar will automatically be updated when
- * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
- *
- * <p>The start destination of your navigation graph is considered the only top level
- * destination. On all other destinations, the Toolbar will show the Up button. This
- * method will call {@link NavController#navigateUp()} when the Navigation button
- * is clicked.
- *
- * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
- *
- * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
- * @param navController The NavController that supplies the secondary menu. Navigation actions
- * on this NavController will be reflected in the title of the Toolbar.
- * @see #setupWithNavController(Toolbar, NavController, AppBarConfiguration)
- */
- public static void setupWithNavController(@NonNull Toolbar toolbar,
- @NonNull NavController navController) {
- setupWithNavController(toolbar, navController,
- new AppBarConfiguration.Builder(navController.getGraph()).build());
- }
-
- /**
- * Sets up a {@link Toolbar} for use with a {@link NavController}.
- *
- * <p>By calling this method, the title in the Toolbar will automatically be updated when
- * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
- *
- * <p>The start destination of your navigation graph is considered the only top level
- * destination. On the start destination of your navigation graph, the Toolbar will show
- * the drawer icon if the given Openable layout is non null. On all other destinations,
- * the Toolbar will show the Up button. This method will call
- * {@link #navigateUp(NavController, Openable)} when the Navigation button is clicked.
- *
- * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
- *
- * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
- * @param navController The NavController whose navigation actions will be reflected
- * in the title of the Toolbar.
- * @param openableLayout The Openable layout that should be toggled from the Navigation button
- * @see #setupWithNavController(Toolbar, NavController, AppBarConfiguration)
- */
- public static void setupWithNavController(@NonNull Toolbar toolbar,
- @NonNull final NavController navController,
- @Nullable final Openable openableLayout) {
- setupWithNavController(toolbar, navController,
- new AppBarConfiguration.Builder(navController.getGraph())
- .setOpenableLayout(openableLayout)
- .build());
- }
-
- /**
- * Sets up a {@link Toolbar} for use with a {@link NavController}.
- *
- * <p>By calling this method, the title in the Toolbar will automatically be updated when
- * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
- *
- * <p>The {@link AppBarConfiguration} you provide controls how the Navigation button is
- * displayed and what action is triggered when the Navigation button is tapped. This method
- * will call {@link #navigateUp(NavController, AppBarConfiguration)} when the Navigation button
- * is clicked.
- *
- * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
- *
- * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
- * @param navController The NavController whose navigation actions will be reflected
- * in the title of the Toolbar.
- * @param configuration Additional configuration options for customizing the behavior of the
- * Toolbar
- */
- public static void setupWithNavController(@NonNull Toolbar toolbar,
- @NonNull final NavController navController,
- @NonNull final AppBarConfiguration configuration) {
- navController.addOnDestinationChangedListener(
- new ToolbarOnDestinationChangedListener(toolbar, configuration));
- toolbar.setNavigationOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- navigateUp(navController, configuration);
- }
- });
- }
-
- /**
- * Sets up a {@link CollapsingToolbarLayout} and {@link Toolbar} for use with a
- * {@link NavController}.
- *
- * <p>By calling this method, the title in the CollapsingToolbarLayout will automatically be
- * updated when the destination changes (assuming there is a valid
- * {@link NavDestination#getLabel label}).
- *
- * <p>The start destination of your navigation graph is considered the only top level
- * destination. On all other destinations, the Toolbar will show the Up button. This
- * method will call {@link NavController#navigateUp()} when the Navigation button
- * is clicked.
- *
- * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
- *
- * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
- * changes to the NavController.
- * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
- * @param navController The NavController that supplies the secondary menu. Navigation actions
- * on this NavController will be reflected in the title of the Toolbar.
- */
- public static void setupWithNavController(
- @NonNull CollapsingToolbarLayout collapsingToolbarLayout,
- @NonNull Toolbar toolbar,
- @NonNull NavController navController) {
- setupWithNavController(collapsingToolbarLayout, toolbar, navController,
- new AppBarConfiguration.Builder(navController.getGraph()).build());
- }
-
- /**
- * Sets up a {@link CollapsingToolbarLayout} and {@link Toolbar} for use with a
- * {@link NavController}.
- *
- * <p>By calling this method, the title in the CollapsingToolbarLayout will automatically be
- * updated when the destination changes (assuming there is a valid
- * {@link NavDestination#getLabel label}).
- *
- * <p>The start destination of your navigation graph is considered the only top level
- * destination. On the start destination of your navigation graph, the Toolbar will show
- * the drawer icon if the given Openable layout is non null. On all other destinations,
- * the Toolbar will show the Up button. This method will call
- * {@link #navigateUp(NavController, Openable)} when the Navigation button is clicked.
- *
- * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
- *
- * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
- * changes to the NavController.
- * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
- * @param navController The NavController whose navigation actions will be reflected
- * in the title of the Toolbar.
- * @param openableLayout The Openable layout that should be toggled from the Navigation button
- */
- public static void setupWithNavController(
- @NonNull CollapsingToolbarLayout collapsingToolbarLayout,
- @NonNull Toolbar toolbar,
- @NonNull final NavController navController,
- @Nullable final Openable openableLayout) {
- setupWithNavController(collapsingToolbarLayout, toolbar, navController,
- new AppBarConfiguration.Builder(navController.getGraph())
- .setOpenableLayout(openableLayout)
- .build());
- }
-
- /**
- * Sets up a {@link CollapsingToolbarLayout} and {@link Toolbar} for use with a
- * {@link NavController}.
- *
- * <p>By calling this method, the title in the CollapsingToolbarLayout will automatically be
- * updated when the destination changes (assuming there is a valid
- * {@link NavDestination#getLabel label}).
- *
- * <p>The {@link AppBarConfiguration} you provide controls how the Navigation button is
- * displayed and what action is triggered when the Navigation button is tapped. This method
- * will call {@link #navigateUp(NavController, AppBarConfiguration)} when the Navigation button
- * is clicked.
- *
- * <p>Destinations that implement {@link androidx.navigation.FloatingWindow} will be ignored.
- *
- * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
- * changes to the NavController.
- * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
- * @param navController The NavController whose navigation actions will be reflected
- * in the title of the Toolbar.
- * @param configuration Additional configuration options for customizing the behavior of the
- * Toolbar
- */
- public static void setupWithNavController(
- @NonNull CollapsingToolbarLayout collapsingToolbarLayout,
- @NonNull Toolbar toolbar,
- @NonNull final NavController navController,
- @NonNull final AppBarConfiguration configuration) {
- navController.addOnDestinationChangedListener(
- new CollapsingToolbarOnDestinationChangedListener(
- collapsingToolbarLayout, toolbar, configuration));
- toolbar.setNavigationOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- navigateUp(navController, configuration);
- }
- });
- }
-
- /**
- * Sets up a {@link NavigationView} for use with a {@link NavController}. This will call
- * {@link #onNavDestinationSelected(MenuItem, NavController)} when a menu item is selected.
- * The selected item in the NavigationView will automatically be updated when the destination
- * changes.
- * <p>
- * If the {@link NavigationView} is directly contained with an {@link Openable} layout,
- * it will be closed when a menu item is selected.
- * <p>
- * Similarly, if the {@link NavigationView} has a {@link BottomSheetBehavior} associated with
- * it (as is the case when using a {@link com.google.android.material.bottomsheet.BottomSheetDialog}),
- * the bottom sheet will be hidden when a menu item is selected.
- *
- * @param navigationView The NavigationView that should be kept in sync with changes to the
- * NavController.
- * @param navController The NavController that supplies the primary and secondary menu.
- * Navigation actions on this NavController will be reflected in the
- * selected item in the NavigationView.
- */
- public static void setupWithNavController(@NonNull final NavigationView navigationView,
- @NonNull final NavController navController) {
- navigationView.setNavigationItemSelectedListener(
- new NavigationView.OnNavigationItemSelectedListener() {
- @Override
- public boolean onNavigationItemSelected(@NonNull MenuItem item) {
- boolean handled = onNavDestinationSelected(item, navController);
- if (handled) {
- ViewParent parent = navigationView.getParent();
- if (parent instanceof Openable) {
- ((Openable) parent).close();
- } else {
- BottomSheetBehavior bottomSheetBehavior =
- findBottomSheetBehavior(navigationView);
- if (bottomSheetBehavior != null) {
- bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
- }
- }
- }
- return handled;
- }
- });
- final WeakReference<NavigationView> weakReference = new WeakReference<>(navigationView);
- navController.addOnDestinationChangedListener(
- new NavController.OnDestinationChangedListener() {
- @Override
- public void onDestinationChanged(@NonNull NavController controller,
- @NonNull NavDestination destination, @Nullable Bundle arguments) {
- NavigationView view = weakReference.get();
- if (view == null) {
- navController.removeOnDestinationChangedListener(this);
- return;
- }
- Menu menu = view.getMenu();
- for (int h = 0, size = menu.size(); h < size; h++) {
- MenuItem item = menu.getItem(h);
- item.setChecked(matchDestination(destination, item.getItemId()));
- }
- }
- });
- }
-
- /**
- * Walks up the view hierarchy, trying to determine if the given View is contained within
- * a bottom sheet.
- */
- @SuppressWarnings("WeakerAccess")
- static BottomSheetBehavior findBottomSheetBehavior(@NonNull View view) {
- ViewGroup.LayoutParams params = view.getLayoutParams();
- if (!(params instanceof CoordinatorLayout.LayoutParams)) {
- ViewParent parent = view.getParent();
- if (parent instanceof View) {
- return findBottomSheetBehavior((View) parent);
- }
- return null;
- }
- CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
- .getBehavior();
- if (!(behavior instanceof BottomSheetBehavior)) {
- // We hit a CoordinatorLayout, but the View doesn't have the BottomSheetBehavior
- return null;
- }
- return (BottomSheetBehavior) behavior;
- }
-
- /**
- * Sets up a {@link BottomNavigationView} for use with a {@link NavController}. This will call
- * {@link #onNavDestinationSelected(MenuItem, NavController)} when a menu item is selected. The
- * selected item in the BottomNavigationView will automatically be updated when the destination
- * changes.
- *
- * @param bottomNavigationView The BottomNavigationView that should be kept in sync with
- * changes to the NavController.
- * @param navController The NavController that supplies the primary menu.
- * Navigation actions on this NavController will be reflected in the
- * selected item in the BottomNavigationView.
- */
- public static void setupWithNavController(
- @NonNull final BottomNavigationView bottomNavigationView,
- @NonNull final NavController navController) {
- bottomNavigationView.setOnNavigationItemSelectedListener(
- new BottomNavigationView.OnNavigationItemSelectedListener() {
- @Override
- public boolean onNavigationItemSelected(@NonNull MenuItem item) {
- return onNavDestinationSelected(item, navController);
- }
- });
- final WeakReference<BottomNavigationView> weakReference =
- new WeakReference<>(bottomNavigationView);
- navController.addOnDestinationChangedListener(
- new NavController.OnDestinationChangedListener() {
- @Override
- public void onDestinationChanged(@NonNull NavController controller,
- @NonNull NavDestination destination, @Nullable Bundle arguments) {
- BottomNavigationView view = weakReference.get();
- if (view == null) {
- navController.removeOnDestinationChangedListener(this);
- return;
- }
- Menu menu = view.getMenu();
- for (int h = 0, size = menu.size(); h < size; h++) {
- MenuItem item = menu.getItem(h);
- if (matchDestination(destination, item.getItemId())) {
- item.setChecked(true);
- }
- }
- }
- });
- }
-
- /**
- * Determines whether the given <code>destId</code> matches the NavDestination. This handles
- * both the default case (the destination's id matches the given id) and the nested case where
- * the given id is a parent/grandparent/etc of the destination.
- */
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- static boolean matchDestination(@NonNull NavDestination destination,
- @IdRes int destId) {
- NavDestination currentDestination = destination;
- while (currentDestination.getId() != destId && currentDestination.getParent() != null) {
- currentDestination = currentDestination.getParent();
- }
- return currentDestination.getId() == destId;
- }
-
- /**
- * Determines whether the given <code>destinationIds</code> match the NavDestination. This
- * handles both the default case (the destination's id is in the given ids) and the nested
- * case where the given ids is a parent/grandparent/etc of the destination.
- */
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- static boolean matchDestinations(@NonNull NavDestination destination,
- @NonNull Set<Integer> destinationIds) {
- NavDestination currentDestination = destination;
- do {
- if (destinationIds.contains(currentDestination.getId())) {
- return true;
- }
- currentDestination = currentDestination.getParent();
- } while (currentDestination != null);
- return false;
- }
-
- /**
- * Finds the actual start destination of the graph, handling cases where the graph's starting
- * destination is itself a NavGraph.
- */
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- static NavDestination findStartDestination(@NonNull NavGraph graph) {
- NavDestination startDestination = graph;
- while (startDestination instanceof NavGraph) {
- NavGraph parent = (NavGraph) startDestination;
- startDestination = parent.findNode(parent.getStartDestination());
- }
- return startDestination;
- }
-}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.kt
new file mode 100644
index 0000000..55b453f
--- /dev/null
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.kt
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2017 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.ui
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import androidx.annotation.IdRes
+import androidx.annotation.RestrictTo
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.Toolbar
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.view.forEach
+import androidx.customview.widget.Openable
+import androidx.navigation.ActivityNavigator
+import androidx.navigation.NavController
+import androidx.navigation.NavDestination
+import androidx.navigation.NavGraph
+import androidx.navigation.NavOptions
+import com.google.android.material.appbar.CollapsingToolbarLayout
+import com.google.android.material.bottomnavigation.BottomNavigationView
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.navigation.NavigationView
+import java.lang.IllegalArgumentException
+import java.lang.ref.WeakReference
+
+/**
+ * Class which hooks up elements typically in the 'chrome' of your application such as global
+ * navigation patterns like a navigation drawer or bottom nav bar with your [NavController].
+ */
+public object NavigationUI {
+ /**
+ * Attempt to navigate to the [NavDestination] associated with the given MenuItem. This
+ * MenuItem should have been added via one of the helper methods in this class.
+ *
+ * Importantly, it assumes the [menu item id][MenuItem.getItemId] matches a valid
+ * [action id][NavDestination.getAction] or [destination id][NavDestination.getId] to be
+ * navigated to.
+ *
+ * By default, the back stack will be popped back to the navigation graph's start destination.
+ * Menu items that have `android:menuCategory="secondary"` will not pop the back
+ * stack.
+ *
+ * @param item The selected MenuItem.
+ * @param navController The NavController that hosts the destination.
+ * @return True if the [NavController] was able to navigate to the destination
+ * associated with the given MenuItem.
+ */
+ @JvmStatic
+ public fun onNavDestinationSelected(item: MenuItem, navController: NavController): Boolean {
+ val builder = NavOptions.Builder().setLaunchSingleTop(true)
+ if (
+ navController.currentDestination!!.parent!!.findNode(item.itemId)
+ is ActivityNavigator.Destination
+ ) {
+ builder.setEnterAnim(R.anim.nav_default_enter_anim)
+ .setExitAnim(R.anim.nav_default_exit_anim)
+ .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
+ .setPopExitAnim(R.anim.nav_default_pop_exit_anim)
+ } else {
+ builder.setEnterAnim(R.animator.nav_default_enter_anim)
+ .setExitAnim(R.animator.nav_default_exit_anim)
+ .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
+ .setPopExitAnim(R.animator.nav_default_pop_exit_anim)
+ }
+ if (item.order and Menu.CATEGORY_SECONDARY == 0) {
+ builder.setPopUpTo(findStartDestination(navController.graph).id, false)
+ }
+ val options = builder.build()
+ return try {
+ // TODO provide proper API instead of using Exceptions as Control-Flow.
+ navController.navigate(item.itemId, null, options)
+ true
+ } catch (e: IllegalArgumentException) {
+ false
+ }
+ }
+
+ /**
+ * Handles the Up button by delegating its behavior to the given NavController. This should
+ * generally be called from [AppCompatActivity.onSupportNavigateUp].
+ *
+ * If you do not have a [Openable] layout, you should call
+ * [NavController.navigateUp] directly.
+ *
+ * @param navController The NavController that hosts your content.
+ * @param openableLayout The Openable layout that should be opened if you are on the topmost
+ * level of the app.
+ * @return True if the [NavController] was able to navigate up.
+ */
+ @JvmStatic
+ public fun navigateUp(navController: NavController, openableLayout: Openable?): Boolean =
+ navigateUp(
+ navController,
+ AppBarConfiguration.Builder(navController.graph)
+ .setOpenableLayout(openableLayout)
+ .build()
+ )
+
+ /**
+ * Handles the Up button by delegating its behavior to the given NavController. This is
+ * an alternative to using [NavController.navigateUp] directly when the given
+ * [AppBarConfiguration] needs to be considered when determining what should happen
+ * when the Up button is pressed.
+ *
+ * In cases where no Up action is available, the
+ * [AppBarConfiguration.getFallbackOnNavigateUpListener] will be called to provide
+ * additional control.
+ *
+ * @param navController The NavController that hosts your content.
+ * @param configuration Additional configuration options for determining what should happen
+ * when the Up button is pressed.
+ * @return True if the [NavController] was able to navigate up.
+ */
+ @JvmStatic
+ public fun navigateUp(
+ navController: NavController,
+ configuration: AppBarConfiguration
+ ): Boolean {
+ val openableLayout = configuration.openableLayout
+ val currentDestination = navController.currentDestination
+ val topLevelDestinations = configuration.topLevelDestinations
+ return if (openableLayout != null && currentDestination != null && matchDestinations(
+ currentDestination,
+ topLevelDestinations
+ )
+ ) {
+ openableLayout.open()
+ true
+ } else {
+ return if (navController.navigateUp()) {
+ true
+ } else configuration.fallbackOnNavigateUpListener?.onNavigateUp() ?: false
+ }
+ }
+
+ /**
+ * Sets up the ActionBar returned by [AppCompatActivity.getSupportActionBar] for use
+ * with a [NavController].
+ *
+ * By calling this method, the title in the action bar will automatically be updated when
+ * the destination changes (assuming there is a valid [label][NavDestination.getLabel]).
+ *
+ * The start destination of your navigation graph is considered the only top level
+ * destination. On the start destination of your navigation graph, the ActionBar will show
+ * the drawer icon if the given Openable layout is non null. On all other destinations,
+ * the ActionBar will show the Up button.
+ * Call [.navigateUp] to handle the Up button.
+ *
+ * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+ *
+ * @param activity The activity hosting the action bar that should be kept in sync with changes
+ * to the NavController.
+ * @param navController The NavController whose navigation actions will be reflected
+ * in the title of the action bar.
+ * @param openableLayout The Openable layout that should be toggled from the home button
+ * @see .setupActionBarWithNavController
+ */
+ @JvmStatic
+ public fun setupActionBarWithNavController(
+ activity: AppCompatActivity,
+ navController: NavController,
+ openableLayout: Openable?
+ ): Unit =
+ setupActionBarWithNavController(
+ activity,
+ navController,
+ AppBarConfiguration.Builder(navController.graph)
+ .setOpenableLayout(openableLayout)
+ .build()
+ )
+
+ /**
+ * Sets up the ActionBar returned by [AppCompatActivity.getSupportActionBar] for use
+ * with a [NavController].
+ *
+ * By calling this method, the title in the action bar will automatically be updated when
+ * the destination changes (assuming there is a valid [label][NavDestination.getLabel]).
+ *
+ * The [AppBarConfiguration] you provide controls how the Navigation button is
+ * displayed.
+ * Call [.navigateUp] to handle the Up button.
+ *
+ * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+ *
+ * @param activity The activity hosting the action bar that should be kept in sync with changes
+ * to the NavController.
+ * @param navController The NavController whose navigation actions will be reflected
+ * in the title of the action bar.
+ * @param configuration Additional configuration options for customizing the behavior of the
+ * ActionBar
+ */
+ @JvmStatic
+ @JvmOverloads
+ public fun setupActionBarWithNavController(
+ activity: AppCompatActivity,
+ navController: NavController,
+ configuration: AppBarConfiguration =
+ AppBarConfiguration.Builder(navController.graph).build()
+ ): Unit =
+ navController.addOnDestinationChangedListener(
+ ActionBarOnDestinationChangedListener(activity, configuration)
+ )
+
+ /**
+ * Sets up a [Toolbar] for use with a [NavController].
+ *
+ * By calling this method, the title in the Toolbar will automatically be updated when
+ * the destination changes (assuming there is a valid [label][NavDestination.getLabel]).
+ *
+ * The start destination of your navigation graph is considered the only top level
+ * destination. On the start destination of your navigation graph, the Toolbar will show
+ * the drawer icon if the given Openable layout is non null. On all other destinations,
+ * the Toolbar will show the Up button. This method will call
+ * [.navigateUp] when the Navigation button is clicked.
+ *
+ * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+ *
+ * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
+ * @param navController The NavController whose navigation actions will be reflected
+ * in the title of the Toolbar.
+ * @param openableLayout The Openable layout that should be toggled from the Navigation button
+ * @see .setupWithNavController
+ */
+ @JvmStatic
+ public fun setupWithNavController(
+ toolbar: Toolbar,
+ navController: NavController,
+ openableLayout: Openable?
+ ): Unit =
+ setupWithNavController(
+ toolbar,
+ navController,
+ AppBarConfiguration.Builder(navController.graph)
+ .setOpenableLayout(openableLayout)
+ .build()
+ )
+
+ /**
+ * Sets up a [Toolbar] for use with a [NavController].
+ *
+ * By calling this method, the title in the Toolbar will automatically be updated when
+ * the destination changes (assuming there is a valid [label][NavDestination.getLabel]).
+ *
+ * The [AppBarConfiguration] you provide controls how the Navigation button is
+ * displayed and what action is triggered when the Navigation button is tapped. This method
+ * will call [.navigateUp] when the Navigation button
+ * is clicked.
+ *
+ * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+ *
+ * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
+ * @param navController The NavController whose navigation actions will be reflected
+ * in the title of the Toolbar.
+ * @param configuration Additional configuration options for customizing the behavior of the
+ * Toolbar
+ */
+ @JvmStatic
+ @JvmOverloads
+ public fun setupWithNavController(
+ toolbar: Toolbar,
+ navController: NavController,
+ configuration: AppBarConfiguration =
+ AppBarConfiguration.Builder(navController.graph).build()
+ ) {
+ navController.addOnDestinationChangedListener(
+ ToolbarOnDestinationChangedListener(toolbar, configuration)
+ )
+ toolbar.setNavigationOnClickListener { navigateUp(navController, configuration) }
+ }
+
+ /**
+ * Sets up a [CollapsingToolbarLayout] and [Toolbar] for use with a
+ * [NavController].
+ *
+ * By calling this method, the title in the CollapsingToolbarLayout will automatically be
+ * updated when the destination changes (assuming there is a valid
+ * [label][NavDestination.getLabel]).
+ *
+ * The start destination of your navigation graph is considered the only top level
+ * destination. On the start destination of your navigation graph, the Toolbar will show
+ * the drawer icon if the given Openable layout is non null. On all other destinations,
+ * the Toolbar will show the Up button. This method will call
+ * [.navigateUp] when the Navigation button is clicked.
+ *
+ * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+ *
+ * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
+ * changes to the NavController.
+ * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
+ * @param navController The NavController whose navigation actions will be reflected
+ * in the title of the Toolbar.
+ * @param openableLayout The Openable layout that should be toggled from the Navigation button
+ */
+ @JvmStatic
+ public fun setupWithNavController(
+ collapsingToolbarLayout: CollapsingToolbarLayout,
+ toolbar: Toolbar,
+ navController: NavController,
+ openableLayout: Openable?
+ ): Unit =
+ setupWithNavController(
+ collapsingToolbarLayout, toolbar, navController,
+ AppBarConfiguration.Builder(navController.graph)
+ .setOpenableLayout(openableLayout)
+ .build()
+ )
+
+ /**
+ * Sets up a [CollapsingToolbarLayout] and [Toolbar] for use with a
+ * [NavController].
+ *
+ * By calling this method, the title in the CollapsingToolbarLayout will automatically be
+ * updated when the destination changes (assuming there is a valid
+ * [label][NavDestination.getLabel]).
+ *
+ * The [AppBarConfiguration] you provide controls how the Navigation button is
+ * displayed and what action is triggered when the Navigation button is tapped. This method
+ * will call [.navigateUp] when the Navigation button
+ * is clicked.
+ *
+ * Destinations that implement [androidx.navigation.FloatingWindow] will be ignored.
+ *
+ * @param collapsingToolbarLayout The CollapsingToolbarLayout that should be kept in sync with
+ * changes to the NavController.
+ * @param toolbar The Toolbar that should be kept in sync with changes to the NavController.
+ * @param navController The NavController whose navigation actions will be reflected
+ * in the title of the Toolbar.
+ * @param configuration Additional configuration options for customizing the behavior of the
+ * Toolbar
+ */
+ @JvmStatic
+ @JvmOverloads
+ public fun setupWithNavController(
+ collapsingToolbarLayout: CollapsingToolbarLayout,
+ toolbar: Toolbar,
+ navController: NavController,
+ configuration: AppBarConfiguration =
+ AppBarConfiguration.Builder(navController.graph).build()
+ ) {
+ navController.addOnDestinationChangedListener(
+ CollapsingToolbarOnDestinationChangedListener(
+ collapsingToolbarLayout, toolbar, configuration
+ )
+ )
+ toolbar.setNavigationOnClickListener { navigateUp(navController, configuration) }
+ }
+
+ /**
+ * Sets up a [NavigationView] for use with a [NavController]. This will call
+ * [.onNavDestinationSelected] when a menu item is selected.
+ * The selected item in the NavigationView will automatically be updated when the destination
+ * changes.
+ *
+ * If the [NavigationView] is directly contained with an [Openable] layout,
+ * it will be closed when a menu item is selected.
+ *
+ * Similarly, if the [NavigationView] has a [BottomSheetBehavior] associated with
+ * it (as is the case when using a [com.google.android.material.bottomsheet.BottomSheetDialog]),
+ * the bottom sheet will be hidden when a menu item is selected.
+ *
+ * @param navigationView The NavigationView that should be kept in sync with changes to the
+ * NavController.
+ * @param navController The NavController that supplies the primary and secondary menu.
+ * Navigation actions on this NavController will be reflected in the
+ * selected item in the NavigationView.
+ */
+ @JvmStatic
+ public fun setupWithNavController(
+ navigationView: NavigationView,
+ navController: NavController
+ ) {
+ navigationView.setNavigationItemSelectedListener { item ->
+ val handled = onNavDestinationSelected(item, navController)
+ if (handled) {
+ val parent = navigationView.parent
+ if (parent is Openable) {
+ parent.close()
+ } else {
+ val bottomSheetBehavior = findBottomSheetBehavior(navigationView)
+ if (bottomSheetBehavior != null) {
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
+ }
+ }
+ }
+ handled
+ }
+ val weakReference = WeakReference(navigationView)
+ navController.addOnDestinationChangedListener(
+ object : NavController.OnDestinationChangedListener {
+ override fun onDestinationChanged(
+ controller: NavController,
+ destination: NavDestination,
+ arguments: Bundle?
+ ) {
+ val view = weakReference.get()
+ if (view == null) {
+ navController.removeOnDestinationChangedListener(this)
+ return
+ }
+ view.menu.forEach { item ->
+ item.isChecked = matchDestination(destination, item.itemId)
+ }
+ }
+ })
+ }
+
+ /**
+ * Walks up the view hierarchy, trying to determine if the given View is contained within
+ * a bottom sheet.
+ */
+ @JvmStatic
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public fun findBottomSheetBehavior(view: View): BottomSheetBehavior<*>? {
+ val params = view.layoutParams
+ if (params !is CoordinatorLayout.LayoutParams) {
+ val parent = view.parent
+ return if (parent is View) {
+ findBottomSheetBehavior(parent as View)
+ } else null
+ }
+ val behavior = params
+ .behavior
+ return if (behavior !is BottomSheetBehavior<*>) {
+ // We hit a CoordinatorLayout, but the View doesn't have the BottomSheetBehavior
+ null
+ } else behavior
+ }
+
+ /**
+ * Sets up a [BottomNavigationView] for use with a [NavController]. This will call
+ * [.onNavDestinationSelected] when a menu item is selected. The
+ * selected item in the BottomNavigationView will automatically be updated when the destination
+ * changes.
+ *
+ * @param bottomNavigationView The BottomNavigationView that should be kept in sync with
+ * changes to the NavController.
+ * @param navController The NavController that supplies the primary menu.
+ * Navigation actions on this NavController will be reflected in the
+ * selected item in the BottomNavigationView.
+ */
+ @JvmStatic
+ public fun setupWithNavController(
+ bottomNavigationView: BottomNavigationView,
+ navController: NavController
+ ) {
+ bottomNavigationView.setOnNavigationItemSelectedListener { item ->
+ onNavDestinationSelected(
+ item,
+ navController
+ )
+ }
+ val weakReference = WeakReference(bottomNavigationView)
+ navController.addOnDestinationChangedListener(
+ object : NavController.OnDestinationChangedListener {
+ override fun onDestinationChanged(
+ controller: NavController,
+ destination: NavDestination,
+ arguments: Bundle?
+ ) {
+ val view = weakReference.get()
+ if (view == null) {
+ navController.removeOnDestinationChangedListener(this)
+ return
+ }
+ view.menu.forEach { item ->
+ if (matchDestination(destination, item.itemId)) {
+ item.isChecked = true
+ }
+ }
+ }
+ })
+ }
+
+ /**
+ * Determines whether the given `destId` matches the NavDestination. This handles
+ * both the default case (the destination's id matches the given id) and the nested case where
+ * the given id is a parent/grandparent/etc of the destination.
+ */
+ @JvmStatic
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public fun matchDestination(destination: NavDestination, @IdRes destId: Int): Boolean {
+ var currentDestination: NavDestination? = destination
+ while (currentDestination!!.id != destId && currentDestination.parent != null) {
+ currentDestination = currentDestination.parent
+ }
+ return currentDestination.id == destId
+ }
+
+ /**
+ * Determines whether the given `destinationIds` match the NavDestination. This
+ * handles both the default case (the destination's id is in the given ids) and the nested
+ * case where the given ids is a parent/grandparent/etc of the destination.
+ */
+ @JvmStatic
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public fun matchDestinations(destination: NavDestination, destinationIds: Set<Int?>): Boolean =
+ generateSequence(destination) { it.parent }.all { destinationIds.contains(it.id) }
+
+ /**
+ * Finds the actual start destination of the graph, handling cases where the graph's starting
+ * destination is itself a NavGraph.
+ */
+ @JvmStatic
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public fun findStartDestination(graph: NavGraph): NavDestination =
+ generateSequence(graph.findNode(graph.startDestination)) {
+ if (it is NavGraph) {
+ it.findNode(it.startDestination)
+ } else {
+ null
+ }
+ }.last()
+}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ToolbarOnDestinationChangedListener.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ToolbarOnDestinationChangedListener.kt
index 4e8b808..a5df7f9 100644
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ToolbarOnDestinationChangedListener.kt
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ToolbarOnDestinationChangedListener.kt
@@ -51,7 +51,7 @@
super.onDestinationChanged(controller, destination, arguments)
}
- override fun setTitle(title: CharSequence) {
+ override fun setTitle(title: CharSequence?) {
toolbarWeakReference.get()?.let { toolbar ->
toolbar.title = title
}
diff --git a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
index c5d7845..0dec76a 100644
--- a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
@@ -24,6 +24,8 @@
import androidx.paging.LoadType.PREPEND
import androidx.paging.LoadType.REFRESH
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
@@ -44,11 +46,17 @@
dataSource.invalidate()
}
- // LegacyPagingSource registers invalidate callback after DataSource is created, so we
- // need to check for race condition here. If DataSource is already invalid, simply
- // propagate invalidation manually.
- if (!invalid && dataSource.isInvalid) {
- invalidate()
+ // dataSource.isInvalid is a @WorkerThread function, so it must be called on
+ // fetchDispatcher. This is normally given since LegacyPagingSource should never be
+ // instantiated on @MainThread, but this workaround exists for Room's current
+ // implementation which is a common use-case. See b/178636235.
+ GlobalScope.launch(fetchDispatcher) {
+ // LegacyPagingSource registers invalidate callback after DataSource is created, so we
+ // need to check for race condition here. If DataSource is already invalid, simply
+ // propagate invalidation manually.
+ if (!invalid && dataSource.isInvalid) {
+ invalidate()
+ }
}
}
diff --git a/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
index add2986..ce0e159 100644
--- a/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
@@ -17,6 +17,7 @@
package androidx.paging
import androidx.paging.PagingSource.LoadResult.Page
+import androidx.testutils.TestDispatcher
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
@@ -53,6 +54,37 @@
)
@Test
+ fun init_invalidateOnFetchDispatcher() {
+ val testDispatcher = TestDispatcher()
+ val dataSource = object : DataSource<Int, Int>(KeyType.ITEM_KEYED) {
+ var isInvalidCalls = 0
+
+ override val isInvalid: Boolean
+ get() {
+ isInvalidCalls++
+ return super.isInvalid
+ }
+
+ override suspend fun load(params: Params<Int>): BaseResult<Int> {
+ return BaseResult(listOf(), null, null)
+ }
+
+ override fun getKeyInternal(item: Int): Int = 0
+ }
+
+ // init will immediately trigger a call to DataSource.isInvalid, but if it's launched on
+ // fetchDispatcher, it should block on testDispatcher.executeAll().
+ LegacyPagingSource(
+ fetchDispatcher = testDispatcher,
+ dataSource = dataSource,
+ )
+
+ assertEquals(0, dataSource.isInvalidCalls)
+ testDispatcher.executeAll()
+ assertEquals(1, dataSource.isInvalidCalls)
+ }
+
+ @Test
fun item() {
@Suppress("DEPRECATION")
val dataSource = object : ItemKeyedDataSource<Int, String>() {
@@ -330,17 +362,22 @@
}
}
- val pagingSourceFactory = dataSourceFactory.asPagingSourceFactory().let {
+ val testDispatcher = TestDispatcher()
+ val pagingSourceFactory = dataSourceFactory.asPagingSourceFactory(
+ fetchDispatcher = testDispatcher
+ ).let {
{ it() as LegacyPagingSource }
}
val pagingSource0 = pagingSourceFactory()
+ testDispatcher.executeAll()
assertTrue { pagingSource0.dataSource.isInvalid }
assertTrue { pagingSource0.invalid }
assertTrue { dataSourceFactory.dataSources[0].isInvalid }
assertEquals(dataSourceFactory.dataSources[0], pagingSource0.dataSource)
val pagingSource1 = pagingSourceFactory()
+ testDispatcher.executeAll()
assertFalse { pagingSource1.dataSource.isInvalid }
assertFalse { pagingSource1.invalid }
assertFalse { dataSourceFactory.dataSources[1].isInvalid }
diff --git a/settings.gradle b/settings.gradle
index 41e1eb6..9e47bd3 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -138,6 +138,7 @@
includeProject(":appcompat:appcompat-benchmark", "appcompat/appcompat-benchmark", [BuildType.MAIN])
includeProject(":appcompat:appcompat-lint", "appcompat/appcompat-lint", [BuildType.MAIN])
includeProject(":appcompat:appcompat-resources", "appcompat/appcompat-resources", [BuildType.MAIN])
+includeProject(":appcompat:integration-tests:receive-content-testapp", "appcompat/integration-tests/receive-content-testapp", [BuildType.MAIN])
includeProject(":appsearch:appsearch", "appsearch/appsearch", [BuildType.MAIN])
includeProject(":appsearch:appsearch-compiler", "appsearch/compiler", [BuildType.MAIN])
includeProject(":appsearch:appsearch-local-storage", "appsearch/local-storage", [BuildType.MAIN])
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index c2aac9f..aa88d92 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -546,14 +546,16 @@
private val batteryLowAndNotChargingObserver = Observer<Boolean> {
// To save power we request a lower hardware display frame rate when the battery is low
// and not charging.
- renderer.surfaceHolder.surface.setFrameRate(
- if (it) {
- 1000f / MAX_LOW_POWER_INTERACTIVE_UPDATE_RATE_MS.toFloat()
- } else {
- SYSTEM_DECIDES_FRAME_RATE
- },
- FRAME_RATE_COMPATIBILITY_DEFAULT
- )
+ if (renderer.surfaceHolder.surface.isValid) {
+ renderer.surfaceHolder.surface.setFrameRate(
+ if (it) {
+ 1000f / MAX_LOW_POWER_INTERACTIVE_UPDATE_RATE_MS.toFloat()
+ } else {
+ SYSTEM_DECIDES_FRAME_RATE
+ },
+ FRAME_RATE_COMPATIBILITY_DEFAULT
+ )
+ }
}
init {
diff --git a/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java b/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java
index 5acba49..0c85963 100644
--- a/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java
+++ b/window/window/src/androidTest/java/androidx/window/SidecarCompatTest.java
@@ -364,12 +364,13 @@
mSidecarCompat.setExtensionCallback(listener);
when(mSidecarCompat.mSidecar.getWindowLayoutInfo(any())).thenReturn(layoutInfo);
View fakeView = mock(View.class);
+ Window fakeWindow = new TestWindow(mActivity, fakeView);
doAnswer(invocation -> {
View.OnAttachStateChangeListener stateChangeListener = invocation.getArgument(0);
+ fakeWindow.getAttributes().token = mock(IBinder.class);
stateChangeListener.onViewAttachedToWindow(fakeView);
return null;
}).when(fakeView).addOnAttachStateChangeListener(any());
- Window fakeWindow = new TestWindow(mActivity, fakeView);
when(mActivity.getWindow()).thenReturn(fakeWindow);
mSidecarCompat.onWindowLayoutChangeListenerAdded(mActivity);
diff --git a/window/window/src/test/java/androidx/window/ActivityTestUtil.java b/window/window/src/main/java/androidx/window/ActivityUtil.java
similarity index 67%
rename from window/window/src/test/java/androidx/window/ActivityTestUtil.java
rename to window/window/src/main/java/androidx/window/ActivityUtil.java
index 2d6d5d7..48839f20 100644
--- a/window/window/src/test/java/androidx/window/ActivityTestUtil.java
+++ b/window/window/src/main/java/androidx/window/ActivityUtil.java
@@ -19,13 +19,17 @@
import android.app.Activity;
import android.os.IBinder;
-import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
-public class ActivityTestUtil {
+final class ActivityUtil {
- private ActivityTestUtil() { }
+ private ActivityUtil() {}
- static IBinder getActivityWindowToken(@NonNull Activity activity) {
- return activity.getWindow().getAttributes().token;
+ @Nullable
+ static IBinder getActivityWindowToken(@Nullable Activity activity) {
+ if (activity == null) {
+ return null;
+ }
+ return activity.getWindow() != null ? activity.getWindow().getAttributes().token : null;
}
}
diff --git a/window/window/src/main/java/androidx/window/SidecarCompat.java b/window/window/src/main/java/androidx/window/SidecarCompat.java
index 4e64397..f0f850c 100644
--- a/window/window/src/main/java/androidx/window/SidecarCompat.java
+++ b/window/window/src/main/java/androidx/window/SidecarCompat.java
@@ -16,6 +16,7 @@
package androidx.window;
+import static androidx.window.ActivityUtil.getActivityWindowToken;
import static androidx.window.ExtensionCompat.DEBUG;
import static androidx.window.Version.VERSION_0_1;
@@ -38,6 +39,7 @@
import androidx.window.sidecar.SidecarProvider;
import androidx.window.sidecar.SidecarWindowLayoutInfo;
+import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -142,17 +144,19 @@
if (windowToken != null) {
register(windowToken, activity);
} else {
- FirstAttachAdapter attachAdapter = new FirstAttachAdapter(() -> {
- IBinder token = getActivityWindowToken(activity);
- register(token, activity);
- });
+ FirstAttachAdapter attachAdapter = new FirstAttachAdapter(this, activity);
activity.getWindow().getDecorView().addOnAttachStateChangeListener(attachAdapter);
}
}
- private void register(IBinder windowToken, Activity activity) {
+ /**
+ * Register an {@link IBinder} token and an {@link Activity} so that the given
+ * {@link Activity} will receive updates when there is a new {@link WindowLayoutInfo}.
+ * @param windowToken for the given {@link Activity}.
+ * @param activity that is listening for changes of {@link WindowLayoutInfo}
+ */
+ void register(@NonNull IBinder windowToken, @NonNull Activity activity) {
mWindowListenerRegisteredContexts.put(windowToken, activity);
-
mSidecar.onWindowLayoutChangeListenerAdded(windowToken);
mExtensionCallback.onWindowLayoutChanged(activity, getWindowLayoutInfo(activity));
}
@@ -319,31 +323,41 @@
}
}
- @Nullable
- private IBinder getActivityWindowToken(Activity activity) {
- return activity.getWindow() != null ? activity.getWindow().getAttributes().token : null;
- }
-
/**
* An adapter that will run a callback when a window is attached and then be removed from the
* listener set.
*/
private static class FirstAttachAdapter implements View.OnAttachStateChangeListener {
- private final Runnable mCallback;
+ private final SidecarCompat mSidecarCompat;
+ private final WeakReference<Activity> mActivityWeakReference;
- FirstAttachAdapter(Runnable callback) {
- mCallback = callback;
+ FirstAttachAdapter(SidecarCompat sidecarCompat, Activity activity) {
+ mSidecarCompat = sidecarCompat;
+ mActivityWeakReference = new WeakReference<>(activity);
}
@Override
public void onViewAttachedToWindow(View view) {
- mCallback.run();
view.removeOnAttachStateChangeListener(this);
+ Activity activity = mActivityWeakReference.get();
+ IBinder token = getActivityWindowToken(activity);
+ if (activity == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Unable to register activity since activity is missing");
+ }
+ return;
+ }
+ if (token == null) {
+ if (DEBUG) {
+ Log.w(TAG, "Unable to register activity since the window token is missing");
+ }
+ return;
+ }
+ mSidecarCompat.register(token, activity);
}
@Override
- public void onViewDetachedFromWindow(View view) {
- }
+ public void onViewDetachedFromWindow(View view) { }
}
}
diff --git a/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java b/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java
index a7b9da2..31cba768 100644
--- a/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java
+++ b/window/window/src/test/java/androidx/window/SidecarCompatUnitTest.java
@@ -16,7 +16,7 @@
package androidx.window;
-import static androidx.window.ActivityTestUtil.getActivityWindowToken;
+import static androidx.window.ActivityUtil.getActivityWindowToken;
import static androidx.window.TestBoundsUtil.invalidFoldBounds;
import static androidx.window.TestBoundsUtil.invalidHingeBounds;
import static androidx.window.TestBoundsUtil.validFoldBound;