Merge "Avoid reallocating capture-less composable lambdas, deprecate emptyContent and orEmpty helpers" 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/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/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/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/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/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/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/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/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/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/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;