Merge "Migrated update_tracing_perfetto.sh code to Python" into androidx-main
diff --git a/activity/activity/api/current.txt b/activity/activity/api/current.txt
index dd1dd37..a6fafc74 100644
--- a/activity/activity/api/current.txt
+++ b/activity/activity/api/current.txt
@@ -73,11 +73,12 @@
}
public abstract class OnBackPressedCallback {
- ctor public OnBackPressedCallback(boolean);
+ ctor public OnBackPressedCallback(boolean isEnabled);
method @MainThread public abstract void handleOnBackPressed();
method @MainThread public final boolean isEnabled();
method @MainThread public final void remove();
method @MainThread public final void setEnabled(boolean);
+ property @MainThread public final boolean isEnabled;
}
public final class OnBackPressedDispatcher {
@@ -141,8 +142,8 @@
field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult!> CREATOR;
}
- public interface ActivityResultCallback<O> {
- method public void onActivityResult(O!);
+ public fun interface ActivityResultCallback<O> {
+ method public void onActivityResult(O? result);
}
public interface ActivityResultCaller {
diff --git a/activity/activity/api/public_plus_experimental_current.txt b/activity/activity/api/public_plus_experimental_current.txt
index dd1dd37..a6fafc74 100644
--- a/activity/activity/api/public_plus_experimental_current.txt
+++ b/activity/activity/api/public_plus_experimental_current.txt
@@ -73,11 +73,12 @@
}
public abstract class OnBackPressedCallback {
- ctor public OnBackPressedCallback(boolean);
+ ctor public OnBackPressedCallback(boolean isEnabled);
method @MainThread public abstract void handleOnBackPressed();
method @MainThread public final boolean isEnabled();
method @MainThread public final void remove();
method @MainThread public final void setEnabled(boolean);
+ property @MainThread public final boolean isEnabled;
}
public final class OnBackPressedDispatcher {
@@ -141,8 +142,8 @@
field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult!> CREATOR;
}
- public interface ActivityResultCallback<O> {
- method public void onActivityResult(O!);
+ public fun interface ActivityResultCallback<O> {
+ method public void onActivityResult(O? result);
}
public interface ActivityResultCaller {
diff --git a/activity/activity/api/restricted_current.txt b/activity/activity/api/restricted_current.txt
index 64662e2..cf252d3 100644
--- a/activity/activity/api/restricted_current.txt
+++ b/activity/activity/api/restricted_current.txt
@@ -72,11 +72,12 @@
}
public abstract class OnBackPressedCallback {
- ctor public OnBackPressedCallback(boolean);
+ ctor public OnBackPressedCallback(boolean isEnabled);
method @MainThread public abstract void handleOnBackPressed();
method @MainThread public final boolean isEnabled();
method @MainThread public final void remove();
method @MainThread public final void setEnabled(boolean);
+ property @MainThread public final boolean isEnabled;
}
public final class OnBackPressedDispatcher {
@@ -140,8 +141,8 @@
field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult!> CREATOR;
}
- public interface ActivityResultCallback<O> {
- method public void onActivityResult(O!);
+ public fun interface ActivityResultCallback<O> {
+ method public void onActivityResult(O? result);
}
public interface ActivityResultCaller {
diff --git a/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt b/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt
index dad575f..a6af035 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/result/ActivityResultRegistryTest.kt
@@ -59,12 +59,10 @@
// register for the result
val activityResult = registry.register(
- "test", lifecycleOwner,
- TakePicturePreview(),
- ActivityResultCallback {
- resultReturned = true
- }
- )
+ "test", lifecycleOwner, TakePicturePreview()
+ ) {
+ resultReturned = true
+ }
// move the state to started
lifecycleOwner.currentState = Lifecycle.State.STARTED
@@ -82,8 +80,8 @@
// register for the result
val activityResult = registry.register(
"test", lifecycleOwner,
- TakePicturePreview(), ActivityResultCallback {}
- )
+ TakePicturePreview()
+ ) {}
// saved the state of the registry
val state = Bundle()
@@ -101,11 +99,10 @@
var resultReturned = false
// re-register for the result that should have been saved
registry.register(
- "test", lifecycleOwner, TakePicturePreview(),
- ActivityResultCallback {
- resultReturned = true
- }
- )
+ "test", lifecycleOwner, TakePicturePreview()
+ ) {
+ resultReturned = true
+ }
lifecycleOwner.currentState = Lifecycle.State.STARTED
@@ -119,9 +116,8 @@
try {
// register for the result
registry.register(
- "test", lifecycleOwner,
- TakePicturePreview(), ActivityResultCallback {}
- )
+ "test", lifecycleOwner, TakePicturePreview()
+ ) {}
fail("Registering for activity result after Lifecycle ON_CREATE should fail")
} catch (e: IllegalStateException) {
assertThat(e).hasMessageThat().contains(
@@ -138,9 +134,8 @@
// register for the result
val activityResult = registry.register(
- "test", lifecycleOwner,
- TakePicturePreview(), ActivityResultCallback {}
- )
+ "test", lifecycleOwner, TakePicturePreview()
+ ) {}
// saved the state of the registry
val state = Bundle()
@@ -155,11 +150,10 @@
var resultReturned = false
// re-register for the result that should have been saved
registry.register(
- "test", lifecycleOwner, TakePicturePreview(),
- ActivityResultCallback {
- resultReturned = true
- }
- )
+ "test", lifecycleOwner, TakePicturePreview()
+ ) {
+ resultReturned = true
+ }
// launch the result
activityResult.launch(null)
@@ -200,10 +194,9 @@
var resultReturned = false
val activityResult = dispatchResultRegistry.register(
"test", lifecycleOwner, TakePicture(),
- ActivityResultCallback {
- resultReturned = true
- }
- )
+ ) {
+ resultReturned = true
+ }
// launch the result
activityResult.launch(null)
@@ -244,10 +237,9 @@
var resultReturned = false
val activityResult = dispatchResultRegistry.register(
"test", lifecycleOwner, TakePicturePreview(),
- ActivityResultCallback {
- resultReturned = true
- }
- )
+ ) {
+ resultReturned = true
+ }
// launch the result
activityResult.launch(null)
@@ -276,9 +268,8 @@
// register for the result
val activityResult = registry.register(
- "test", lifecycleOwner,
- TakePicturePreview(), ActivityResultCallback {}
- )
+ "test", lifecycleOwner, TakePicturePreview()
+ ) {}
// saved the state of the registry
val state = Bundle()
@@ -296,11 +287,10 @@
var resultReturned = false
// re-register for the result that should have been saved
registry.register(
- "test", lifecycleOwner, TakePicturePreview(),
- ActivityResultCallback {
- resultReturned = true
- }
- )
+ "test", lifecycleOwner, TakePicturePreview()
+ ) {
+ resultReturned = true
+ }
// move to CREATED and make sure the callback is not fired
lifecycleOwner.currentState = Lifecycle.State.CREATED
diff --git a/activity/activity/src/main/java/androidx/activity/Cancellable.java b/activity/activity/src/main/java/androidx/activity/Cancellable.kt
similarity index 90%
rename from activity/activity/src/main/java/androidx/activity/Cancellable.java
rename to activity/activity/src/main/java/androidx/activity/Cancellable.kt
index a5cb90a..33af37c 100644
--- a/activity/activity/src/main/java/androidx/activity/Cancellable.java
+++ b/activity/activity/src/main/java/androidx/activity/Cancellable.kt
@@ -13,17 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package androidx.activity;
+package androidx.activity
/**
* Token representing a cancellable operation.
*/
-interface Cancellable {
-
+internal interface Cancellable {
/**
* Cancel the subscription. This call should be idempotent, making it safe to
* call multiple times.
*/
- void cancel();
-}
+ fun cancel()
+}
\ No newline at end of file
diff --git a/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.java b/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.java
deleted file mode 100644
index afa44f3..0000000
--- a/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.java
+++ /dev/null
@@ -1,122 +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.activity;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.OptIn;
-import androidx.core.os.BuildCompat;
-import androidx.core.util.Consumer;
-import androidx.lifecycle.LifecycleOwner;
-
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * Class for handling {@link OnBackPressedDispatcher#onBackPressed()} callbacks without
- * strongly coupling that implementation to a subclass of {@link ComponentActivity}.
- * <p>
- * This class maintains its own {@link #isEnabled() enabled state}. Only when this callback
- * is enabled will it receive callbacks to {@link #handleOnBackPressed()}.
- * <p>
- * Note that the enabled state is an additional layer on top of the
- * {@link androidx.lifecycle.LifecycleOwner} passed to
- * {@link OnBackPressedDispatcher#addCallback(LifecycleOwner, OnBackPressedCallback)}
- * which controls when the callback is added and removed to the dispatcher.
- * <p>
- * By calling {@link #remove()}, this callback will be removed from any
- * {@link OnBackPressedDispatcher} it has been added to. It is strongly recommended
- * to instead disable this callback to handle temporary changes in state.
- *
- * @see ComponentActivity#getOnBackPressedDispatcher()
- */
-public abstract class OnBackPressedCallback {
-
- private boolean mEnabled;
- private CopyOnWriteArrayList<Cancellable> mCancellables = new CopyOnWriteArrayList<>();
- private Consumer<Boolean> mEnabledConsumer;
-
- /**
- * Create a {@link OnBackPressedCallback}.
- *
- * @param enabled The default enabled state for this callback.
- * @see #setEnabled(boolean)
- */
- public OnBackPressedCallback(boolean enabled) {
- mEnabled = enabled;
- }
-
- /**
- * Set the enabled state of the callback. Only when this callback
- * is enabled will it receive callbacks to {@link #handleOnBackPressed()}.
- * <p>
- * Note that the enabled state is an additional layer on top of the
- * {@link androidx.lifecycle.LifecycleOwner} passed to
- * {@link OnBackPressedDispatcher#addCallback(LifecycleOwner, OnBackPressedCallback)}
- * which controls when the callback is added and removed to the dispatcher.
- *
- * @param enabled whether the callback should be considered enabled
- */
- @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
- @MainThread
- public final void setEnabled(boolean enabled) {
- mEnabled = enabled;
- if (mEnabledConsumer != null) {
- mEnabledConsumer.accept(mEnabled);
- }
- }
-
- /**
- * Checks whether this callback should be considered enabled. Only when this callback
- * is enabled will it receive callbacks to {@link #handleOnBackPressed()}.
- *
- * @return Whether this callback should be considered enabled.
- */
- @MainThread
- public final boolean isEnabled() {
- return mEnabled;
- }
-
- /**
- * Removes this callback from any {@link OnBackPressedDispatcher} it is currently
- * added to.
- */
- @MainThread
- public final void remove() {
- for (Cancellable cancellable: mCancellables) {
- cancellable.cancel();
- }
- }
-
- /**
- * Callback for handling the {@link OnBackPressedDispatcher#onBackPressed()} event.
- */
- @MainThread
- public abstract void handleOnBackPressed();
-
- void addCancellable(@NonNull Cancellable cancellable) {
- mCancellables.add(cancellable);
- }
-
- void removeCancellable(@NonNull Cancellable cancellable) {
- mCancellables.remove(cancellable);
- }
-
- void setIsEnabledConsumer(@Nullable Consumer<Boolean> isEnabled) {
- mEnabledConsumer = isEnabled;
- }
-}
diff --git a/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.kt b/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.kt
new file mode 100644
index 0000000..67efa4b
--- /dev/null
+++ b/activity/activity/src/main/java/androidx/activity/OnBackPressedCallback.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.activity
+
+import androidx.annotation.MainThread
+import androidx.annotation.OptIn
+import androidx.core.os.BuildCompat
+import androidx.core.util.Consumer
+import java.util.concurrent.CopyOnWriteArrayList
+
+/**
+ * Class for handling [OnBackPressedDispatcher.onBackPressed] callbacks without
+ * strongly coupling that implementation to a subclass of [ComponentActivity].
+ *
+ * This class maintains its own [enabled state][isEnabled]. Only when this callback
+ * is enabled will it receive callbacks to [handleOnBackPressed].
+ *
+ * Note that the enabled state is an additional layer on top of the
+ * [androidx.lifecycle.LifecycleOwner] passed to
+ * [OnBackPressedDispatcher.addCallback]
+ * which controls when the callback is added and removed to the dispatcher.
+ *
+ * By calling [remove], this callback will be removed from any
+ * [OnBackPressedDispatcher] it has been added to. It is strongly recommended
+ * to instead disable this callback to handle temporary changes in state.
+ *
+ * @param isEnabled The default enabled state for this callback.
+ *
+ * @see ComponentActivity.getOnBackPressedDispatcher
+ */
+abstract class OnBackPressedCallback(isEnabled: Boolean) {
+ /**
+ * The enabled state of the callback. Only when this callback
+ * is enabled will it receive callbacks to [handleOnBackPressed].
+ *
+ * Note that the enabled state is an additional layer on top of the
+ * [androidx.lifecycle.LifecycleOwner] passed to
+ * [OnBackPressedDispatcher.addCallback]
+ * which controls when the callback is added and removed to the dispatcher.
+ */
+ @get:MainThread
+ @set:MainThread
+ @set:OptIn(markerClass = [BuildCompat.PrereleaseSdkCheck::class])
+ var isEnabled: Boolean = isEnabled
+ set(value) {
+ field = value
+ if (enabledConsumer != null) {
+ enabledConsumer!!.accept(field)
+ }
+ }
+
+ private val cancellables = CopyOnWriteArrayList<Cancellable>()
+ private var enabledConsumer: Consumer<Boolean>? = null
+
+ /**
+ * Removes this callback from any [OnBackPressedDispatcher] it is currently
+ * added to.
+ */
+ @MainThread
+ fun remove() = cancellables.forEach { it.cancel() }
+
+ /**
+ * Callback for handling the [OnBackPressedDispatcher.onBackPressed] event.
+ */
+ @MainThread
+ abstract fun handleOnBackPressed()
+
+ @JvmName("addCancellable")
+ internal fun addCancellable(cancellable: Cancellable) {
+ cancellables.add(cancellable)
+ }
+
+ @JvmName("removeCancellable")
+ internal fun removeCancellable(cancellable: Cancellable) {
+ cancellables.remove(cancellable)
+ }
+
+ @JvmName("setIsEnabledConsumer")
+ internal fun setIsEnabledConsumer(isEnabled: Consumer<Boolean>?) {
+ enabledConsumer = isEnabled
+ }
+}
diff --git a/activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.java b/activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.kt
similarity index 66%
rename from activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.java
rename to activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.kt
index fde2497..1c500f4 100644
--- a/activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.java
+++ b/activity/activity/src/main/java/androidx/activity/result/ActivityResultCallback.kt
@@ -13,23 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package androidx.activity.result
-
-package androidx.activity.result;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
+import android.app.Activity
/**
- * A type-safe callback to be called when an {@link Activity#onActivityResult activity result}
+ * A type-safe callback to be called when an [activity result][Activity.onActivityResult]
* is available.
- *
- * @param <O> result type
*/
-public interface ActivityResultCallback<O> {
-
+fun interface ActivityResultCallback<O> {
/**
* Called when result is available
*/
- void onActivityResult(@SuppressLint("UnknownNullness") O result);
+ fun onActivityResult(result: O)
}
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index c188aea..afb0b80 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -25,7 +25,7 @@
implementation("androidx.collection:collection:1.0.0")
api("androidx.cursoradapter:cursoradapter:1.0.0")
api("androidx.activity:activity:1.6.0")
- api("androidx.fragment:fragment:1.3.6")
+ api("androidx.fragment:fragment:1.5.4")
api(project(":appcompat:appcompat-resources"))
api("androidx.drawerlayout:drawerlayout:1.0.0")
implementation("androidx.lifecycle:lifecycle-runtime:2.5.1")
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
new file mode 100644
index 0000000..73d01c3
--- /dev/null
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark
+
+import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
+import androidx.benchmark.perfetto.PerfettoHelper
+import androidx.benchmark.perfetto.PerfettoTrace
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import kotlin.test.assertFailsWith
+import kotlin.test.assertNotNull
+import kotlin.test.fail
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalPerfettoCaptureApi::class)
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 21)
+class PerfettoTraceTest {
+ @Test
+ fun record_basic() {
+ assumeTrue(PerfettoHelper.isAbiSupported())
+ var perfettoTrace: PerfettoTrace? = null
+ PerfettoTrace.record(
+ fileLabel = "testTrace",
+ traceCallback = { trace ->
+ perfettoTrace = trace
+ }
+ ) {
+ // noop
+ }
+ assertNotNull(perfettoTrace)
+ assert(perfettoTrace!!.path.matches(Regex(".*/testTrace_[0-9-]+.perfetto-trace"))) {
+ "$perfettoTrace didn't match!"
+ }
+ }
+ @Test
+ fun record_reentrant() {
+ assumeTrue(PerfettoHelper.isAbiSupported())
+ var perfettoTrace: PerfettoTrace? = null
+ PerfettoTrace.record(
+ fileLabel = "outer",
+ traceCallback = { trace ->
+ perfettoTrace = trace
+ }
+ ) {
+ // tracing while tracing should fail
+ assertFailsWith<IllegalStateException> {
+ PerfettoTrace.record(
+ fileLabel = "inner",
+ traceCallback = { _ ->
+ fail("inner trace should not complete / record")
+ }
+ ) {
+ // noop
+ }
+ }
+ }
+ assertNotNull(perfettoTrace)
+ }
+}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
index 58a1723..198ab7a 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
@@ -39,6 +39,17 @@
}
}
+ companion object {
+ val inUseLock = Object()
+
+ /**
+ * Prevents re-entrance of perfetto trace capture, as it doesn't handle this correctly
+ *
+ * (Single file output location, process cleanup, etc.)
+ */
+ var inUse = false
+ }
+
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun start(
appTagPackages: List<String>,
@@ -63,23 +74,6 @@
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
- private fun stop(benchmarkName: String, iteration: Int?): String {
- val traceName: String
- val reportKey: String
- if (iteration != null) {
- val iterString = iteration.toString().padStart(3, '0')
- traceName = "${benchmarkName}_iter${iterString}_${dateToFileName()}.perfetto-trace"
- reportKey = "perfetto_trace_$iterString"
- } else {
- traceName = "${benchmarkName}_${dateToFileName()}.perfetto-trace"
- reportKey = "perfetto_trace"
- }
- return Outputs.writeFile(fileName = traceName, reportKey = reportKey) {
- capture!!.stop(it.absolutePath)
- }
- }
-
- @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun stop(traceLabel: String): String {
return Outputs.writeFile(
fileName = "${traceLabel}_${dateToFileName()}.perfetto-trace",
@@ -102,6 +96,17 @@
return null
}
+ synchronized(inUseLock) {
+ if (inUse) {
+ throw IllegalStateException(
+ "Reentrant Perfetto Tracing is not supported." +
+ " This means you cannot use more than one of" +
+ " BenchmarkRule/MacrobenchmarkRule/PerfettoTraceRule/PerfettoTrace.record" +
+ " together."
+ )
+ }
+ inUse = true
+ }
// Prior to Android 11 (R), a shell property must be set to enable perfetto tracing, see
// https://perfetto.dev/docs/quickstart/android-tracing#starting-the-tracing-services
val propOverride = if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
@@ -122,6 +127,9 @@
return path
} finally {
propOverride?.resetIfOverridden()
+ synchronized(inUseLock) {
+ inUse = false
+ }
}
}
}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
index 4d149d2..3e74e58 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
@@ -375,11 +375,8 @@
fun isAbiSupported(): Boolean {
Log.d(LOG_TAG, "Supported ABIs: ${Build.SUPPORTED_ABIS.joinToString()}")
- // Cuttlefish is x86 but claims support for x86_64
- return !Build.MODEL.contains("Cuttlefish") && ( // b/204892353
- Build.SUPPORTED_64_BIT_ABIS.any { SUPPORTED_64_ABIS.contains(it) } ||
- Build.SUPPORTED_32_BIT_ABIS.any { SUPPORTED_32_ABIS.contains(it) }
- )
+ return Build.SUPPORTED_64_BIT_ABIS.any { SUPPORTED_64_ABIS.contains(it) } ||
+ Build.SUPPORTED_32_BIT_ABIS.any { SUPPORTED_32_ABIS.contains(it) }
}
@get:TestOnly
@@ -399,6 +396,8 @@
// supported by a device. That is why we need to search from most specific to
// least specific. For e.g. emulators claim to support aarch64, when in reality
// they can only support x86 or x86_64.
+ // Note: Cuttlefish is x86 but claims support for x86_64
+ Build.MODEL.contains("Cuttlefish") -> "x86" // TODO(204892353): handle properly
Build.SUPPORTED_64_BIT_ABIS.any { it.startsWith("x86_64") } -> "x86_64"
Build.SUPPORTED_32_BIT_ABIS.any { it.startsWith("x86") } -> "x86"
Build.SUPPORTED_64_BIT_ABIS.any { it.startsWith("arm64") } -> "aarch64"
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
index 33bdde3..98ed9d8 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
@@ -35,6 +35,15 @@
/**
* Record a Perfetto System Trace for the specified [block].
*
+ * ```
+ * PerfettoTrace.record("myTrace") {
+ * // content in here is traced to myTrace_<timestamp>.perfetto_trace
+ * }
+ * ```
+ *
+ * Reentrant Perfetto trace capture is not supported, so this API may not be combined with
+ * `BenchmarkRule`, `MacrobenchmarkRule`, or `PerfettoTraceRule`.
+ *
* If the block throws, the trace is still captured and passed to [traceCallback].
*/
@JvmStatic
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt
index e98a49b..a016541 100644
--- a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt
@@ -20,6 +20,8 @@
import java.util.concurrent.atomic.AtomicBoolean
import org.gradle.api.Plugin
import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
@@ -109,15 +111,47 @@
it.xcResultPath.set(runDarwinBenchmarks.flatMap { task ->
task.xcResultPath
})
+ val resultFileName = "${extension.xcodeProjectName.get()}-benchmark-result.json"
it.outputFile.set(
project.layout.buildDirectory.file(
- "${extension.xcodeProjectName.get()}-benchmark-result.json"
+ resultFileName
)
)
+ val provider = project.benchmarksDistributionDirectory(extension)
+ val resultFile = provider.map { directory ->
+ directory.file(resultFileName)
+ }
+ it.distFile.set(resultFile)
+ }
+ }
+
+ private fun Project.benchmarksDistributionDirectory(
+ extension: DarwinBenchmarkPluginExtension
+ ): Provider<Directory> {
+ val distProvider = project.distributionDirectory()
+ val benchmarksDirProvider = distProvider.flatMap { distDir ->
+ extension.xcodeProjectName.map { projectName ->
+ val projectPath = project.path.replace(":", "/")
+ val benchmarksDirectory = File(distDir, DARWIN_BENCHMARKS_DIR)
+ File(benchmarksDirectory, "$projectPath/$projectName")
+ }
+ }
+ return project.layout.dir(benchmarksDirProvider)
+ }
+
+ private fun Project.distributionDirectory(): Provider<File> {
+ // We want to write metrics to library metrics specific location
+ // Context: b/257326666
+ return providers.environmentVariable(DIST_DIR).map { value ->
+ File(value, LIBRARY_METRICS)
}
}
private companion object {
+ // Environment variables
+ const val DIST_DIR = "DIST_DIR"
+ const val LIBRARY_METRICS = "librarymetrics"
+ const val DARWIN_BENCHMARKS_DIR = "darwinBenchmarks"
// Gradle Properties
const val XCODEGEN_DOWNLOAD_URI = "androidx.benchmark.darwin.xcodeGenDownloadUri"
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt
index b95352d..7d4136d 100644
--- a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt
@@ -27,6 +27,7 @@
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
@@ -44,6 +45,10 @@
@get:OutputFile
abstract val outputFile: RegularFileProperty
+ @get:OutputFile
+ @get:Optional
+ abstract val distFile: RegularFileProperty
+
@TaskAction
fun benchmarkResults() {
val xcResultFile = xcResultPath.get().asFile
@@ -68,5 +73,10 @@
.toJson(metrics)
outputFile.get().asFile.writeText(output)
+
+ // Add output to the DIST_DIR when specified
+ if (distFile.isPresent) {
+ distFile.get().asFile.writeText(output)
+ }
}
}
diff --git a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt
index a39994d..aa35558 100644
--- a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt
+++ b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt
@@ -29,6 +29,17 @@
/**
* Add this rule to record a Perfetto trace for each test on Android Lollipop (API 21)+ devices.
*
+ * ```
+ * @RunWith(AndroidJUnit4::class)
+ * class PerfettoOverheadBenchmark {
+ * // traces all tests in file
+ * @get:Rule
+ * val perfettoRule = PerfettoTraceRule()
+ *
+ * @Test
+ * fun test() {}
+ * }
+ * ```
* Captured traces can be observed through any of:
* * Android Studio trace linking under `Benchmark` in test output tab
* * The optional `traceCallback` parameter
@@ -44,6 +55,9 @@
* ```
* > adb pull /storage/emulated/0/Android/data/mypackage.test/files/PerfettoCaptureTest.trace
* ```
+ *
+ * Reentrant Perfetto trace capture is not supported, so this API may not be combined with
+ * `BenchmarkRule`, `MacrobenchmarkRule`, or `PerfettoTrace.record`.
*/
@ExperimentalPerfettoCaptureApi
class PerfettoTraceRule(
diff --git a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
index 080c259..7b09cb7 100644
--- a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
+++ b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/BaselineProfileRule.kt
@@ -44,7 +44,7 @@
*
* @Test
* fun startup() = baselineProfileRule.collectBaselineProfile(
- * packageName = "com.example.app"
+ * packageName = "com.example.my.application.id"
* ) {
* pressHome()
* // This block defines the app's critical user journey. Here we are
@@ -78,10 +78,12 @@
}
/**
- * Collects baseline profiles for a critical user journey.
- * @param packageName Package name of the app for which profiles are to be generated.
+ * Collects baseline profiles for a set of interactions with the application
+ * @param packageName ApplicationId / Application manifest package name of the app for
+ * which profiles are generated.
* @param packageFilters List of package names to use as a filter for the generated profiles.
- * By default no filters are applied. Note that this works only when the code is not obfuscated.
+ * By default no filters are applied. Note that this works only when the code is not
+ * obfuscated.
* @param [profileBlock] defines the critical user journey.
*/
@JvmOverloads
diff --git a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt
index f14b0bd9..d8a01b7 100644
--- a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt
+++ b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt
@@ -40,7 +40,7 @@
*
* @Test
* fun startup() = benchmarkRule.measureRepeated(
- * packageName = "mypackage.myapp",
+ * packageName = "com.example.my.application.id"
* metrics = listOf(StartupTimingMetric()),
* iterations = 5,
* startupMode = StartupMode.COLD,
@@ -70,13 +70,14 @@
* compile(compilationMode)
* repeat(iterations) {
* setupBlock()
- * captureTrace {
+ * captureTraceAndMetrics {
* measureBlock()
* }
* }
* ```
*
- * @param packageName Package name of the app being measured.
+ * @param packageName ApplicationId / Application manifest package name of the app for
+ * which profiles are generated.
* @param metrics List of metrics to measure.
* @param compilationMode Mode of compilation used before capturing measurement, such as
* [CompilationMode.Partial], defaults to [CompilationMode.DEFAULT].
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
index 1db82176..de94880 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
@@ -17,6 +17,7 @@
package androidx.benchmark.macro
import androidx.annotation.RequiresApi
+import androidx.benchmark.DeviceInfo
import androidx.benchmark.perfetto.PerfettoHelper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
@@ -29,12 +30,19 @@
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
+import org.junit.Assume.assumeFalse
+import org.junit.Before
@RunWith(AndroidJUnit4::class)
@SmallTest
@OptIn(ExperimentalMacrobenchmarkApi::class)
class MacrobenchmarkTest {
+ @Before
+ fun setUp() {
+ assumeFalse(DeviceInfo.isEmulator)
+ }
+
@Test
fun macrobenchmarkWithStartupMode_emptyMetricList() {
val exception = assertFailsWith<IllegalArgumentException> {
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
index b3561e8..4deb295 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
@@ -20,6 +20,7 @@
import android.content.Intent
import android.os.Build
import androidx.annotation.RequiresApi
+import androidx.benchmark.DeviceInfo
import androidx.benchmark.Outputs
import androidx.benchmark.macro.perfetto.PerfettoTraceProcessor
import androidx.benchmark.perfetto.PerfettoCaptureWrapper
@@ -37,6 +38,7 @@
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
+import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
import org.junit.Test
import org.junit.runner.RunWith
@@ -182,12 +184,14 @@
@LargeTest
@Test
fun startupInAppNav_immediate() {
+ assumeFalse(DeviceInfo.isEmulator) // TODO(b/255754739): address failures on Cuttlefish
validateStartup_fullyDrawn(delayMs = 0, useInAppNav = true)
}
@LargeTest
@Test
fun startupInAppNav_fullyDrawn() {
+ assumeFalse(DeviceInfo.isEmulator) // TODO(b/255754739): address failures on Cuttlefish
validateStartup_fullyDrawn(delayMs = 100, useInAppNav = true)
}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt
index db37987..296d682 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt
@@ -36,7 +36,7 @@
*/
public class MacrobenchmarkScope(
/**
- * Package name of the app being tested.
+ * ApplicationId / Package name of the app being tested.
*/
val packageName: String,
/**
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
index cc8012b..449e495 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
@@ -17,11 +17,10 @@
package androidx.benchmark.benchmark
import androidx.benchmark.junit4.BenchmarkRule
-import androidx.benchmark.junit4.PerfettoTraceRule
import androidx.benchmark.junit4.measureRepeated
-import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.tracing.Trace
import androidx.tracing.trace
import org.junit.Rule
@@ -31,12 +30,11 @@
@LargeTest
@RunWith(AndroidJUnit4::class)
class PerfettoOverheadBenchmark {
- @get:Rule
- val benchmarkRule = BenchmarkRule()
+ private val targetPackage =
+ InstrumentationRegistry.getInstrumentation().targetContext.packageName
- @OptIn(ExperimentalPerfettoCaptureApi::class)
@get:Rule
- val mPerfettoTraceRule = PerfettoTraceRule()
+ val benchmarkRule = BenchmarkRule(packages = listOf(targetPackage))
/**
* Empty baseline, no tracing. Expect similar results to [TrivialJavaBenchmark.nothing].
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt
index 18de45a..0da127f 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt
@@ -18,9 +18,7 @@
import android.os.Build
import androidx.benchmark.junit4.BenchmarkRule
-import androidx.benchmark.junit4.PerfettoTraceRule
import androidx.benchmark.junit4.measureRepeated
-import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
import androidx.benchmark.perfetto.PerfettoCapture
import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -50,10 +48,6 @@
@get:Rule
val benchmarkRule = BenchmarkRule(packages = listOf(targetPackage))
- @OptIn(ExperimentalPerfettoCaptureApi::class)
- @get:Rule
- val mPerfettoTraceRule = PerfettoTraceRule()
-
private val testData = Array(50_000) { UUID.randomUUID().toString() }
@Before
diff --git a/biometric/biometric/src/main/res/values-da/strings.xml b/biometric/biometric/src/main/res/values-da/strings.xml
index 41fe471..b08401b 100644
--- a/biometric/biometric/src/main/res/values-da/strings.xml
+++ b/biometric/biometric/src/main/res/values-da/strings.xml
@@ -17,11 +17,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="fingerprint_dialog_touch_sensor" msgid="1072308044213194243">"Sæt finger på fingeraftrykslæser"</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="1072308044213194243">"Sæt finger på fingeraftrykssensor"</string>
<string name="fingerprint_not_recognized" msgid="3873359464293253009">"Ikke genkendt"</string>
<string name="fingerprint_error_hw_not_available" msgid="8216738333501875566">"Hardwaren til fingeraftryk er ikke tilgængelig."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7520712796891883488">"Der er ikke registreret nogen fingeraftryk."</string>
- <string name="fingerprint_error_hw_not_present" msgid="6306988885793029438">"Denne enhed har ingen fingeraftrykslæser"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="6306988885793029438">"Denne enhed har ingen fingeraftrykssensor"</string>
<string name="fingerprint_error_user_canceled" msgid="7627716295344353987">"Fingeraftrykshandlingen blev annulleret af brugeren."</string>
<string name="fingerprint_error_lockout" msgid="7291787166416782245">"Der var for mange forsøg Prøv igen senere."</string>
<string name="default_error_msg" msgid="4776854077120974966">"Ukendt fejl"</string>
diff --git a/browser/browser/api/current.txt b/browser/browser/api/current.txt
index f084924..0598270 100644
--- a/browser/browser/api/current.txt
+++ b/browser/browser/api/current.txt
@@ -73,7 +73,7 @@
ctor public CustomTabsCallback();
method public void extraCallback(String, android.os.Bundle?);
method public android.os.Bundle? extraCallbackWithResult(String, android.os.Bundle?);
- method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
+ method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, @Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
method public void onMessageChannelReady(android.os.Bundle?);
method public void onNavigationEvent(int, android.os.Bundle?);
method public void onPostMessage(String, android.os.Bundle?);
@@ -118,7 +118,7 @@
field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
- field public static final String EXTRA_ACTIVITY_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_RESIZE_BEHAVIOR";
+ field public static final String EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_HEIGHT_RESIZE_BEHAVIOR";
field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
field public static final String EXTRA_CLOSE_BUTTON_POSITION = "androidx.browser.customtabs.extra.CLOSE_BUTTON_POSITION";
field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
diff --git a/browser/browser/api/public_plus_experimental_current.txt b/browser/browser/api/public_plus_experimental_current.txt
index f084924..0598270 100644
--- a/browser/browser/api/public_plus_experimental_current.txt
+++ b/browser/browser/api/public_plus_experimental_current.txt
@@ -73,7 +73,7 @@
ctor public CustomTabsCallback();
method public void extraCallback(String, android.os.Bundle?);
method public android.os.Bundle? extraCallbackWithResult(String, android.os.Bundle?);
- method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
+ method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, @Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
method public void onMessageChannelReady(android.os.Bundle?);
method public void onNavigationEvent(int, android.os.Bundle?);
method public void onPostMessage(String, android.os.Bundle?);
@@ -118,7 +118,7 @@
field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
- field public static final String EXTRA_ACTIVITY_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_RESIZE_BEHAVIOR";
+ field public static final String EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_HEIGHT_RESIZE_BEHAVIOR";
field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
field public static final String EXTRA_CLOSE_BUTTON_POSITION = "androidx.browser.customtabs.extra.CLOSE_BUTTON_POSITION";
field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
diff --git a/browser/browser/api/restricted_current.txt b/browser/browser/api/restricted_current.txt
index 09759da..de4bb0c 100644
--- a/browser/browser/api/restricted_current.txt
+++ b/browser/browser/api/restricted_current.txt
@@ -84,7 +84,7 @@
ctor public CustomTabsCallback();
method public void extraCallback(String, android.os.Bundle?);
method public android.os.Bundle? extraCallbackWithResult(String, android.os.Bundle?);
- method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
+ method public void onActivityResized(@Dimension(unit=androidx.annotation.Dimension.PX) int, @Dimension(unit=androidx.annotation.Dimension.PX) int, android.os.Bundle);
method public void onMessageChannelReady(android.os.Bundle?);
method public void onNavigationEvent(int, android.os.Bundle?);
method public void onPostMessage(String, android.os.Bundle?);
@@ -129,7 +129,7 @@
field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
- field public static final String EXTRA_ACTIVITY_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_RESIZE_BEHAVIOR";
+ field public static final String EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR = "androidx.browser.customtabs.extra.ACTIVITY_HEIGHT_RESIZE_BEHAVIOR";
field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
field public static final String EXTRA_CLOSE_BUTTON_POSITION = "androidx.browser.customtabs.extra.CLOSE_BUTTON_POSITION";
field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
diff --git a/browser/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsCallbackTest.java b/browser/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsCallbackTest.java
index 2caee24..6df7e8b 100644
--- a/browser/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsCallbackTest.java
+++ b/browser/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsCallbackTest.java
@@ -45,7 +45,7 @@
@Test
public void testOnActivityResized() throws Throwable {
- mToken.getCallback().onActivityResized(75239, new Bundle());
+ mToken.getCallback().onActivityResized(75239, 1200, new Bundle());
assertTrue(mCallback.hasActivityBeenResized());
}
}
diff --git a/browser/browser/src/androidTest/java/androidx/browser/customtabs/TestCustomTabsCallback.java b/browser/browser/src/androidTest/java/androidx/browser/customtabs/TestCustomTabsCallback.java
index 3363e0f..9293212 100644
--- a/browser/browser/src/androidTest/java/androidx/browser/customtabs/TestCustomTabsCallback.java
+++ b/browser/browser/src/androidTest/java/androidx/browser/customtabs/TestCustomTabsCallback.java
@@ -70,8 +70,8 @@
}
@Override
- public void onActivityResized(int size, Bundle extras) throws RemoteException {
- TestCustomTabsCallback.this.onActivityResized(size, extras);
+ public void onActivityResized(int height, int width, Bundle extras) throws RemoteException {
+ TestCustomTabsCallback.this.onActivityResized(height, width, extras);
}
};
@@ -104,7 +104,7 @@
}
@Override
- public void onActivityResized(int size, Bundle extras) {
+ public void onActivityResized(int height, int width, @NonNull Bundle extras) {
mOnResizedReceived = true;
}
diff --git a/browser/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl b/browser/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl
index 38bb3e1..951e8ee 100644
--- a/browser/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl
+++ b/browser/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl
@@ -29,5 +29,5 @@
void onPostMessage(String message, in Bundle extras) = 4;
void onRelationshipValidationResult(int relation, in Uri origin, boolean result, in Bundle extras) = 5;
Bundle extraCallbackWithResult(String callbackName, in Bundle args) = 6;
- void onActivityResized(int size, in Bundle extras) = 7;
+ void onActivityResized(int height, int width, in Bundle extras) = 7;
}
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java
index 3ac0a3c..a111f89 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java
@@ -161,12 +161,12 @@
boolean result, @Nullable Bundle extras) {}
/**
- * Called when the tab is resized in its height. This is applicable when users resize a tab
- * launched with {@link CustomTabsIntent#ACTIVITY_HEIGHT_ADJUSTABLE} for the {@link
- * CustomTabsIntent#ActivityResizeBehavior}.
+ * Called when the tab is resized.
*
- * @param size The updated height in px.
+ * @param height The updated height in px.
+ * @param width The updated width in px.
* @param extras Reserved for future use.
*/
- public void onActivityResized(@Dimension(unit = PX) int size, @NonNull Bundle extras) {}
+ public void onActivityResized(@Dimension(unit = PX) int height,
+ @Dimension(unit = PX) int width, @NonNull Bundle extras) {}
}
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java
index e45d218..36d1393 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java
@@ -398,13 +398,14 @@
}
@Override
- public void onActivityResized(final int size, final @Nullable Bundle extras)
+ public void onActivityResized(final int height, final int width,
+ final @Nullable Bundle extras)
throws RemoteException {
if (callback == null) return;
mHandler.post(new Runnable() {
@Override
public void run() {
- callback.onActivityResized(size, extras);
+ callback.onActivityResized(height, width, extras);
}
});
}
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
index 9792aff..53c77a0 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
@@ -357,24 +357,24 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
@IntDef({ACTIVITY_HEIGHT_DEFAULT, ACTIVITY_HEIGHT_ADJUSTABLE, ACTIVITY_HEIGHT_FIXED})
@Retention(RetentionPolicy.SOURCE)
- public @interface ActivityResizeBehavior {
+ public @interface ActivityHeightResizeBehavior {
}
/**
- * Applies the default resize behavior for the Custom Tab Activity when it behaves as a
+ * Applies the default height resize behavior for the Custom Tab Activity when it behaves as a
* bottom sheet.
*/
public static final int ACTIVITY_HEIGHT_DEFAULT = 0;
/**
- * The Custom Tab Activity, when it behaves as a bottom sheet, can be manually resized by the
- * user.
+ * The Custom Tab Activity, when it behaves as a bottom sheet, can have its height manually
+ * resized by the user.
*/
public static final int ACTIVITY_HEIGHT_ADJUSTABLE = 1;
/**
- * The Custom Tab Activity, when it behaves as a bottom sheet, cannot be manually resized by
- * the user.
+ * The Custom Tab Activity, when it behaves as a bottom sheet, cannot have its height manually
+ * resized by the user.
*/
public static final int ACTIVITY_HEIGHT_FIXED = 2;
@@ -385,12 +385,12 @@
/**
* Extra that, if set in combination with
- * {@link CustomTabsIntent#EXTRA_INITIAL_ACTIVITY_HEIGHT_PX}, defines the resize behavior of
- * the Custom Tab Activity when it behaves as a bottom sheet.
+ * {@link CustomTabsIntent#EXTRA_INITIAL_ACTIVITY_HEIGHT_PX}, defines the height resize
+ * behavior of the Custom Tab Activity when it behaves as a bottom sheet.
* Default is {@link CustomTabsIntent#ACTIVITY_HEIGHT_DEFAULT}.
*/
- public static final String EXTRA_ACTIVITY_RESIZE_BEHAVIOR =
- "androidx.browser.customtabs.extra.ACTIVITY_RESIZE_BEHAVIOR";
+ public static final String EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR =
+ "androidx.browser.customtabs.extra.ACTIVITY_HEIGHT_RESIZE_BEHAVIOR";
/**
* Extra that sets the toolbar's top corner radii in dp. This will only have
@@ -980,27 +980,28 @@
* The Custom Tab will behave as a bottom sheet.
*
* @param initialHeightPx The Custom Tab Activity's initial height in pixels.
- * @param activityResizeBehavior Desired height behavior.
+ * @param activityHeightResizeBehavior Desired height behavior.
* @see CustomTabsIntent#EXTRA_INITIAL_ACTIVITY_HEIGHT_PX
- * @see CustomTabsIntent#EXTRA_ACTIVITY_RESIZE_BEHAVIOR
+ * @see CustomTabsIntent#EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR
* @see CustomTabsIntent#ACTIVITY_HEIGHT_DEFAULT
* @see CustomTabsIntent#ACTIVITY_HEIGHT_ADJUSTABLE
* @see CustomTabsIntent#ACTIVITY_HEIGHT_FIXED
*/
@NonNull
public Builder setInitialActivityHeightPx(@Dimension(unit = PX) int initialHeightPx,
- @ActivityResizeBehavior int activityResizeBehavior) {
+ @ActivityHeightResizeBehavior int activityHeightResizeBehavior) {
if (initialHeightPx <= 0) {
throw new IllegalArgumentException("Invalid value for the initialHeightPx "
+ "argument");
}
- if (activityResizeBehavior < 0 || activityResizeBehavior > ACTIVITY_HEIGHT_MAX) {
- throw new IllegalArgumentException("Invalid value for the activityResizeBehavior "
- + "argument");
+ if (activityHeightResizeBehavior < 0
+ || activityHeightResizeBehavior > ACTIVITY_HEIGHT_MAX) {
+ throw new IllegalArgumentException(
+ "Invalid value for the activityHeightResizeBehavior argument");
}
mIntent.putExtra(EXTRA_INITIAL_ACTIVITY_HEIGHT_PX, initialHeightPx);
- mIntent.putExtra(EXTRA_ACTIVITY_RESIZE_BEHAVIOR, activityResizeBehavior);
+ mIntent.putExtra(EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR, activityHeightResizeBehavior);
return this;
}
@@ -1184,14 +1185,14 @@
* @param intent Intent to retrieve the resize behavior from.
* @return The resize behavior. If {@link CustomTabsIntent#EXTRA_INITIAL_ACTIVITY_HEIGHT_PX}
* is not set as part of the same intent, the value has no effect.
- * @see CustomTabsIntent#EXTRA_ACTIVITY_RESIZE_BEHAVIOR
+ * @see CustomTabsIntent#EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR
* @see CustomTabsIntent#ACTIVITY_HEIGHT_DEFAULT
* @see CustomTabsIntent#ACTIVITY_HEIGHT_ADJUSTABLE
* @see CustomTabsIntent#ACTIVITY_HEIGHT_FIXED
*/
- @ActivityResizeBehavior
+ @ActivityHeightResizeBehavior
public static int getActivityResizeBehavior(@NonNull Intent intent) {
- return intent.getIntExtra(EXTRA_ACTIVITY_RESIZE_BEHAVIOR,
+ return intent.getIntExtra(EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR,
CustomTabsIntent.ACTIVITY_HEIGHT_DEFAULT);
}
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java
index e495fc9..435e242 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java
@@ -74,7 +74,7 @@
Uri requestedOrigin, boolean result, Bundle extras) {}
@Override
- public void onActivityResized(int size, Bundle extras) {}
+ public void onActivityResized(int height, int width, Bundle extras) {}
@Override
public IBinder asBinder() {
@@ -191,9 +191,9 @@
@SuppressWarnings("NullAway") // TODO: b/142938599
@Override
- public void onActivityResized(int size, @NonNull Bundle extras) {
+ public void onActivityResized(int height, int width, @NonNull Bundle extras) {
try {
- mCallbackBinder.onActivityResized(size, extras);
+ mCallbackBinder.onActivityResized(height, width, extras);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException during ICustomTabsCallback transaction");
}
diff --git a/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java b/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
index 18e3aa6..1bba7b6 100644
--- a/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
+++ b/browser/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
@@ -157,7 +157,7 @@
}
@Test
- public void testActivityInitialFixedResizeBehavior() {
+ public void testActivityInitialFixedHeightResizeBehavior() {
int heightFixedResizeBehavior = CustomTabsIntent.ACTIVITY_HEIGHT_FIXED;
int initialActivityHeight = 200;
@@ -166,9 +166,10 @@
.build()
.intent;
- assertEquals("The value of EXTRA_ACTIVITY_FIXED_HEIGHT should be ACTIVITY_HEIGHT_FIXED.",
+ assertEquals("The value of EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR should be "
+ + "ACTIVITY_HEIGHT_FIXED.",
heightFixedResizeBehavior,
- intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_RESIZE_BEHAVIOR,
+ intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR,
CustomTabsIntent.ACTIVITY_HEIGHT_DEFAULT));
assertEquals("The height should be the same as the one that was set.",
initialActivityHeight,
@@ -182,7 +183,7 @@
}
@Test
- public void testActivityInitialAdjustableResizeBehavior() {
+ public void testActivityInitialAdjustableHeightResizeBehavior() {
int heightAdjustableResizeBehavior = CustomTabsIntent.ACTIVITY_HEIGHT_ADJUSTABLE;
int initialActivityHeight = 200;
@@ -191,10 +192,10 @@
.build()
.intent;
- assertEquals("The value of EXTRA_ACTIVITY_FIXED_HEIGHT should be "
+ assertEquals("The value of EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR should be "
+ "ACTIVITY_HEIGHT_ADJUSTABLE.",
heightAdjustableResizeBehavior,
- intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_RESIZE_BEHAVIOR,
+ intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR,
CustomTabsIntent.ACTIVITY_HEIGHT_DEFAULT));
assertEquals("The height should be the same as the one that was set.",
initialActivityHeight,
@@ -220,10 +221,10 @@
assertEquals("The height should be the same as the one that was set.",
initialActivityHeight,
intent.getIntExtra(CustomTabsIntent.EXTRA_INITIAL_ACTIVITY_HEIGHT_PX, 0));
- assertEquals("The value of EXTRA_ACTIVITY_RESIZE_BEHAVIOR should be "
+ assertEquals("The value of EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR should be "
+ "ACTIVITY_HEIGHT_DEFAULT.",
defaultResizeBehavior,
- intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_RESIZE_BEHAVIOR,
+ intent.getIntExtra(CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR,
CustomTabsIntent.ACTIVITY_HEIGHT_FIXED));
assertEquals("The height returned by the getter should be the same.",
initialActivityHeight,
@@ -242,8 +243,8 @@
assertFalse("The EXTRA_INITIAL_ACTIVITY_HEIGHT_PX should not be set.",
intent.hasExtra(CustomTabsIntent.EXTRA_INITIAL_ACTIVITY_HEIGHT_PX));
- assertFalse("The EXTRA_ACTIVITY_RESIZE_BEHAVIOR should not be set.",
- intent.hasExtra(CustomTabsIntent.EXTRA_ACTIVITY_RESIZE_BEHAVIOR));
+ assertFalse("The EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR should not be set.",
+ intent.hasExtra(CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR));
assertEquals("The getter should return the default value.",
defaultInitialActivityHeight,
CustomTabsIntent.getInitialActivityHeightPx(intent));
diff --git a/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt b/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt
index 5ad9a67..653ae96 100644
--- a/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt
+++ b/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt
@@ -88,9 +88,13 @@
if (outDir == null || outDir == "") {
outDir = File(projectDir, "../../out").normalize().toString()
}
+ // --dependency-verification=off is set because we don't have to do validation of
+ // dependencies during these tests, it is already handled by the main build.
+ // Having it validate here breaks in androidx-studio-integration case where we
+ // might get new dependencies from AGP that are missinng signatures.
GradleRunner.create()
.withProjectDir(projectDir)
- .withArguments("-Pandroidx.projects=$name", "tasks")
+ .withArguments("-Pandroidx.projects=$name", "tasks", "--dependency-verification=off")
.withTestKitDir(File(outDir, ".gradle-testkit"))
.build(); // fails the test if the build fails
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt b/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
index bc95bb6e..9a796c7e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/MavenUploadHelper.kt
@@ -36,7 +36,6 @@
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.XmlProvider
-import org.gradle.api.attributes.DocsType
import org.gradle.api.component.ComponentWithVariants
import org.gradle.api.component.SoftwareComponent
import org.gradle.api.component.SoftwareComponentFactory
@@ -383,10 +382,7 @@
) {
val targetConfigurations = mutableSetOf<Configuration>()
configurations.configureEach {
- if (
- it.attributes.getAttribute(DocsType.DOCS_TYPE_ATTRIBUTE)?.name == DocsType.SOURCES &&
- it.name in names
- ) {
+ if (it.name in names) {
targetConfigurations.add(it)
if (targetConfigurations.size == names.size) {
action(
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index a4a255a..597648d 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -623,6 +623,15 @@
":compose:material:material:icons:generator",
":compose:material:material-icons-extended"
),
+ // Link glance-appwidget macrobenchmark and its target.
+ setOf(
+ ":glance:glance-appwidget:integration-tests:macrobenchmark",
+ ":glance:glance-appwidget:integration-tests:macrobenchmark-target"
+ ),
+ setOf(
+ ":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark",
+ ":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark-target"
+ )
)
val IGNORED_PATHS = setOf(
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
index 8aec946..db29d61 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
@@ -17,16 +17,13 @@
package androidx.camera.camera2.pipe.integration.adapter
import android.content.Context
-import android.graphics.Point
import android.hardware.camera2.CameraCaptureSession.CaptureCallback
import android.hardware.camera2.CameraDevice
-import android.hardware.display.DisplayManager
-import android.util.Size
-import android.view.Display
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.core.Log.debug
import androidx.camera.camera2.pipe.core.Log.info
import androidx.camera.camera2.pipe.integration.impl.Camera2ImplConfig
+import androidx.camera.camera2.pipe.integration.impl.DisplayInfoManager
import androidx.camera.camera2.pipe.integration.impl.SESSION_PHYSICAL_CAMERA_ID_OPTION
import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
import androidx.camera.core.impl.CameraCaptureCallback
@@ -48,17 +45,7 @@
@Suppress("DEPRECATION")
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
class CameraUseCaseAdapter(context: Context) : UseCaseConfigFactory {
- private val MAX_PREVIEW_SIZE = Size(1920, 1080)
-
- private val displayManager: DisplayManager by lazy {
- context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
- }
- private val defaultDisplay: Display by lazy {
- getMaxSizeDisplay()
- }
- private val previewSize: Size by lazy {
- calculatePreviewSize()
- }
+ private val displayInfoManager by lazy { DisplayInfoManager(context) }
init {
if (context === context.applicationContext) {
@@ -132,56 +119,17 @@
if (captureType == UseCaseConfigFactory.CaptureType.PREVIEW) {
mutableConfig.insertOption(
ImageOutputConfig.OPTION_MAX_RESOLUTION,
- previewSize
+ displayInfoManager.previewSize
)
}
mutableConfig.insertOption(
ImageOutputConfig.OPTION_TARGET_ROTATION,
- defaultDisplay.rotation
+ displayInfoManager.defaultDisplay.rotation
)
return OptionsBundle.from(mutableConfig)
}
- private fun getMaxSizeDisplay(): Display {
- val displays = displayManager.displays
- var maxDisplay: Display? = null
- var maxDisplaySize = -1
- for (display: Display in displays) {
- val displaySize = Point()
- // TODO(b/230400472): Use WindowManager#getCurrentWindowMetrics(). Display#getRealSize()
- // is deprecated since API level 31.
- display.getRealSize(displaySize)
- if (displaySize.x * displaySize.y > maxDisplaySize) {
- maxDisplaySize = displaySize.x * displaySize.y
- maxDisplay = display
- }
- }
- return checkNotNull(maxDisplay) { "No displays found from ${displayManager.displays}!" }
- }
-
- /**
- * Calculates the device's screen resolution, or MAX_PREVIEW_SIZE, whichever is smaller.
- */
- private fun calculatePreviewSize(): Size {
- val displaySize = Point()
- val display: Display = defaultDisplay
- display.getRealSize(displaySize)
- var displayViewSize: Size
- displayViewSize = if (displaySize.x > displaySize.y) {
- Size(displaySize.x, displaySize.y)
- } else {
- Size(displaySize.y, displaySize.x)
- }
- if (displayViewSize.width * displayViewSize.height
- > MAX_PREVIEW_SIZE.width * MAX_PREVIEW_SIZE.height
- ) {
- displayViewSize = MAX_PREVIEW_SIZE
- }
- // TODO(b/230402463): Migrate extra cropping quirk from CameraX.
- return displayViewSize
- }
-
object DefaultCaptureOptionsUnpacker : CaptureConfig.OptionUnpacker {
@OptIn(ExperimentalCamera2Interop::class)
override fun unpack(config: UseCaseConfig<*>, builder: CaptureConfig.Builder) {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt
new file mode 100644
index 0000000..1babffd
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.impl
+
+import android.content.Context
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.util.Size
+import android.view.Display
+import androidx.annotation.RequiresApi
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Suppress("DEPRECATION") // getRealSize
+@Singleton
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class DisplayInfoManager @Inject constructor(context: Context) {
+ private val MAX_PREVIEW_SIZE = Size(1920, 1080)
+
+ private val displayManager: DisplayManager by lazy {
+ context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ }
+
+ // TODO(b/198257203): Fetch latest display information for devices where display size is not
+ // guaranteed to be fixed. (e.g. foldable devices or devices with multiple displays)
+
+ val defaultDisplay: Display by lazy {
+ getMaxSizeDisplay()
+ }
+
+ val previewSize: Size by lazy {
+ calculatePreviewSize()
+ }
+
+ private fun getMaxSizeDisplay(): Display {
+ val displays = displayManager.displays
+ var maxDisplay: Display? = null
+ var maxDisplaySize = -1
+
+ // TODO(b/211945950, b/255170076): Handle STATE_OFF displays.
+
+ for (display: Display in displays) {
+ val displaySize = Point()
+ // TODO(b/230400472): Use WindowManager#getCurrentWindowMetrics(). Display#getRealSize()
+ // is deprecated since API level 31.
+ display.getRealSize(displaySize)
+ if (displaySize.x * displaySize.y > maxDisplaySize) {
+ maxDisplaySize = displaySize.x * displaySize.y
+ maxDisplay = display
+ }
+ }
+ return checkNotNull(maxDisplay) { "No displays found from ${displayManager.displays}!" }
+ }
+
+ /**
+ * Calculates the device's screen resolution, or MAX_PREVIEW_SIZE, whichever is smaller.
+ */
+ private fun calculatePreviewSize(): Size {
+ val displaySize = Point()
+ val display: Display = defaultDisplay
+ // TODO(b/230400472): Use WindowManager#getCurrentWindowMetrics(). Display#getRealSize()
+ // is deprecated since API level 31.
+ display.getRealSize(displaySize)
+ var displayViewSize: Size
+ displayViewSize = if (displaySize.x > displaySize.y) {
+ Size(displaySize.x, displaySize.y)
+ } else {
+ Size(displaySize.y, displaySize.x)
+ }
+ if (displayViewSize.width * displayViewSize.height
+ > MAX_PREVIEW_SIZE.width * MAX_PREVIEW_SIZE.height
+ ) {
+ displayViewSize = MAX_PREVIEW_SIZE
+ }
+ // TODO(b/230402463): Migrate extra cropping quirk from CameraX.
+ return displayViewSize
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
index 0f8323fa..684eb88 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
@@ -43,6 +43,7 @@
import androidx.camera.core.impl.UseCaseConfig.OPTION_SESSION_CONFIG_UNPACKER
import androidx.camera.core.impl.UseCaseConfigFactory
import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import kotlin.math.min
private val DEFAULT_PREVIEW_SIZE = Size(0, 0)
@@ -55,9 +56,10 @@
class MeteringRepeating(
private val cameraProperties: CameraProperties,
config: MeteringRepeatingConfig,
+ private val displayInfoManager: DisplayInfoManager
) : UseCase(config) {
- private val meteringSurfaceSize = cameraProperties.getMinimumPreviewSize()
+ private val meteringSurfaceSize = getProperPreviewSize()
private val deferrableSurfaceLock = Any()
@@ -65,9 +67,10 @@
private var deferrableSurface: DeferrableSurface? = null
override fun getDefaultConfig(applyDefaultConfig: Boolean, factory: UseCaseConfigFactory) =
- Builder(cameraProperties).useCaseConfig
+ Builder(cameraProperties, displayInfoManager).useCaseConfig
- override fun getUseCaseConfigBuilder(config: Config) = Builder(cameraProperties)
+ override fun getUseCaseConfigBuilder(config: Config) =
+ Builder(cameraProperties, displayInfoManager)
override fun onSuggestedResolutionUpdated(suggestedResolution: Size): Size {
updateSessionConfig(createPipeline().build())
@@ -120,28 +123,57 @@
}
}
- private fun CameraProperties.getMinimumPreviewSize(): Size {
- val map = metadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]
- if (map == null) {
+ private fun CameraProperties.getOutputSizes(): Array<Size>? {
+ val map = metadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP] ?: run {
error { "Can not retrieve SCALER_STREAM_CONFIGURATION_MAP." }
- return DEFAULT_PREVIEW_SIZE
+ return null
}
- val outputSizes = if (Build.VERSION.SDK_INT < 23) {
+ return if (Build.VERSION.SDK_INT < 23) {
// ImageFormat.PRIVATE is only public after Android level 23. Therefore, use
// SurfaceTexture.class to get the supported output sizes before Android level 23.
map.getOutputSizes(SurfaceTexture::class.java)
} else {
map.getOutputSizes(ImageFormat.PRIVATE)
}
+ }
+
+ private fun getProperPreviewSize(): Size {
+ val outputSizes = cameraProperties.getOutputSizes()
if (outputSizes == null) {
error { "Can not get output size list." }
return DEFAULT_PREVIEW_SIZE
}
- check(outputSizes.isNotEmpty()) { "Output sizes empty" }
- return outputSizes.minWithOrNull { size1, size2 -> size1.area().compareTo(size2.area()) }!!
+ if (outputSizes.isEmpty()) {
+ error { "Output sizes empty" }
+ return DEFAULT_PREVIEW_SIZE
+ }
+
+ // TODO(b/256805716): get supported output sizes handling quirks.
+
+ outputSizes.sortBy { size -> size.width.toLong() * size.height.toLong() }
+
+ // Find maximum supported resolution that is <= min(VGA, display resolution)
+ // Using minimum supported size could cause some issue on certain devices.
+ val previewSize = displayInfoManager.previewSize
+ val maxSizeProduct =
+ min(640L * 480L, previewSize.width.toLong() * previewSize.height.toLong())
+
+ var previousSize: Size? = null
+ for (outputSize in outputSizes) {
+ val product = outputSize.width.toLong() * outputSize.height.toLong()
+ if (product == maxSizeProduct) {
+ return outputSize
+ } else if (product > maxSizeProduct) {
+ return previousSize ?: break // fallback to minimum size.
+ }
+ previousSize = outputSize
+ }
+
+ // If not found, return the minimum size.
+ return outputSizes[0]
}
class MeteringRepeatingConfig : UseCaseConfig<MeteringRepeating>, ImageInputConfig {
@@ -157,7 +189,10 @@
override fun getInputFormat() = ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
}
- class Builder(private val cameraProperties: CameraProperties) :
+ class Builder(
+ private val cameraProperties: CameraProperties,
+ private val displayInfoManager: DisplayInfoManager
+ ) :
UseCaseConfig.Builder<MeteringRepeating, MeteringRepeatingConfig, Builder> {
override fun getMutableConfig() = MutableOptionsBundle.create()
@@ -185,7 +220,7 @@
override fun setZslDisabled(disabled: Boolean) = this
override fun build(): MeteringRepeating {
- return MeteringRepeating(cameraProperties, useCaseConfig)
+ return MeteringRepeating(cameraProperties, useCaseConfig, displayInfoManager)
}
}
}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index cdef936..537adf9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -66,7 +66,8 @@
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") // Java version required for Dagger
private val controls: java.util.Set<UseCaseCameraControl>,
private val camera2CameraControl: Camera2CameraControl,
- cameraProperties: CameraProperties
+ cameraProperties: CameraProperties,
+ displayInfoManager: DisplayInfoManager
) {
private val lock = Any()
@@ -76,7 +77,12 @@
@GuardedBy("lock")
private val activeUseCases = mutableSetOf<UseCase>()
- private val meteringRepeating by lazy { MeteringRepeating.Builder(cameraProperties).build() }
+ private val meteringRepeating by lazy {
+ MeteringRepeating.Builder(
+ cameraProperties,
+ displayInfoManager
+ ).build()
+ }
@Volatile
private var _activeComponent: UseCaseCameraComponent? = null
@@ -285,4 +291,4 @@
private fun <T> Collection<T>.only(predicate: (T) -> Boolean): Boolean {
return isNotEmpty() && all(predicate)
}
-}
\ No newline at end of file
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt
new file mode 100644
index 0000000..f550d39
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.impl
+
+import android.content.Context
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.util.Size
+import androidx.test.core.app.ApplicationProvider
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadows.ShadowDisplayManager
+
+@Suppress("DEPRECATION") // getRealSize
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+class DisplayInfoManagerTest {
+ private val displayInfoManager = DisplayInfoManager(ApplicationProvider.getApplicationContext())
+
+ private fun addDisplay(width: Int, height: Int) {
+ ShadowDisplayManager.addDisplay(String.format("w%ddp-h%ddp", width, height))
+ }
+
+ @Test
+ fun defaultDisplayIsDeviceDisplay_whenOneDisplay() {
+ // Arrange
+ val displayManager = (ApplicationProvider.getApplicationContext() as Context)
+ .getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ val currentDisplaySize = Point()
+ displayManager.displays[0].getRealSize(currentDisplaySize)
+
+ // Act
+ val size = Point()
+ displayInfoManager.defaultDisplay.getRealSize(size)
+
+ // Assert
+ assertEquals(currentDisplaySize, size)
+ }
+
+ @Test
+ fun defaultDisplayIsMaxSizeDisplay_whenMultipleDisplay() {
+ // Arrange
+ addDisplay(2000, 3000)
+ addDisplay(480, 640)
+
+ // Act
+ val size = Point()
+ displayInfoManager.defaultDisplay.getRealSize(size)
+
+ // Assert
+ assertEquals(Point(2000, 3000), size)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun throwsCorrectExceptionForDefaultDisplay_whenNoDisplay() {
+ // Arrange
+ ShadowDisplayManager.removeDisplay(0)
+
+ // Act
+ val size = Point()
+ displayInfoManager.defaultDisplay.getRealSize(size)
+ }
+
+ @Test
+ fun previewSizeIsProperSize_whenDisplaySmallerThan1080P() {
+ // Arrange
+ addDisplay(480, 640)
+
+ // Act & Assert
+ assertEquals(Size(640, 480), displayInfoManager.previewSize)
+ }
+
+ @Test
+ fun previewSizeIsMaxPreviewSize_whenDisplayLargerThan1080P() {
+ // Arrange
+ addDisplay(2000, 3000)
+
+ // Act & Assert
+ assertEquals(Size(1920, 1080), displayInfoManager.previewSize)
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeatingTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeatingTest.kt
new file mode 100644
index 0000000..f40a4cf
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeatingTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.impl
+
+import android.hardware.camera2.CameraCharacteristics
+import android.os.Build
+import android.util.Size
+import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
+import androidx.camera.camera2.pipe.integration.testing.FakeCameraProperties
+import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
+import androidx.test.core.app.ApplicationProvider
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadows.ShadowDisplayManager
+import org.robolectric.shadows.StreamConfigurationMapBuilder
+
+@RunWith(RobolectricCameraPipeTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class MeteringRepeatingTest {
+ companion object {
+ val dummyZeroSize = Size(0, 0)
+
+ val dummySizeListWithout640x480 = listOf(
+ Size(4160, 3120),
+ Size(1920, 1080),
+ Size(1280, 720),
+ Size(320, 240),
+ Size(240, 144),
+ )
+
+ val dummySizeListWith640x480 = listOf(
+ Size(4160, 3120),
+ Size(1920, 1080),
+ Size(1280, 720),
+ Size(640, 480),
+ Size(320, 240),
+ )
+
+ val dummySizeListWithoutSmaller = listOf(
+ Size(4160, 3120),
+ Size(1920, 1080),
+ Size(1280, 720)
+ )
+
+ val dummySizeListSmallerThan640x480 = listOf(
+ Size(320, 480),
+ Size(320, 240),
+ Size(240, 144),
+ )
+
+ fun getFakeMetadata(sizeList: List<Size>): FakeCameraMetadata {
+ val shuffledList = sizeList.shuffled()
+
+ val builder = StreamConfigurationMapBuilder.newBuilder()
+ for (size in shuffledList) {
+ builder.addOutputSize(size)
+ }
+
+ return FakeCameraMetadata(
+ mapOf(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP to builder.build(),
+ )
+ )
+ }
+ }
+
+ private lateinit var meteringRepeating: MeteringRepeating
+
+ private fun addDisplay(width: Int, height: Int) {
+ ShadowDisplayManager.addDisplay(String.format("w%ddp-h%ddp", width, height))
+ }
+
+ private fun getMeteringRepeatingAndInitDisplay(outputSizeList: List<Size>): MeteringRepeating {
+ for (size in outputSizeList) {
+ addDisplay(size.width, size.height)
+ }
+
+ return MeteringRepeating.Builder(
+ FakeCameraProperties(
+ getFakeMetadata(
+ outputSizeList
+ )
+ ),
+ DisplayInfoManager(ApplicationProvider.getApplicationContext())
+ ).build()
+ }
+
+ @Test
+ fun attachedSurfaceResolutionIsLargestLessThan640x480_when640x480NotPresentInOutputSizes() {
+ meteringRepeating = getMeteringRepeatingAndInitDisplay(dummySizeListWithout640x480)
+
+ meteringRepeating.updateSuggestedResolution(dummyZeroSize)
+
+ assertEquals(Size(320, 240), meteringRepeating.attachedSurfaceResolution)
+ }
+
+ @Test
+ fun attachedSurfaceResolutionIs640x480_when640x480PresentInOutputSizes() {
+ meteringRepeating = getMeteringRepeatingAndInitDisplay(dummySizeListWith640x480)
+
+ meteringRepeating.updateSuggestedResolution(dummyZeroSize)
+
+ assertEquals(Size(640, 480), meteringRepeating.attachedSurfaceResolution)
+ }
+
+ @Test
+ fun attachedSurfaceResolutionFallsBackToMinimum_whenAllOutputSizesLargerThan640x480() {
+ meteringRepeating = getMeteringRepeatingAndInitDisplay(dummySizeListWithoutSmaller)
+
+ meteringRepeating.updateSuggestedResolution(dummyZeroSize)
+
+ assertEquals(Size(1280, 720), meteringRepeating.attachedSurfaceResolution)
+ }
+
+ @Test
+ fun attachedSurfaceResolutionIsLargestWithinPreviewSize_whenAllOutputSizesLessThan640x480() {
+ meteringRepeating = getMeteringRepeatingAndInitDisplay(dummySizeListSmallerThan640x480)
+
+ meteringRepeating.updateSuggestedResolution(dummyZeroSize)
+
+ assertEquals(Size(320, 480), meteringRepeating.attachedSurfaceResolution)
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
index 865ad2c..13ef7aa 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
@@ -27,6 +27,7 @@
import androidx.camera.camera2.pipe.integration.testing.FakeUseCaseCameraComponentBuilder
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
+import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -183,6 +184,7 @@
FakeCamera2CameraControlCompat(),
useCaseThreads,
ComboRequestListener()
- )
+ ),
+ displayInfoManager = DisplayInfoManager(ApplicationProvider.getApplicationContext())
)
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RequestWithCallback.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RequestWithCallback.java
index c9816f5..dd26326 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RequestWithCallback.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RequestWithCallback.java
@@ -144,6 +144,10 @@
@MainThread
void abortAndSendErrorToApp(@NonNull ImageCaptureException imageCaptureException) {
checkMainThread();
+ if (mCompleteFuture.isDone()) {
+ // The app has already received a callback. No need to abort.
+ return;
+ }
abort();
onFailure(imageCaptureException);
}
@@ -151,6 +155,10 @@
@MainThread
void abortSilentlyAndRetry() {
checkMainThread();
+ if (mCompleteFuture.isDone()) {
+ // The app has already received a callback. No need to abort.
+ return;
+ }
abort();
mRetryControl.retryRequest(mTakePictureRequest);
}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RequestWithCallbackTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RequestWithCallbackTest.kt
index f0b794e..acdc78e 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RequestWithCallbackTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RequestWithCallbackTest.kt
@@ -91,6 +91,37 @@
}
@Test
+ fun abortAndRetryAfterSuccess_abortIsNoOp() {
+ // Arrange.
+ val request = FakeTakePictureRequest(FakeTakePictureRequest.Type.IN_MEMORY)
+ val callback = RequestWithCallback(request, retryControl)
+
+ // Act: deliver result then abort
+ callback.onImageCaptured()
+ callback.onFinalResult(imageResult)
+ callback.abortSilentlyAndRetry()
+ shadowOf(getMainLooper()).idle()
+
+ // Assert: retry is not queued.
+ assertThat(retryControl.retriedRequest).isNull()
+ }
+
+ @Test
+ fun abortAndFailAfterFail_abortIsNoOp() {
+ // Arrange.
+ val request = FakeTakePictureRequest(FakeTakePictureRequest.Type.IN_MEMORY)
+ val callback = RequestWithCallback(request, retryControl)
+
+ // Fail request then abort
+ callback.onCaptureFailure(otherError)
+ callback.abortAndSendErrorToApp(abortError)
+ shadowOf(getMainLooper()).idle()
+
+ // Assert:
+ assertThat(request.exceptionReceived).isEqualTo(otherError)
+ }
+
+ @Test
fun abortRequestThenSendOtherErrors_receiveAbortError() {
// Arrange.
val request = FakeTakePictureRequest(FakeTakePictureRequest.Type.IN_MEMORY)
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/Recording.java b/camera/camera-video/src/main/java/androidx/camera/video/Recording.java
index 9002f39..c4c874b 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/Recording.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/Recording.java
@@ -16,8 +16,11 @@
package androidx.camera.video;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
import androidx.camera.core.impl.utils.CloseGuardHelper;
import androidx.core.util.Consumer;
import androidx.core.util.Preconditions;
@@ -46,7 +49,7 @@
public final class Recording implements AutoCloseable {
// Indicates the recording has been explicitly stopped by users.
- private final AtomicBoolean mIsStopped = new AtomicBoolean(false);
+ private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
private final Recorder mRecorder;
private final long mRecordingId;
private final OutputOptions mOutputOptions;
@@ -59,7 +62,7 @@
mOutputOptions = options;
if (finalizedOnCreation) {
- mIsStopped.set(true);
+ mIsClosed.set(true);
} else {
mCloseGuard.open("stop");
}
@@ -119,7 +122,7 @@
* {@link #close()} or {@link #stop()}.
*/
public void pause() {
- if (mIsStopped.get()) {
+ if (mIsClosed.get()) {
throw new IllegalStateException("The recording has been stopped.");
}
mRecorder.pause(this);
@@ -138,43 +141,44 @@
* {@link #close()} or {@link #stop()}.
*/
public void resume() {
- if (mIsStopped.get()) {
+ if (mIsClosed.get()) {
throw new IllegalStateException("The recording has been stopped.");
}
mRecorder.resume(this);
}
/**
- * Stops the recording.
+ * Stops the recording, as if calling {@link #close()}.
*
- * <p>Once stopped, all methods for controlling the state of this recording besides
- * {@code stop()} or {@link #close()} will throw an {@link IllegalStateException}.
- *
- * <p>Once an active recording has been stopped, the next recording can be started with
- * {@link PendingRecording#start(Executor, Consumer)}.
- *
- * <p>This method is idempotent; if the recording has already been stopped or has been
- * finalized internally, calling {@code stop()} is a no-op.
+ * <p>This method is equivalent to calling {@link #close()}.
*/
public void stop() {
- mCloseGuard.close();
- if (mIsStopped.getAndSet(true)) {
- return;
- }
- mRecorder.stop(this);
+ close();
}
/**
- * Close this recording, as if calling {@link #stop()}.
+ * Close this recording.
+ *
+ * <p>Once {@link #stop()} or {@code close()} called, all methods for controlling the state of
+ * this recording besides {@link #stop()} or {@code close()} will throw an
+ * {@link IllegalStateException}.
+ *
+ * <p>Once an active recording has been closed, the next recording can be started with
+ * {@link PendingRecording#start(Executor, Consumer)}.
+ *
+ * <p>This method is idempotent; if the recording has already been closed or has been
+ * finalized internally, calling {@link #stop()} or {@code close()} is a no-op.
*
* <p>This method is invoked automatically on active recording instances managed by the {@code
* try-with-resources} statement.
- *
- * <p>This method is equivalent to calling {@link #stop()}.
*/
@Override
public void close() {
- stop();
+ mCloseGuard.close();
+ if (mIsClosed.getAndSet(true)) {
+ return;
+ }
+ mRecorder.stop(this);
}
@Override
@@ -192,4 +196,21 @@
long getRecordingId() {
return mRecordingId;
}
+
+ /**
+ * Returns whether the recording is closed.
+ *
+ * <p>The returned value does not reflect the state of the recording; it only reflects
+ * whether {@link #stop()} or {@link #close()} was called on this object.
+ *
+ * <p>The state of the recording should be checked from the listener passed to
+ * {@link PendingRecording#start(Executor, Consumer)}. Once the active recording is
+ * stopped, a {@link VideoRecordEvent.Finalize} event will be sent to the listener.
+ *
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public boolean isClosed() {
+ return mIsClosed.get();
+ }
}
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsImageCaptureStressTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsImageCaptureStressTest.kt
new file mode 100644
index 0000000..f9d8b9d
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/camera2extensions/Camera2ExtensionsImageCaptureStressTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.extensions.camera2extensions
+
+import android.content.Context
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraExtensionSession
+import android.hardware.camera2.CameraManager
+import android.hardware.camera2.params.OutputConfiguration
+import android.media.ImageReader
+import android.view.Surface
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.assumeCameraExtensionSupported
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.createCaptureImageReader
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.openCameraDevice
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.openExtensionSession
+import androidx.camera.integration.extensions.util.Camera2ExtensionsTestUtil.takePicture
+import androidx.camera.integration.extensions.util.assertImageIsValid
+import androidx.camera.integration.extensions.utils.CameraIdExtensionModePair
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.StressTestRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Stress test to verify that the camera can successfully capture images for all supported
+ * extension modes for each cameras ID.
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = 31)
+class Camera2ExtensionsImageCaptureStressTest(private val config: CameraIdExtensionModePair) {
+ @get:Rule
+ val useCamera =
+ CameraUtil.grantCameraPermissionAndPreTest(
+ CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
+ )
+
+ companion object {
+ @ClassRule
+ @JvmField val stressTest = StressTestRule()
+
+ @Parameterized.Parameters(name = "config = {0}")
+ @JvmStatic
+ fun parameters() = Camera2ExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
+ }
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
+
+ private lateinit var cameraDevice: CameraDevice
+ private lateinit var imageReader: ImageReader
+ private lateinit var captureSurface: Surface
+ private lateinit var extensionSession: CameraExtensionSession
+
+ @Before
+ fun setUp(): Unit = runBlocking {
+ assumeTrue(Camera2ExtensionsTestUtil.isTargetDeviceExcludedForExtensionsTest())
+
+ val (cameraId, extensionMode) = config
+
+ val extensionsCharacteristics = cameraManager.getCameraExtensionCharacteristics(cameraId)
+ assumeCameraExtensionSupported(extensionMode, extensionsCharacteristics)
+
+ cameraDevice = openCameraDevice(cameraManager, cameraId)
+ imageReader = createCaptureImageReader(extensionsCharacteristics, extensionMode)
+ captureSurface = imageReader.surface
+ val outputConfigurationCapture = OutputConfiguration(captureSurface)
+ extensionSession = openExtensionSession(
+ cameraDevice,
+ extensionMode,
+ listOf(outputConfigurationCapture)
+ )
+ assertThat(extensionSession).isNotNull()
+ }
+
+ @After
+ fun tearDown() {
+ if (::extensionSession.isInitialized) {
+ extensionSession.close()
+ }
+
+ if (::cameraDevice.isInitialized) {
+ cameraDevice.close()
+ }
+
+ if (::imageReader.isInitialized) {
+ imageReader.close()
+ }
+
+ if (::captureSurface.isInitialized) {
+ captureSurface.release()
+ }
+ }
+
+ @Test
+ fun captureImage(): Unit = runBlocking {
+ repeat(Camera2ExtensionsTestUtil.getStressTestRepeatingCount()) {
+ val image = takePicture(cameraDevice, extensionSession, imageReader)
+ assertThat(image).isNotNull()
+
+ image?.let {
+ assertThat(it.timestamp).isGreaterThan(0)
+ assertImageIsValid(it, imageReader.width, imageReader.height)
+ }
+
+ image!!.close()
+ }
+ }
+}
\ No newline at end of file
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
index 18051b9..429f3c8 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/Camera2ExtensionsTestUtil.kt
@@ -22,6 +22,7 @@
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraExtensionCharacteristics
import android.hardware.camera2.CameraExtensionSession
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
@@ -88,15 +89,7 @@
verifyOutput: Boolean = false
) {
val extensionsCharacteristics = cameraManager.getCameraExtensionCharacteristics(cameraId)
- assumeTrue(extensionsCharacteristics.supportedExtensions.contains(extensionMode))
- assumeTrue(
- extensionsCharacteristics
- .getExtensionSupportedSizes(extensionMode, SurfaceTexture::class.java).isNotEmpty()
- )
- assumeTrue(
- extensionsCharacteristics
- .getExtensionSupportedSizes(extensionMode, ImageFormat.JPEG).isNotEmpty()
- )
+ assumeCameraExtensionSupported(extensionMode, extensionsCharacteristics)
// Preview surface
val previewSize = extensionsCharacteristics
@@ -119,11 +112,7 @@
val previewSurface = Surface(surfaceTextureHolder.surfaceTexture)
// Still capture surface
- val captureSize = extensionsCharacteristics
- .getExtensionSupportedSizes(extensionMode, ImageFormat.JPEG)
- .maxBy { it.width * it.height }
- val imageReader = ImageReader
- .newInstance(captureSize.width, captureSize.height, ImageFormat.JPEG, 2)
+ val imageReader = createCaptureImageReader(extensionsCharacteristics, extensionMode)
val captureSurface = imageReader.surface
val cameraDevice = openCameraDevice(cameraManager, cameraId)
@@ -197,9 +186,39 @@
}
/**
+ * Check if the device supports the [extensionMode] and other extension specific characteristics
+ * required for testing. Halt the test if any criteria is not satisfied.
+ */
+ fun assumeCameraExtensionSupported(
+ extensionMode: Int,
+ extensionsCharacteristics: CameraExtensionCharacteristics
+ ) {
+ assumeTrue(extensionsCharacteristics.supportedExtensions.contains(extensionMode))
+ assumeTrue(
+ extensionsCharacteristics
+ .getExtensionSupportedSizes(extensionMode, SurfaceTexture::class.java).isNotEmpty()
+ )
+ assumeTrue(
+ extensionsCharacteristics
+ .getExtensionSupportedSizes(extensionMode, ImageFormat.JPEG).isNotEmpty()
+ )
+ }
+
+ fun createCaptureImageReader(
+ extensionsCharacteristics: CameraExtensionCharacteristics,
+ extensionMode: Int
+ ): ImageReader {
+ val captureSize = extensionsCharacteristics
+ .getExtensionSupportedSizes(extensionMode, ImageFormat.JPEG)
+ .maxBy { it.width * it.height }
+ return ImageReader
+ .newInstance(captureSize.width, captureSize.height, ImageFormat.JPEG, 2)
+ }
+
+ /**
* Open the camera device and return the [CameraDevice] instance.
*/
- private suspend fun openCameraDevice(
+ suspend fun openCameraDevice(
cameraManager: CameraManager,
cameraId: String
): CameraDevice {
@@ -228,7 +247,7 @@
/**
* Open the [CameraExtensionSession] and return the instance.
*/
- private suspend fun openExtensionSession(
+ suspend fun openExtensionSession(
cameraDevice: CameraDevice,
extensionMode: Int,
outputConfigs: List<OutputConfiguration>
@@ -256,7 +275,11 @@
return deferred.await()
}
- private suspend fun takePicture(
+ /**
+ * Take a picture with the provided [session] and output the contents to the [imageReader]. The
+ * latest image written to the [imageReader] is returned.
+ */
+ suspend fun takePicture(
cameraDevice: CameraDevice,
session: CameraExtensionSession,
imageReader: ImageReader
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/ImageCaptureTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/ImageCaptureTestUtil.kt
new file mode 100644
index 0000000..1875421
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/ImageCaptureTestUtil.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.extensions.util
+
+import android.graphics.BitmapFactory
+import android.graphics.ImageFormat
+import android.media.Image
+import com.google.common.truth.Truth.assertThat
+import junit.framework.TestCase.assertNotNull
+import junit.framework.TestCase.assertTrue
+
+/**
+ * Validate the image can be correctly decoded from a jpeg format to a bitmap.
+ */
+fun assertImageIsValid(image: Image, width: Int, height: Int) {
+ assertThat(image.width).isEqualTo(width)
+ assertThat(image.height).isEqualTo(height)
+ assertThat(image.format).isEqualTo(ImageFormat.JPEG)
+
+ val data = imageData(image)
+ assertTrue("Invalid image data", data.isNotEmpty())
+
+ val bmpOptions = BitmapFactory.Options().apply {
+ inJustDecodeBounds = true
+ }
+
+ BitmapFactory.decodeByteArray(data, 0, data.size, bmpOptions)
+
+ assertThat(width).isEqualTo(bmpOptions.outWidth)
+ assertThat(height).isEqualTo(bmpOptions.outHeight)
+
+ val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
+ assertNotNull("Decoding jpeg failed", bitmap)
+}
+
+private fun imageData(image: Image): ByteArray {
+ val planes = image.planes
+ assertTrue("Fail to get image planes", planes != null && planes.isNotEmpty())
+
+ val buffer = planes[0].buffer
+ assertNotNull("Fail to get jpeg ByteBuffer", buffer)
+
+ val data = ByteArray(buffer.remaining())
+ buffer.get(data)
+ buffer.rewind()
+ return data
+}
\ No newline at end of file
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml
index 6f6ad1c..d269c76 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Taaklimiet is bereik\nAs jy voortgaan, sal die program verplig word om te stop"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Taak se stap %1$d van %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Klik om aan te beweeg"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Besoek asseblief die verskillende template en maak seker die motor is in rymodus"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstrasie van wisselknoppie"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Wissel toets"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Dinamiese veranderinge word toegelaat"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Opdaterings toegelaat vir terughandelinge."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Wisseltoets is geaktiveer"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Wisseltoets is gedeaktiveer"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Sekondêre handeling en versieringdemonstrasie"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Sekondêrehandelingtoets"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Net die sekondêre handeling kan gekies word"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Versieringtoets"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekondêre handelinge en versiering"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Die ry kan ook gekies word"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Sekondêre handeling is gekies"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Primêre handeling vir ry is gekies"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrasies oor diverse template"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Stal demonstrasies uit"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrasie van templaatuitlegte"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Demonstrasieskerm vir stemtoegang"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Gebruikerinteraksies"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Versoek demonstrasies van toestemmings"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Appoorvloei-bekragtiger"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Toets asseblief die volgende template terwyl jy"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"die voertuig van parkeer- na bestuurstaat toe verander"</string>
<string name="perm_group" msgid="3834918337351876270">"Toestemminggroep"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Toestemminggroep vir uitstalprogram"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Toegang tot fyn ligging"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
index 364cd95..70ff493 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"የተግባር ገደብ ላይ ደርሷል\nወደፊት መሄድ መተግበሪያውን በኃይል ያስቆማል"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"የተግባር እርምጃ %1$d ከ%2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ወደፊት ለመሄድ ጠቅ ያድርጉ"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"እባክዎ የተለያዩ የቅንብር ደንቦችን ይጎብኙ እና መኪናው የማሽከርከር ሁነታ ላይ እንደሆነ ያረጋግጡ"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"የቀያይር አዝራር ቅንጭብ ማሳያ"</string>
<string name="toggle_test_title" msgid="924485265152862631">"ሙከራን ቀያይር"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"ተጨባጭ ለውጦች ተፈቅደዋል"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"ዝማኔዎች በዳራ ክወናዎች ላይ ይፈቀዳሉ።"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"የመቀያየር ሙከራ ነቅቷል"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"የመቀያየር ሙከራ ተሰናክሏል"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ሁለተኛ እርምጃ እና የማስዋቢያ ቅንጭብ ማሳያ"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"የሁለተኛ እርምጃ ሙከራ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"መመረጥ የሚችለው ሁለተኛው እርምጃ ብቻ ነው"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"የማስዋቢያ ሙከራ"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ሁለተኛ እርምጃዎች እና ማስዋብ"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"እንዲሁም ረድፉ መመረጥ ይችላል"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"ሁለተኛ እርምጃ ተመርጧል"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"የረድፍ አንደኛ እርምጃ ተመርጧል"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"የተለያዩ ቅንብር ደንቦች ቅንጭብ ማሳያዎች"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"የመሳያ ቅንጭብ ማሳያ"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"የቅንብር ደንብ አቀማመጥ ቅንጭብ ማሳያዎች"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"የድምጽ መዳረሻ ቅንጭብ ማሳያ ማያ ገጽ"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"የተጠቃሚ መስተጋብሮች"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"የቅንጭብ ማሳያ ፈቃዶችን ይጠይቁ"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"የመተግበሪያ ትርፍ አረጋጋጭ"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"በሚቀይሩበት ጊዜ እባክዎ የሚከተሉትን የቅንብር ደንቦች ይሞክሩ"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ተሽከርካሪው ከመቆም ወደ የማሽከርከር ሁኔታ"</string>
<string name="perm_group" msgid="3834918337351876270">"የፈቃድ ቡድን"</string>
<string name="perm_group_description" msgid="7348847631139139024">"ለማሳያ መተግበሪያ የፈቃድ ቡድን"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"የትክክለኛ አካባቢ መዳረሻ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml
index 576646c..afb470f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"تم الوصول إلى حد المهمّة.\nسيؤدي التقدم إلى فرض إيقاف التطبيق."</string>
<string name="task_step_of_title" msgid="2791717962535723839">"خطوة المهمة: %1$d من %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"انقر للتقدم إلى الأمام."</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"يُرجى الانتقال إلى النماذج المختلفة والتأكّد من أنّ السيارة في وضع القيادة."</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"عرض توضيحي لزر الإيقاف/التفعيل"</string>
<string name="toggle_test_title" msgid="924485265152862631">"إيقاف/تفعيل الاختبار"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"يُسمَح بتغيير الحالة."</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"التحديثات المسموح بها على العمليات التي تتم بالخلفية"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"تم تفعيل زر إيقاف/تفعيل الاختبار."</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"تم إيقاف زر إيقاف/تفعيل الاختبار."</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"عرض توضيحي للإجراء الثانوي والتصميم"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"إجراء ثانوي تجريبي"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"يمكن اختيار الإجراء الثانوي فقط."</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"تصميم تجريبي"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"الإجراءات الثانوية والتصميم"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"يمكن أيضًا اختيار الصف."</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"تم اختيار إجراء ثانوي للصف."</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"تم اختيار إجراء أساسي للصف."</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"إصدارات تجريبية لنموذج ميزات متنوّعة"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"عرض الإصدارات التجريبية"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"عروض توضيحية لتنسيقات النماذج"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"شاشة العرض التوضيحي للاستخدام عبر الصوت"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"تفاعلات المستخدمين"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"عروض توضيحية لطلب الأذونات"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"أداة السماح بالتجاوز"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"يُرجى تجربة النماذج التالية أثناء تغيير"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"حالة المركبة من \"مركونة\" إلى \"القيادة\""</string>
<string name="perm_group" msgid="3834918337351876270">"مجموعة أذونات"</string>
<string name="perm_group_description" msgid="7348847631139139024">"مجموعة أذونات لتطبيق العرض"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"الوصول إلى الموقع الدقيق"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml
index 5e1c2d3..7e4e8da 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"কাৰ্যৰ সীমাত উপনীত হৈছে\nআগবাঢ়ি গ’লে এপ্টো বলপূৰ্বকভাৱে বন্ধ কৰা হ’ব"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"কাৰ্যৰ পদক্ষেপ %2$d টাৰ %1$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"আগবাঢ়ি যাবলৈ ক্লিক কৰক"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"অনুগ্ৰহ কৰি বিভিন্ন টেমপ্লে’টসমূহ চাওক আৰু গাড়ীখন ড্ৰাইভিং ম’ডত থকাটো নিশ্চিত কৰক"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"ট’গল বুটামৰ ডেম’"</string>
<string name="toggle_test_title" msgid="924485265152862631">"পৰীক্ষা ট’গল কৰক"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"ষ্টে’টফুল সলনি কৰাৰ অনুমতি আছে"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"নেপথ্যৰ কাৰ্যত আপডে’টৰ অনুমতি আছে।"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"পৰীক্ষা ট’গল কৰাৰ সুবিধা সক্ষম কৰা হৈছে"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"পৰীক্ষা ট’গল কৰাৰ সুবিধা অক্ষম কৰা হৈছে"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"গৌণ কাৰ্য আৰু সজোৱা কাৰ্যৰ ডেম’"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"গৌণ কাৰ্যৰ পৰীক্ষা"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"কেৱল গৌণ কাৰ্যটোও বাছনি কৰিব পৰা যায়"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"সজোৱা কাৰ্য"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"গৌণ কাৰ্য আৰু সজোৱা কাৰ্য"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"শাৰীটোও বাছনি কৰিব পৰা যায়"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"গৌণ কাৰ্য বাছনি কৰা হৈছে"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"শাৰীৰ প্ৰাথমিক কাৰ্য বাছনি কৰা হৈছে"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"সানমিহলি টেম্পলে’টৰ ডেম’"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"ডেম’ দেখুৱাওক"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"টেমপ্লে’ট লে’আউটৰ ডেম’"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"কণ্ঠধ্বনিৰে এক্সেছ কৰাৰ ডেম’ৰ স্ক্ৰীন"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"ব্যৱহাৰকাৰীৰ ভাব-বিনিময়"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"অনুমতিৰ অনুৰোধৰ ডেম’"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"এপ্লিকেশ্বন অভাৰফ্ল’ৰ মান্যতা প্ৰদানকাৰী"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"অনুগ্ৰহ কৰি, সলনি কৰাৰ সময়ত তলত দিয়া টেমপ্লে’টসমূহ পৰীক্ষা কৰি চাওক"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ৰখাই থোৱা অৱস্থাৰ পৰা চলাই থকা অৱস্থালৈ বাহনখন"</string>
<string name="perm_group" msgid="3834918337351876270">"অনুমতিৰ গোট"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase এপক দিয়া অনুমতিৰ গোট"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"সঠিক অৱস্থানৰ এক্সেছ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml
index f7eab61..de50258 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Tapşırıq limitinə çatdınız\nİrəli getmək tətbiqi məcburi dayandıracaq"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Tapşırıq mərhələsi %1$d/%2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"İrəli keçmək üçün klikləyin"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Müxtəlif şablonları ziyarət edin və avtomobilin sürücülük rejimində olduğuna əmin olun"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Keçirmə Düyməsi Demosu"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Keçirmə testi"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Status dəyişikliyinə icazə verilir"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Geri əməliyyatlar üzrə güncəlləmələrə icazə verilir."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Keçirmə Testi Aktivdir"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Keçirmə Testi Deaktivdir"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"İkinci Dərəcəli Əməliyyat və Dekorasiya Demosu"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"İkinci Dərəcəli Əməliyyat Testi"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Yalnız ikinci dərəcəli əməliyyat seçilə bilər"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Dekorasiya Testi"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"İkinci Dərəcəli Əməliyyatlar və Dekorasiya"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Sıra da seçilə bilər"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"İkinci Dərəcəli Əməliyyat seçilib"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Sıra üzrə əsas əməliyyat seçilib"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Digər Şablon Demoları"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Vitrin Demoları"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Şablon Düzən Demoları"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Səsli Giriş Demo Ekranı"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"İstifadəçi əlaqələri"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"İcazə Demosu Tələb edin"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Tətbiq Aşma Doğrulayıcısı"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Avtomobili park vəziyyətindən sürmə vəziyyətinə"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"dəyişdirərkən aşağıdakı şablonları sınayın"</string>
<string name="perm_group" msgid="3834918337351876270">"İcazə Qrupu"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Xəbərlər Vitrini Tətbiqi üçün İcazə Qrupu"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Dəqiq Məkana Giriş"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
index 21b1418..b6e3dac 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Dostignuto je ograničenje zadataka\nAko nastavite, aplikacija će se prinudno zaustaviti"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%1$d. korak zadatka od %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Kliknite da biste išli napred"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Posetite različite šablone i uverite se da je automobil u režimu vožnje"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstracija dugmeta za uključivanje/isključivanje"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Uključi/isključi test"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Promene stanja su dozvoljene"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Ažuriranja su dozvoljena za operacije u pozadini."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Test uključivanja/isključivanja je omogućen"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Test uključivanja/isključivanja je onemogućen"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demonstracija sekundarne radnje i dekoracije"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test sekundarne radnje"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Može da se izabere samo sekundarna radnja"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Test dekoracije"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundarne radnje i dekoracija"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Može da se izabere i red"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Sekundarna radnja je izabrana"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Primarna radnja reda je izabrana"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstracije različitih šablona"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demonstracije prikazivanja"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstracije izgleda šablona"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Ekran demonstracije pristupa glasu"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Korisničke interakcije"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demonstracije zahteva za dozvole"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validator prekoračenja aplikacije"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testirajte sledeće šablone tokom punjenja"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"status vozila iz parkiranog u vožnja"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupa dozvola"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupa dozvola za isticanje aplikacije"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Pristup preciznoj lokaciji"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml
index 2deca03..eb81f87 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Дасягнута максімальная колькасць задач\nДалейшы рух прывядзе да прымусовага спынення праграмы"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Крок задачы %1$d з %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Націсніце, каб рухацца далей"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Праглядзіце розныя шаблоны і пераканайцеся, што аўтамабіль знаходзіцца ў рэжыме \"За рулём\""</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Дэманстрацыя кнопкі пераключэння"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Праверка пераключэння"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Дазволена змяняць стан"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Абнаўленні дазволены для аперацый вяртання."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Праверка пераключэння ўключана"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Праверка пераключэння адключана"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Дэмаверсія другаснага дзеяння і аздаблення"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Тэст другаснага дзеяння"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Можна выбраць толькі другаснае дзеянне"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Тэст элементаў аздаблення"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Другасныя дзеянні і аздабленне"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Таксама можна выбраць радок"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Выбрана другаснае дзеянне"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Выбрана галоўнае дзеянне для радка"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Дэманстрацыі розных шаблонаў"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Дэманстрацыі выбранага"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Дэманстрацыі макета шаблона"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Экран дэманстрацыі Галасавога доступу"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Узаемадзеянне з карыстальнікамі"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Запытаць дэманстрацыі дазволаў"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Сродак праверкі дадатковага меню праграмы"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Пры змяненні пратэсціруйце наступныя шаблоны"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"змяненне стану аўтамабіля з \"Прыпаркаваны\" на \"За рулём\""</string>
<string name="perm_group" msgid="3834918337351876270">"Група дазволаў"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Група дазволаў для праграмы \"Выбранае\""</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Доступ да дакладнага месцазнаходжання"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml
index 2aad28d..f278d69 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Ограничението за задачи е достигнато\nАко продължите, приложението ще бъде спряно принудително"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Стъпка %1$d от %2$d в задачата"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Кликнете, за да продължите напред"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Отворете отделните шаблони, докато автомобилът е в режим за шофиране"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Демонстрация на бутон за превключване"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Тест с превключвател"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Промените на състоянието са разрешени"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Актуализациите са разрешени за операции за връщане назад."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Тестът с превключвател е активиран"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Тестът с превключвател е деактивиран"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демонстрация на алтернативно действие и декоративен елемент"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Тестване на алтернативно действие"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Само алтернативното действие може да бъде избрано"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Тестване на декоративен елемент"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Алтернативни действия и декоративни елементи"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Редът може също да бъде избран"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Избрано е алтернативното действие"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Избрано е основното действие за реда"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрации на разни шаблони"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Демонстрации на Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Демонстрации на оформления за шаблон"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Екран за демонстрация на Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Потребителски взаимодействия"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демонстрации за заявяване на разрешения"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Проверка при препълване за приложението"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Тествайте следните шаблони, докато превключвате между"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"режим за паркиране и шофиране"</string>
<string name="perm_group" msgid="3834918337351876270">"Група разрешения"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Група разрешения за приложението „На фокус“"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Достъп до точното местоположение"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml
index 541d5e3..282632f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"টাস্কের সীমা পেরিয়ে গেছে\nআরও এগোলে, অ্যাপকে জোর করে বন্ধ করা হবে"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"টাস্কের %2$d ধাপের %1$d নম্বর ধাপ"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"এগিয়ে যাওয়ার জন্য ক্লিক করুন"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"বিভিন্ন টেমপ্লেট দেখুন ও নিশ্চিত করুন যে গাড়িটি \'ড্রাইভিং\' মোডে আছে"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"টগল বোতাম সংক্রান্ত ডেমো"</string>
<string name="toggle_test_title" msgid="924485265152862631">"টগল টেস্ট"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"স্টেটফুল পরিবর্তন করার অনুমতি দেওয়া হয়েছে"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"পিছনের পৃষ্ঠাতে গিয়ে পরিবর্তন করা যাবে।"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"টগল টেস্ট চালু করা হয়েছে"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"টগল টেস্ট বন্ধ করা হয়েছে"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"সেকেন্ডারি অ্যাকশন ও ডেকরেশন ডেমো"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"সেকেন্ডারি অ্যাকশন টেস্ট"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"শুধুমাত্র সেকেন্ডারি অ্যাকশন বেছে নেওয়া যেতে পারে"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"ডেকরেশন টেস্ট"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"সেকেন্ডারি অ্যাকশন ও ডেকরেশন"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"সারিও বেছে নেওয়া যেতে পারে"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"সেকেন্ডারি অ্যাকশন বেছে নেওয়া হয়েছে"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"সারির প্রাইমারি অ্যাকশন বেছে নেওয়া হয়েছে"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"অন্যান্য টেমপ্লেটের ডেমো"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"ডেমো শোকেস করুন"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"টেমপ্লেট লেআউট সংক্রান্ত ডেমো"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"\'ভয়েস অ্যাক্সেস\' অ্যাপের ডেমো স্ক্রিন"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"ব্যবহারকারীর ইন্টার্যাকশন"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"অনুমতি সংক্রান্ত ডেমোর জন্য অনুরোধ জানানো"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"অ্যাপ্লিকেশন ওভারফ্লো ভ্যালিডেটর"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"পরিবর্তন করার সময় নিম্নলিখিত টেমপ্লেট পরীক্ষা করুন"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"গাড়ি পার্ক করা থেকে ড্রাইভিং অবস্থা"</string>
<string name="perm_group" msgid="3834918337351876270">"অনুমতি সংক্রান্ত গ্রুপ"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase অ্যাপের জন্য অনুমতি সংক্রান্ত গ্রুপ"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"সুনির্দিষ্ট লোকেশনে অ্যাক্সেস"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml
index f92b5a5..e2b505e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Dostignuto je ograničenje za zadatak\nAko nastavite, prisilno ćete zaustaviti aplikaciju"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Korak zadatka: %1$d od %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Kliknite da idete naprijed"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Posjetite različite šablone i pobrinite se da automobil bude u načinu rada za vožnju"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo verzija dugmeta za uključivanje/isključivanje"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Testiranje uključivanja/isključivanja"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Dozvoljene su izmjene koje zavise od statusa"</string>
@@ -307,14 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Dozvoljena su ažuriranja u vezi s radnjama navigiranja prema nazad."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Testiranje uključivanja/isključivanja je omogućeno"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Testiranje uključivanja/isključivanja je onemogućeno"</string>
- <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Pokazna verzija sekundarne radnje i dekoracije"</string>
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Sekundarna radnja i demo verzija dekoracije"</string>
<string name="secondary_actions_test_title" msgid="3664453747553733613">"Test sekundarne radnje"</string>
- <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Može se odabrati samo sekundarna radnja"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Moguće je odabrati samo sekundarnu radnju"</string>
<string name="decoration_test_title" msgid="8450127046762442244">"Test dekoracije"</string>
<string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundarne radnje i dekoracija"</string>
- <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Može se odabrati i redak"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Moguće je odabrati i red"</string>
<string name="secondary_action_toast" msgid="5076434693504006565">"Odabrana je sekundarna radnja"</string>
- <string name="row_primary_action_toast" msgid="756516694751965204">"Odabran je redak primarne radnje"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Odabrana je primarna radnja reda"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demo verzije raznih šablona"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demo verzije predstavljanja"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demo verzije rasporeda predloška"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Ekran demo verzije Pristupa glasom"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Korisničke interakcije"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Zatraži demo verzije odobrenja"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Alat za provjeru preklopnog menija aplikacije"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testirajte sljedeće šablone tokom promjene"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"vozilo iz stanja parkiranosti u stanje vožnje"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupa odobrenja"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupa odobrenja za aplikaciju Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Pristup preciznoj lokaciji"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
index db8e8fa..e51c494 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"S\'ha arribat al límit de tasques\nEn continuar es forçarà l\'aturada de l\'aplicació"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Pas de la tasca %1$d de %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Fes clic per anar endavant"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Visita les diferents plantilles i comprova que el cotxe sigui en mode de conducció"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demostració d\'un botó de commutació"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Test de commutació"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Es permeten canvis amb estat"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Es permeten actualitzacions a les operacions d\'anar enrere."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Prova de commutació activada"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Prova de commutació desactivada"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demostració de l\'acció secundària i la decoració"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Prova de l\'acció secundària"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Només es pot seleccionar l\'acció secundària"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Prova de decoració"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Accions secundàries i decoració"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"La fila també es pot seleccionar"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"S\'ha seleccionat l\'acció secundària"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"S\'ha seleccionat l\'acció principal de la fila"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demostracions de plantilles diverses"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demostracions de Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demostracions de dissenys de plantilles"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Pantalla de demostració de Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interaccions dels usuaris"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demostracions de sol·licitud de permisos"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validador del menú addicional de l\'aplicació"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Prova les següents plantilles mentre canvies"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"el vehicle de l\'estat aparcat a mode de conducció"</string>
<string name="perm_group" msgid="3834918337351876270">"Grup de permisos"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grup de permisos de l\'aplicació Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Accés a la ubicació precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml
index ba7bab0..5f4d0ad 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Byl dosažen limit úkolů\nPokračováním vynutíte ukončení aplikace"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Krok úkolu %1$d z %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Kliknutím přejdete vpřed"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Navštivte různé šablony a ujistěte se, že je auto v režimu jízdy"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Ukázka přepínače"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Test přepínače"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Sledované změny jsou povoleny"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"U zpětných operací jsou povoleny aktualizace."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Test přepínače je povolen"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Test přepínače byl zakázán"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Ukázka sekundární akce a dekorace"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test sekundární akce"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Lze vybrat pouze sekundární akci"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Zkouška dekorace"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundární akce a dekorace"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Řádek lze také vybrat"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Je vybrána sekundární akce"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Je vybrána primární akce řádku"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Ukázky různých šablon"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Ukázky Výběru"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Ukázky šablon rozvržení"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Ukázka obrazovky pro ovládání hlasem"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interakce uživatelů"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Ukázky žádostí o oprávnění"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validátor přetečení aplikace"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Vyzkoušejte následující šablony při přechodu"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"auta ze zaparkovaného do jedoucího stavu"</string>
<string name="perm_group" msgid="3834918337351876270">"Skupina oprávnění"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Skupina oprávnění pro vystavenou aplikaci"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Přístup k přesné poloze"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml
index 03958d9..5039e9d 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Grænsen for opgaver blev nået\nHvis du fortsætter, tvinges appen til at standse"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Opgavetrin %1$d af %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Klik for at gå videre"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Gå til de forskellige skabeloner, og tjek, at bilen er i køretilstand"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo for kontakt"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Slå test til/fra"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Tilstandsfulde ændringer er tilladt"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Demoskærm for Stemmeadgang"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Brugerinteraktioner"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Anmod om tilladelsesdemoer"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validator for overløb af apps"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Test følgende skabeloner, mens du skifter"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"bilens tilstand fra Parkeret til Kører"</string>
<string name="perm_group" msgid="3834918337351876270">"Tilladelsesgruppe"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Tilladelsesgruppe for Showcase-appen"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Adgang til nøjagtig lokation"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml
index e3c79af..0ea9b40 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Aufgabenlimit erreicht\nDurch Fortfahren wird das Beenden der App erzwungen"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Aufgabe: Schritt %1$d von %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Klicken, um fortzufahren"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Bitte rufe die verschiedenen Vorlagen auf und achte darauf, dass sich das Auto im Fahrmodus befindet"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo der Ein-/Aus-Schaltfläche"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Test der Ein-/Aus-Schaltfläche"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Zustandsorientierte Änderungen sind erlaubt"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Updates beim Zurückgehen erlaubt."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Test der Ein-/Aus-Schaltfläche aktiviert"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Test der Ein-/Aus-Schaltfläche deaktiviert"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo der sekundären Aktion und der Dekoration"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test für sekundäre Aktion"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Nur die sekundäre Aktion kann ausgewählt werden"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Dekorationstest"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundäre Aktionen und Dekoration"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Auch diese Zeile kann ausgewählt werden"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Sekundäre Aktion ist ausgewählt"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Zeile mit der primären Aktion ist ausgewählt"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demos der verschiedenen Vorlagen"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demos anzeigen"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demo der Layoutvorlagen"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Bildschirm für Voice Access-Demo"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Nutzerinteraktionen"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demos zur Berechtigungsanfrage"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Anwendungs-Overflow-Validator"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Bitte teste die folgenden Vorlagen, wenn du"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"das Fahrzeug vom Park- in den Fahrmodus umschaltest"</string>
<string name="perm_group" msgid="3834918337351876270">"Berechtigungsgruppe"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Berechtigungsgruppe für die Showcase App"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Zugriff auf genauen Standort"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml
index 5213362..13c3309 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Συμπληρώθηκε το όριο εργασιών\nΜε μετάβαση προς τα εμπρός, θα γίνει αναγκαστική διακοπή της εφαρμογής."</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Βήμα εργασίας %1$d από %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Κάντε κλικ για μετάβαση εμπρός"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Επισκεφτείτε διαφορετικά πρότυπα και διασφαλίστε ότι το αυτοκίνητό σας είναι σε λειτουργία οδήγησης"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Επίδειξη κουμπιού εναλλαγής"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Εναλλαγή δοκιμής"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Επιτρέπονται οι αλλαγές με επίβλεψη κατάστασης"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Οθόνη επίδειξης Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Αλληλεπιδράσεις χρήστη"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Αίτημα για επιδείξεις αδειών"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Επικύρωση υπερχείλισης εφαρμογής"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Δοκιμάστε τα παρακάτω πρότυπα κατά την αλλαγή"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"της κατάστασης του αυτοκινήτου από στάθμευση σε οδήγηση"</string>
<string name="perm_group" msgid="3834918337351876270">"Ομάδα αδειών"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Ομάδα αδειών για την εφαρμογή Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Πρόσβαση στην Ακριβή τοποθεσία"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml
index d2a35fd..7d3d9cb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Toggle button demo"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Toggle test"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Stateful changes are allowed"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access demo screen"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"User interactions"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Request permissions demos"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Application overflow validator"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Please test the following templates while changing"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"the vehicle from parked to driving state"</string>
<string name="perm_group" msgid="3834918337351876270">"Permission group"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Permission group for Showcase app"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Access to fine location"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml
index d2a35fd..7d3d9cb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Toggle button demo"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Toggle test"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Stateful changes are allowed"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access demo screen"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"User interactions"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Request permissions demos"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Application overflow validator"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Please test the following templates while changing"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"the vehicle from parked to driving state"</string>
<string name="perm_group" msgid="3834918337351876270">"Permission group"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Permission group for Showcase app"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Access to fine location"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml
index d2a35fd..7d3d9cb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Toggle button demo"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Toggle test"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Stateful changes are allowed"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access demo screen"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"User interactions"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Request permissions demos"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Application overflow validator"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Please test the following templates while changing"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"the vehicle from parked to driving state"</string>
<string name="perm_group" msgid="3834918337351876270">"Permission group"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Permission group for Showcase app"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Access to fine location"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml
index d2a35fd..7d3d9cb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Toggle button demo"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Toggle test"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Stateful changes are allowed"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access demo screen"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"User interactions"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Request permissions demos"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Application overflow validator"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Please test the following templates while changing"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"the vehicle from parked to driving state"</string>
<string name="perm_group" msgid="3834918337351876270">"Permission group"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Permission group for Showcase app"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Access to fine location"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml
index 1e8bca3..dabdb75 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Se alcanzó el límite de tareas\nSi avanzas, se forzará la detención de la app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Paso %1$d de %2$d de la tarea"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Haz clic para avanzar"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Visita las diferentes plantillas y asegúrate de que el automóvil esté en modo de conducción"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demostración de botón de activación"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Prueba el botón de activación"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Se permiten los cambios con estado"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Se permiten las actualizaciones en las operaciones de retroceso."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Prueba del botón de activación habilitada"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Prueba del botón de activación desactivada"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demostración de una acción secundaria y decoración"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Prueba de la acción secundaria"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Sólo puede seleccionarse la acción secundaria"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Prueba de decoración"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Acciones secundarias y decoraciones"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"También se puede seleccionar la fila"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Se seleccionó una acción secundaria"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Se seleccionó una acción de fila principal"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demostraciones de plantillas varias"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demostraciones de Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demostración de plantilla de diseño"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Pantalla de demostración de Acceso por voz"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interacciones del usuario"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demostración de solicitudes de permisos"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validador de exceso de aplicaciones"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Prueba las siguientes plantillas mientras cambias"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"el vehículo del estado de estacionamiento al de conducción"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupo de permisos"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupo de permisos para la app de Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Acceso a la ubicación precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml
index 3db6907..1d82de7 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Se ha alcanzado el límite de tareas\nSi continuas, se forzará la detención de la aplicación"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Paso %1$d de %2$d de la tarea"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Haz clic para continuar"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Consulta las diferentes plantillas y comprueba que el coche esté en modo de conducción"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo de botón activar/desactivar"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Prueba del interruptor"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Se permiten cambios de estado"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Actualizaciones permitidas al ir atrás."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Prueba del interruptor habilitada"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Prueba del interruptor inhabilitada"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo sobre acción secundaria y decoración"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Prueba de acción secundaria"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Solo se puede seleccionar la acción secundaria"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Prueba de decoración"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Acciones secundarias y decoración"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"También se puede seleccionar la fila"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Se ha seleccionado la acción secundaria"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Se ha seleccionado la acción principal de la fila"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Otras demos de plantillas"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demos de Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demos de diseño de plantillas"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Pantalla de la demo de Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interacciones de usuarios"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demos de solicitud de permisos"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validador de desbordamiento de aplicaciones"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Prueba las siguientes plantillas mientras se hace el cambio"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"el vehículo del estado de estacionamiento al de conducción"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupo de permisos"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupo de permisos de la aplicación Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Acceso a la ubicación precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml
index 40d2630..9d508d5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Ülesannete piirang on saavutatud\nJätkamisel rakendus sundsuletakse"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Ülesande toiming %1$d %2$d-st"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Klõpsake edasiliikumiseks"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Vaadake eri mallid üle ja veenduge, et auto oleks sõidurežiimis"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Lülitusnupu demo"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Testi lüliti"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Olekulised muudatused on lubatud"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Värskendused on tagasiminekutoimingute korral lubatud."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Lülititest on lubatud"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Lülititest on keelatud"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Teisese toimingu ja dekoratsiooni demo"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Teisese toimingu test"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Valida saab ainult teisese toimingu"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Dekoratsiooni test"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Teisesed toimingud ja dekoratsioon"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Rea saab samuti valida"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Valitud on teisene toiming"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Valitud on rea peamine toiming"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Mitmesuguste mallide demod"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Esiletõstmise demod"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Malli paigutuse demod"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Accessi demo kuva"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Kasutaja interaktsioonid"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Lubade taotlemise demod"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Rakenduse ületäite kinnitaja"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testige järgmisi malle, kui lülitate"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"sõidukit pargitud olekust sõitmise olekusse"</string>
<string name="perm_group" msgid="3834918337351876270">"Lubade grupp"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase\'i rakenduse lubade grupp"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Juurdepääs täpsele asukohale"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml
index 447e227..eda6e05 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Zereginen mugara iritsi zara\nAurrera eginez gero, aplikazioa gelditzera behartuko duzu"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Zereginaren %1$d/%2$d urratsa"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Aurrera egiteko, sakatu hau"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Ikusi txantiloiak eta ziurtatu ibilgailua gidatze moduan dagoela"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Etengailuaren demo-bertsioa"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Etengailuaren proba"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Aldaketa egoeradunak egin daitezke"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Atzeko planoko eragiketak eguneratu egin daitezke."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Gaitu da etengailuaren proba"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Desgaitu da etengailuaren proba"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Bigarren mailako ekintzaren eta apainketaren demo-bertsioa"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Bigarren mailako ekintzaren proba"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Bigarren mailako ekintza soilik hauta daiteke"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Apainketaren proba"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Bigarren mailako ekintzak eta apainketa"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Errenkada ere hauta daiteke"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Bigarren mailako ekintza hautatuta dago"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Errenkadari dagokion ekintza nagusia hautatuta dago"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Bestelako txantiloien demo-bertsioak"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Erakutsi demo-bertsioak"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Txantiloi-diseinuen demo-bertsioak"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Ahots bidezko sarbidearen demo-bertsioaren pantaila"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Erabiltzailearen interakzioak"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Baimen-eskaeren demo-bertsioak"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Aplikazio-luzapenen baliozkotzailea"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Egin txantiloi hauen probak"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ibilgailua \"aparkatuta\" egoeratik \"gidatzen\" egoerara aldatu bitartean"</string>
<string name="perm_group" msgid="3834918337351876270">"Baimenen taldea"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase aplikazioaren baimenen taldea"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Kokapen zehatzerako sarbidea"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
index 4dbd909..1cd0cda 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"به حد مجاز تکلیف رسیدید\nاگر ادامه دهید، برنامه بهاجبار متوقف خواهد شد"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"مرحله %1$d از %2$d تکلیف"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"برای ادامه دادن، کلیک کنید"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"لطفاً از الگوهای مختلف بازدید کنید و مطمئن شوید خودرو در حالت رانندگی باشد"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"نسخه نمونه دکمه مبدل"</string>
<string name="toggle_test_title" msgid="924485265152862631">"امتحان کردن تغییر وضعیت"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"تغییرات حالتمند مجاز است"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"بهروزرسانی در عملیاتهای برگشتی مجاز است."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"آزمایش تغییر وضعیت فعال شد"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"آزمایش تغییر وضعیت غیرفعال شد"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"نمونه آرایهگری و کنش فرعی"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"آزمایش کنش فرعی"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"فقط کنش فرعی را میتوان انتخاب کرد"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"آزمایش آرایهگری"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"کنشهای فرعی و آرایهگری"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ردیف را هم میتوان انتخاب کرد"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"کنش فرعی انتخاب شده است"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"کنش اصلی ردیف انتخاب شده است"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"الگوهای متفرقه نمونه"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"نمایش نمونهها"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"نسخههای نمونه طرحبندی الگو"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"صفحه نمونه دسترسی صوتی"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"تعاملات کاربران"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"درخواست اجازه برای نسخههای نمونه"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"اعتبارسنج سرریز برنامه"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"لطفاً درحین تغییر دادن، الگوهای زیر را آزمایش کنید"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"وسیله نقلیه از حالت پارک به حالت رانندگی"</string>
<string name="perm_group" msgid="3834918337351876270">"گروه اجازه"</string>
<string name="perm_group_description" msgid="7348847631139139024">"گروه اجازه برنامه «ویترین»"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"دسترسی به مکان دقیق"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml
index 622bd9e..f5de8d0 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Tehtäväraja saavutettu\nEteneminen pakottaa sovelluksen sulkeutumaan"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Tehtävän vaihe %1$d/%2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Siirry eteenpäin klikkaamalla"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Tarkista eri mallit ja varmista, että auto on ajotilassa"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Päälle/pois-painikkeen esittely"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Vaihtotesti"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Tilattomat muutokset ovat sallittuja"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Päivitykset sallittu siirryttäessä edelliseen näkymään."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Vaihtotesti käytössä"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Vaihtotesti pois käytöstä"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Toissijainen toiminto ja kuvio ‑demo"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Toissijaisen toiminnon testi"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Voit valita vain toissijaisen toiminnon"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Kuviotesti"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Toissijaiset toiminnot ja kuvio"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Voit valita myös rivin"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Toissijainen toiminto on valittu"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Rivin ensisijainen toiminto on valittu"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Sekalaisten mallien esittelyt"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Showcase-esittelyt"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Malliasettelujen esittelyt"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Accessin esittelynäyttö"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Käyttäjän toiminta"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Pyydä lupien esittelyjä"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Sovelluksen ylivuodon tarkistustyökalu"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testaa seuraavat mallit vaihtaessasi"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"pysäköidystä autosta ajettuun autoon"</string>
<string name="perm_group" msgid="3834918337351876270">"Luparyhmä"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Esittely-sovelluksen luparyhmä"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Pääsy tarkkaan sijaintiin"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml
index a215254..1f27a42 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite de la tâche atteinte\nAller de l\'avant forcera l\'arrêt de l\'application"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Étape %1$d de %2$d de la tâche"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Cliquez pour avancer"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Veuillez consulter les différents modèles et vous assurer que la voiture est en mode Voiture"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Démo du commutateur"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Essai du commutateur"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Les changements à état sont autorisés"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Mises à jour autorisées lors des opérations de retour."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Essai du commutateur activé"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Essai du commutateur désactivé"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Démo d\'action secondaire et de décoration"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test d\'action secondaire"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Seule l\'action secondaire peut être sélectionnée"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Test de décoration"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Actions secondaires et décoration"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Cette ligne peut également être sélectionnée"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"L\'action secondaire est sélectionnée"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"L\'action primaire de la ligne est sélectionnée"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Démos de divers modèles"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Présenter les démos"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Démos de mise en page du modèle"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Écran d\'autorisation à l\'accès vocal"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interactions avec l\'utilisateur"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demande d\'autorisation pour les démos."</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Outil de validation de menu à développer d\'application"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Veuillez tester les modèles suivants lorsque vous basculez"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"l\'état du véhicule stationné à celui de conduite"</string>
<string name="perm_group" msgid="3834918337351876270">"Groupe d\'autorisations"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Groupe d\'autorisations pour l\'application Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Accès à la localisation précise"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml
index b38ddf4..917226e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite de tâche atteinte\nContinuer forcera l\'arrêt de l\'appli"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Étape %1$d sur %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Cliquer pour continuer"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Veuillez consulter les différents modèles et vous assurer que le véhicule est en mode Voiture."</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Démo du bouton d\'activation"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Activer/Désactiver le test"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Les modifications avec état sont autorisées"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Mises à jour autorisées sur les opérations de retour."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Test d\'activation/de désactivation activé"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Test d\'activation/de désactivation désactivé"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Démo de l\'action secondaire et de la décoration"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test de l\'action secondaire"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Seule l\'action secondaire peut être sélectionnée"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Test de la décoration"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Actions secondaires et décoration"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"La ligne peut également être sélectionnée"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"L\'action secondaire est sélectionnée"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"L\'action principale de la ligne est sélectionnée"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Démos de divers modèles"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Présenter les démos"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Démos de mise en page du modèle"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Écran de démo Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interactions des utilisateurs"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Démos de demandes d\'autorisation"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Programme de validation du menu de développement de l\'appli"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Veuillez tester les modèles suivant lorsque vous faites passer"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"l\'état du véhicule de garé à en mouvement"</string>
<string name="perm_group" msgid="3834918337351876270">"Groupe d\'autorisations"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Groupe d\'autorisations pour l\'appli Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Accès à la position précise"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml
index 7610451..f8d344e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml
@@ -307,22 +307,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Permítense actualizacións ao ir á pantalla anterior."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Activouse a proba do interruptor"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Desactivouse a proba do interruptor"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Acción secundaria e demostración de decoración"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Proba da acción secundaria"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Só se pode seleccionar a acción secundaria"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Proba de decoración"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Accións secundarias e decoración"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Tamén se pode seleccionar a fila"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Seleccionouse a acción secundaria"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Seleccionouse a acción principal da fila"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Outras demostracións de modelos"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demostracións de Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demostracións de deseños de modelo"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml
index c64878f..fc2bc84 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"કાર્યની મર્યાદાએ પહોંચી ગયા\nઆગળ વધવાથી ઍપ ફરજિયાત બંધ થઈ જશે"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%2$dમાંથી %1$d કાર્ય માટેનું પગલું"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"આગળ વધવા માટે ક્લિક કરો"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"કૃપા કરીને વિભિન્ન નમૂનાઓની મુલાકાત લો અને ખાતરી કરો કે કાર ડ્રાઇવિંગ મોડમાં છે"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"ટૉગલ બટનનો ડેમો"</string>
<string name="toggle_test_title" msgid="924485265152862631">"પરીક્ષણ ટૉગલ કરો"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"સ્ટેટસમાં થતા ફેરફારનો મંજૂરી છે"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"અગાઉના કાર્યો પર અપડેટ કરવાની મંજૂરી છે."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"ટૉગલનું પરીક્ષણ ચાલુ કર્યું"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"ટૉગલનું પરીક્ષણ બંધ કર્યું"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ગૌણ ઍક્શન અને ડેકોરેશન ડેમો"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"ગૌણ ઍક્શન પરીક્ષણ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"માત્ર ગૌણ ઍક્શન પસંદ કરી શકાય છે"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"ડેકોરેશન પરીક્ષણ"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ગૌણ ઍક્શન અને ડેકોરેશન"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"પંક્તિ પણ પસંદ કરી શકાય છે"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"ગૌણ ઍક્શન પસંદ કરી"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"પ્રાથમિક ઍક્શન પંક્તિ પસંદ કરી"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"વિવિધ નમૂનાઓના ડેમો"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"ડેમો બતાવો"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"નમૂનાના લેઆઉટનો ડેમો"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Accessના ડેમોની સ્ક્રીન"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"વપરાશકર્તાની ક્રિયાપ્રતિક્રિયાઓ"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"પરવાનગીઓના ડેમોની વિનંતી કરો"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ઍપ્લિકેશન ઓવરફ્લો વૅલિડેટર"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"વાહનને પાર્ક કરેલામાંથી ડ્રાઇવિંગ સ્થિતિમાં બદલતી વખતે"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"કૃપા કરીને નીચે આપેલા નમૂનાનું પરીક્ષણ કરો"</string>
<string name="perm_group" msgid="3834918337351876270">"પરવાનગીનું ગ્રૂપ"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase ઍપ માટે પરવાનગીનું ગ્રૂપ"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"સચોટ લોકેશનનો ઍક્સેસ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml
index 3d7f0af..44b1b65 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"टास्क की सीमा पूरी हुई\nआगे जाने पर, ऐप्लिकेशन को ज़बरदस्ती रोक दिया जाएगा"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"टास्क के %2$d चरणों में से %1$d चरण"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"आगे जाने के लिए क्लिक करें"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"कृपया अलग-अलग टेंप्लेट पर जाकर, यह पक्का करें कि कार ड्राइविंग मोड में है"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"टॉगल बटन का डेमो"</string>
<string name="toggle_test_title" msgid="924485265152862631">"टॉगल टेस्ट"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"स्टेटफ़ुल बदलाव करने की अनुमति है"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"पिछले पेज पर जाकर बदलाव किए जा सकते हैं."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"टॉगल टेस्ट चालू किया गया"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"टॉगल टेस्ट बंद किया गया"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"सेकंडरी ऐक्शन और डेकोरेशन का डेमो"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"सेकंडरी ऐक्शन टेस्ट"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"सिर्फ़ सेकंडरी ऐक्शन को चुना जा सकता है"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"डेकोरेशन टेस्ट"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"सेकंडरी ऐक्शन और डेकोरेशन"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"पंक्ति को भी चुना जा सकता है"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"सेकंडरी ऐक्शन चुना गया"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"पंक्ति से जुड़ा प्राइमरी ऐक्शन चुना गया"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"दूसरे टेंप्लेट के डेमो"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"डेमो दिखाएं"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"टेंप्लेट लेआउट के डेमो"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access के डेमो की स्क्रीन"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"यूज़र इंटरैक्शन"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"अनुमतियों के अनुरोध का डेमो"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ऐप्लिकेशन ओवरफ़्लो वैलिडेटर"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"कार की स्थिति को पार्क से ड्राइविंग में बदलते समय"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"दिए गए टेंप्लेट आज़माएं"</string>
<string name="perm_group" msgid="3834918337351876270">"अनुमतियों का ग्रुप"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase ऐप्लिकेशन को दी गई अनुमतियों का ग्रुप"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"जगह की सटीक जानकारी का ऐक्सेस"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml
index 7b12f9d..dbe9520 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Dosegnuto ograničenje zadatka\nAko nastavite, aplikacija će se prisilno zaustaviti"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Korak zadatka: %1$d od %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Kliknite da biste nastavili"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Posjetite različite predloške i potvrdite da je automobil u načinu za vožnju"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Pokazna verzija prekidača"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Prebaci test"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Dopuštene su promjene u praćenju stanja"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Pokazna verzija zaslona za Glasovni pristup"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Korisničke interakcije"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Zatraži pokazne verzije dopuštenja"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validator prelijevanja aplikacije"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testirajte sljedeće predloške tijekom promjene"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"stanja vozila iz parkiranog u vožnju"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupa dopuštenja"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupa dopuštenja za aplikaciju Odabir urednika"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Pristup preciznoj lokaciji"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml
index cd2c9c8..919d60a 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Feladatkorlát elérve\nA továbblépés az app bezárását kényszeríti"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%2$d/%1$d. lépés a feladatban"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Kattintson a továbblépéshez"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Keresse fel a különböző sablonokat, és ellenőrizze, hogy az autó vezetési módban van-e"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Váltógomb bemutatója"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Kapcsolóteszt"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Az állapotmegőrző módosítások engedélyezettek"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Frissítések engedélyezve a visszalépési műveleteknél."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Kapcsolóteszt engedélyezve"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Kapcsolóteszt letiltva"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Másodlagos művelet és dekorációs demó"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Másodlagos művelet tesztelése"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Csak a másodlagos művelet választható ki"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Dekoráció tesztelése"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Másodlagos műveletek és dekorációk"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"A sor is kiválasztható"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Másodlagos művelet kiválasztva"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Sor elsődleges művelete kiválasztva"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Egyéb sablonok – demók"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Kirakat – demók"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Sablonelrendezések bemutatói"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Hangalapú hozzáférés képernyő – demó"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Felhasználói interakciók"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Engedélyek kérése – demók"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"További alkalmazáselemeket tartalmazó menü ellenőrzője"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Tesztelje a következő sablonokat, miközben"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"a járművel parkolásból vezetés állapotba vált"</string>
<string name="perm_group" msgid="3834918337351876270">"Engedélycsoport"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Engedélycsoport a bemutató alkalmazáshoz"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Pontos helymeghatározáshoz való hozzáférés"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml
index 3c3fc70..375942e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Առաջադրանքների սահմանաչափը սպառված է։\nԵթե շարունակեք, հավելվածի աշխատանքը կկանգնեցվի։"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Առաջադրանքի քայլ %1$d՝ %2$d-ից"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Սեղմեք՝ առաջ անցնելու համար"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Դիտեք տարբեր ձևանմուշներ և համոզվեք, որ մեքենան վարելու ռեժիմում է"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Փոխանջատիչի դեմո"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Միացնել/անջատել ստուգումը"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Կարգավիճակի փոփոխությունները թույլատրված են"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Թարմացումները թույլատրված են՝ ֆոնային ռեժիմում գործողություններ կատարելու համար։"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Փոխանջատիչի փորձարկումը միացված է"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Փոխանջատիչի փորձարկումն անջատված է"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Երկրորդական գործողության և ձևավորման դեմո"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Երկրորդական գործողության թեստ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Կարելի է ընտրել միայն երկրորդական գործողությունը"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Ձևավորման թեստ"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Երկրորդական գործողություններ և ձևավորում"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Շարքը ևս կարելի է ընտրել"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Ընտրված է շարքի երկրորդական գործողությունը"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Ընտրված է շարքի գլխավոր գործողությունը"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Այլ ձևանմուշների դեմոներ"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Ցուցադրել դեմոները"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Ձևանմուշի դասավորության դեմոներ"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access-ի դեմոյի էկրան"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Օգտատիրոջ փոխազդումներ"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Թույլտվության հարցումների դեմոներ"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Հավելվածի լրացուցիչ ընտրացանկի ստուգում"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Փորձարկեք հետևյալ ձևանմուշները, երբ փոխում եք"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"մեքենայի ռեժիմը կայանվածից վարելու ռեժիմի"</string>
<string name="perm_group" msgid="3834918337351876270">"Թույլտվությունների խումբ"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase հավելվածի թույլտվությունների խումբ"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Ճշգրիտ տեղադրության հասանելիություն"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml
index c5708cf..4735ecd 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Batas tugas tercapai\nJika dilanjutkan, aplikasi akan dipaksa berhenti"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Langkah tugas %1$d dari %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Klik untuk melanjutkan"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Buka template lain dan pastikan mobil dalam mode mengemudi"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo Tombol"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Pengujian tombol"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Perubahan stateful diizinkan"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Update diizinkan di halaman sebelumnya."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Pengujian Tombol Diaktifkan"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Pengujian Tombol Dinonaktifkan"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo Tindakan Sekunder dan Dekorasi"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Pengujian Tindakan Sekunder"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Hanya tindakan sekunder yang dapat dipilih"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Pengujian Dekorasi"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Tindakan Sekunder dan Dekorasi"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Baris juga dapat dipilih"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Tindakan Sekunder dipilih"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Tindakan utama baris dipilih"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demo Template Lain-Lain"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demo Berita Pilihan"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demo Tata Letak Template"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Layar Demo Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interaksi Pengguna"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demo Permintaan Izin"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validator Overflow Aplikasi"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Uji template berikut saat mengubah"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"status kendaraan dari parkir ke mengemudi"</string>
<string name="perm_group" msgid="3834918337351876270">"Grup Izin"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grup Izin untuk Aplikasi Etalase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Akses ke Lokasi Terperinci"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml
index e81894d..5ffd5fb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Verkefnismörkum náð\nEf haldið er áfram verður lokun forrits þvinguð"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Verkefnisskref %1$d af %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Smelltu til að halda áfram"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Opnaðu mismunandi sniðmát og gakktu úr skugga um að bíllinn sé í akstursstillingu"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Sýnishorn hnapps til að slökkva og kveikja"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Velja/afvelja prófun"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Breytingar á stöðu eru leyfðar"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Uppfærslur leyfðar á fyrri aðgerðum."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Kveikt á rofaprófun"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Slökkt á rofaprófun"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Aukaaðgerð og prufuskreyting"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Prufa aukaaðgerðar"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Aðeins er hægt að velja aukaaðgerð"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Prufuskreyting"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Aukaaðgerðir og skreyting"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Einnig er hægt að velja línuna"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Aukaaðgerð er valin"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Aðalaðgerð línu er valin"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Sýnishorn ýmissa sniðmáta"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Prufuútgáfur Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Sýnishorn sniðmátsuppsetningar"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Prufuskjár fyrir raddskipanir"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Aðgerðir notenda"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Sýnishorn heimildabeiðna"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Notkunarprófun á yfirflæði forrits"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Prófaðu eftirfarandi sniðmát á meðan skipt er á milli þess að hafa"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ökutækið kyrrstætt eða akandi"</string>
<string name="perm_group" msgid="3834918337351876270">"Heimildahópur"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Heimildahópur fyrir Showcase forritið"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Aðgangur að nákvæmri staðsetningu"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml
index 09c13bf..c3a7bdf 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite attività raggiunto\nSe prosegui forzerai l\'interruzione dell\'app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Passaggio attività %1$d di %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Fai clic per proseguire"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Controlla i diversi modelli e assicurati che l\'auto sia in modalità Auto"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo Pulsante di attivazione/disattivazione"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Test attivazione/disattivazione"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Sono consentite modifiche stateful"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Aggiornamenti consentiti per le operazioni in background."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Test di attivazione/disattivazione abilitato"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Test di attivazione/disattivazione disabilitato"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo su azione secondaria e decorazione"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test azione secondaria"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Solo l\'azione secondaria può essere selezionata"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Test decorazione"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Azioni secondarie e decorazione"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Anche la riga può essere selezionata"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"È stata selezionata l\'azione secondaria"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"È stata selezionata l\'azione principale della riga"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demo modelli vari"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demo Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demo Layout modello"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Schermata demo Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interazioni dell\'utente"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demo su come richiedere le autorizzazioni"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Strumento di convalida del menu Extra dell\'applicazione"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testa i seguenti modelli mentre lo stato"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"del veicolo passa da \"Parcheggiato\" a \"Alla guida\""</string>
<string name="perm_group" msgid="3834918337351876270">"Gruppo di autorizzazioni"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Gruppo di autorizzazioni relativo all\'app Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Accesso alla posizione precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml
index 7fc4821..f5bc7e7 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"הגעת להגבלת המשימות\nהמשך יוביל לסגירה ידנית של האפליקציה"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"שלב %1$d מתוך %2$d במשימה"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"יש ללחוץ כדי להמשיך"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"יש לבדוק את התבניות השונות ולוודא שהמכונית במצב נהיגה"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"הדגמה של מתג להחלפת מצב"</string>
<string name="toggle_test_title" msgid="924485265152862631">"בדיקה של החלפת המצב"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"מותר לערוך שינויי מצב"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"מסך ההדגמה של אפליקציית Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"אינטראקציות של משתמשים"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"הדגמה של בקשת הרשאות"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"כלי התיקוף לגבי גלישה באפליקציה"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"יש לבדוק את התבניות הבאות במהלך שינוי"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"מצב הרכב מ\'חניה\' ל\'נהיגה\'"</string>
<string name="perm_group" msgid="3834918337351876270">"קבוצת הרשאות"</string>
<string name="perm_group_description" msgid="7348847631139139024">"קבוצת הרשאות לאפליקציית Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"גישה למיקום המדויק"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml
index 772f3a6..e697085 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"タスクの上限に達しました\n続行するとアプリが強制停止されます"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"タスクのステップ %1$d / %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"クリックして続行"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"別のテンプレートにアクセスして自動車が運転モードであることを確認してください"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"切り替えボタンのデモ"</string>
<string name="toggle_test_title" msgid="924485265152862631">"切り替えのテスト"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"ステートフルな変更が許可されています"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"アップデートにより「戻る」操作ができるようになりました。"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"切り替えのテスト有効"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"切り替えのテスト無効"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"セカンダリ アクションとデコレーションのデモ"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"セカンダリ アクション テスト"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"セカンダリ アクションのみ選択できます"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"デコレーション テスト"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"セカンダリ アクションとデコレーション"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"行も選択できます"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"セカンダリ アクションを選択しました"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"行のプライマリ アクションを選択しました"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"その他のテンプレートのデモ"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"デモを表示"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"テンプレート レイアウトのデモ"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access のデモ画面"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"ユーザー操作"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"権限リクエストのデモ"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"アプリ オーバーフロー バリデータ"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"変更を行う際に以下のテンプレートをテストしてください"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"車両の状態が駐車中から運転状態に変わりました"</string>
<string name="perm_group" msgid="3834918337351876270">"権限グループ"</string>
<string name="perm_group_description" msgid="7348847631139139024">"ショーケース アプリの権限グループ"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"精細な位置情報へのアクセス"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml
index 0db9ab0..3a78f70 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"მიღწეულია ამოცანების ლიმიტი\nგაგრძელება გამოიწვევს აპის ძალით შეჩერებას"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"ამოცანის ეტაპი: %1$d/%2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"დააწკაპუნეთ გასაგრძელებლად"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"გთხოვთ, ეწვიოთ სხვადასხვა შაბლონებს, რათა დარწმუნდეთ, რომ მანქანა საავტომობილო რეჟიმშია"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"გადართვა ღილაკის დემო"</string>
<string name="toggle_test_title" msgid="924485265152862631">"გადამრთველის ტესტი"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"სტატუსის ცვლილებები ნებადართულია"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"განახლებებმა დაუშვა უკან დაბრუნება."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"გადამრთველის ტესტი ჩართულია"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"გადამრთველის ტესტი გათიშულია"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"მეორადი მოქმედების და დეკორაციის დემო"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"მეორადი მოქმედების ტესტი"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"შესაძლებელია მხოლოდ მეორადი მოქმედების არჩევა"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"დეკორაციის ტესტი"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"მეორადი მოქმედებები და დეკორაცია"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ასევე შეიძლება მწკრივის არჩევა"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"არჩეულია მეორადი მოქმედება"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"არჩეულია მწკრივის პირველადი მოქმედება"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"სხვადასხვა შაბლონური დემოები"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"დემოების ჩვენება"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"შაბლონის განლაგების დემო"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"ხმის წვდომის დემო ეკრანი"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"მომხმარებლის ინტერაქციები"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"მოითხოვეთ ნებართვების დემო"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"აპლიკაციების გადატვირთვის ვალიდატორი"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"გთხოვთ, შეამოწმოთ შემდეგი შაბლონები ცვლილებისას"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ავტომობილი პარკირებულიდან მართვის მდგომარეობამდე"</string>
<string name="perm_group" msgid="3834918337351876270">"ნებართვის ჯგუფი"</string>
<string name="perm_group_description" msgid="7348847631139139024">"ნებართვის ჯგუფი Showcase აპისთვის"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"წვდომა კარგ მდებარეობაზე"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml
index fbec90e..fe5649b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Тапсырма өз шегіне жетті.\nІлгері жүрсеңіз, қолданба күштеп тоқтатылады."</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%1$d/%2$d тапсырма қадамы"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Ілгері жүру үшін басыңыз."</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Әртүрлі үлгілерді қарап, автокөліктің жүргізу режимінде тұрғанына көз жеткізіңіз."</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Ауыстыру түймесі (демо нұсқасы)"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Ауыстырғыш сынағы"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Күй бақыланатын өзгерістерге рұқсат берілген."</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"\"Артқа\" операциясында өзгертуге рұқсат етілген"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Ауыстырғыш сынағы қосылған."</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Ауыстырғыш сынағы өшірілген."</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Қосымша әрекеттің және безендірудің демо нұсқасы"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Қосымша әрекетті сынау"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Қосымша әрекет қана таңдалады."</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Безендіруді сынау"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Қосымша әрекеттер және безендіру"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Жолды да таңдауға болады."</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Қосымша әрекет таңдалды."</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Жол үшін негізгі әрекет таңдалды."</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"\"Басқалары\" үлгісінің демо нұсқасы"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Презентацияның демо нұсқасы"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Формат үлгісі (демо нұсқалары)"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access демо нұсқасының экраны"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Пайдаланушы әрекеттері"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Рұқсат сұрау операциясының демо нұсқасы"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Қолданбаның қосымша мәзірін тексеру құралы"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Көлікті тұраққа қойылған күйден жүргізу күйіне өзгерту кезінде"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"келесі үлгілерді тексеріп көріңіз."</string>
<string name="perm_group" msgid="3834918337351876270">"Рұқсат тобы"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase қолданбасының рұқсат тобы"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Нақты локацияны пайдалану"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml
index 99666c01..afb1fc5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"បានឈានដល់ដែនកំណត់កិច្ចការហើយ\nការបន្តទៀតនឹងបង្ខំឱ្យកម្មវិធីបញ្ឈប់"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"ជំហានកិច្ចការទី %1$d នៃ %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ចុចដើម្បីបន្ត"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"សូមចូលមើលទម្រង់គំរូផ្សេងៗ និងធ្វើឱ្យប្រាកដថា រថយន្តស្ថិតក្នុងមុខងារបើកបរ"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"គំរូបង្ហាញនៃប៊ូតុងបិទ/បើក"</string>
<string name="toggle_test_title" msgid="924485265152862631">"ការធ្វើតេស្តប៊ូតុងបិទ/បើក"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"ការផ្លាស់ប្ដូរតាមស្ថានភាពត្រូវបានអនុញ្ញាត"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"បានអនុញ្ញាតការដំឡើងកំណែនៅលើប្រតិបត្តិការថយក្រោយ។"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"បានបើកការធ្វើតេស្តប៊ូតុងបិទ/បើក"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"បានបិទការធ្វើតេស្តប៊ូតុងបិទ/បើក"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"គំរូបង្ហាញការតុបតែង និងសកម្មភាពបន្ទាប់បន្សំ"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"តេស្តសកម្មភាពបន្ទាប់បន្សំ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"អាចជ្រើសរើសបានតែសកម្មភាពបន្ទាប់បន្សំប៉ុណ្ណោះ"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"តេស្តនៃការតុបតែង"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ការតុបតែង និងសកម្មភាពបន្ទាប់បន្សំ"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ជួរដេកក៏អាចត្រូវបានជ្រើសរើសផងដែរ"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"សកម្មភាពបន្ទាប់បន្សំត្រូវបានជ្រើសរើស"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"សកម្មភាពចម្បងជួរដេកត្រូវបានជ្រើសរើស"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"គំរូបង្ហាញនៃទម្រង់គំរូផ្សេងៗ"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"គំរូបង្ហាញអំពីការតាំងរំលេច"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"គំរូបង្ហាញនៃប្លង់ទម្រង់គំរូ"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"អេក្រង់គំរូបង្ហាញនៃការចូលប្រើប្រាស់តាមរយៈសំឡេង"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"អន្តរកម្មរបស់អ្នកប្រើប្រាស់"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"គំរូបង្ហាញនៃការស្នើសុំការអនុញ្ញាត"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ឧបករណ៍បញ្ជាក់ភាពលើសចំណុះកម្មវិធី"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"សូមធ្វើតេស្តទម្រង់គំរូដូចខាងក្រោម ពេលផ្លាស់ប្ដូរ"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"យានជំនិះពីស្ថានភាពចតទៅជាស្ថានភាពបើកបរ"</string>
<string name="perm_group" msgid="3834918337351876270">"ក្រុមអនុញ្ញាត"</string>
<string name="perm_group_description" msgid="7348847631139139024">"ក្រុមអនុញ្ញាតសម្រាប់កម្មវិធី Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"សិទ្ធិចូលប្រើទីតាំងជាក់លាក់"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml
index c85df6b..196f9f0 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"ಕಾರ್ಯದ ಮಿತಿಯನ್ನು ತಲುಪಿದ್ದೀರಿ\nಮುಂದುವರಿಸಿದರೆ ಆ್ಯಪ್ ಅನ್ನು ಬಲವಂತವಾಗಿ ನಿಲ್ಲಿಸಲಾಗುತ್ತದೆ"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"ಕಾರ್ಯದ ಹಂತ %1$d of %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ಮುಂದೆ ಹೋಗಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"ವಿವಿಧ ಟೆಂಪ್ಲೇಟ್ಗಳಿಗೆ ಭೇಟಿ ನೀಡಿ ಮತ್ತು ಕಾರು, ಡ್ರೈವಿಂಗ್ ಮೋಡ್ನಲ್ಲಿರುವುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"ಟಾಗಲ್ ಬಟನ್ ಡೆಮೋ"</string>
<string name="toggle_test_title" msgid="924485265152862631">"ಟಾಗಲ್ ಟೆಸ್ಟ್"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"ಪುನಃ ಸ್ಥಾಪಿಸಬಹುದಾದ ಬದಲಾವಣೆಗಳನ್ನು ಅನುಮತಿಸಲಾಗಿದೆ"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"ಹಿನ್ನೆಲೆ ಕಾರ್ಯಾಚರಣೆಗಳಿಗಾಗಿ ಅಪ್ಡೇಟ್ಗಳನ್ನು ಅನುಮತಿಸಲಾಗಿದೆ."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"ಟಾಗಲ್ ಟೆಸ್ಟ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"ಟಾಗಲ್ ಟೆಸ್ಟ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆ ಮತ್ತು ಅಲಂಕಾರ ಡೆಮೊ"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆಯ ಪರೀಕ್ಷೆ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆಯನ್ನು ಮಾತ್ರ ಆಯ್ಕೆ ಮಾಡಬಹುದು"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"ಅಲಂಕಾರ ಪರೀಕ್ಷೆ"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆಗಳು ಮತ್ತು ಅಲಂಕಾರ"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ಸಾಲನ್ನು ಸಹ ಆಯ್ಕೆ ಮಾಡಬಹುದು"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"ದ್ವಿತೀಯಕ ಕ್ರಿಯೆಯನ್ನು ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"ಸಾಲಿನ ಪ್ರಾಥಮಿಕ ಕ್ರಿಯೆಯನ್ನು ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"ಇತರ ಟೆಂಪ್ಲೇಟ್ಗಳ ಡೆಮೋಗಳು"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Showcase ಡೆಮೋಗಳು"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"ಟೆಂಪ್ಲೇಟ್ ಲೇಔಟ್ ಡೆಮೋಗಳು"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"ಧ್ವನಿ ಆ್ಯಕ್ಸೆಸ್ ಡೆಮೋ ಸ್ಕ್ರೀನ್"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"ಬಳಕೆದಾರರ ಸಂವಹನಗಳು"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ಅನುಮತಿಗಳ ಡೆಮೋಗಳನ್ನು ವಿನಂತಿಸಿ"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ಅಪ್ಲಿಕೇಶನ್ ಓವರ್ಫ್ಲೋ ಮೌಲ್ಯಮಾಪಕ"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"ಬದಲಾಯಿಸುವಾಗ ಕೆಳಗಿನ ಟೆಂಪ್ಲೇಟ್ಗಳನ್ನು ಪರೀಕ್ಷಿಸಿ"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ನಿಲುಗಡೆಯಿಂದ ಚಾಲನೆಯ ಸ್ಥಿತಿಗೆ ಬಂದ ವಾಹನ"</string>
<string name="perm_group" msgid="3834918337351876270">"ಅನುಮತಿ ಗುಂಪು"</string>
<string name="perm_group_description" msgid="7348847631139139024">"ಶೋಕೇಸ್ ಆ್ಯಪ್ನ ಅನುಮತಿ ಗುಂಪು"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"ನಿಖರವಾದ ಸ್ಥಳಕ್ಕೆ ಆ್ಯಕ್ಸೆಸ್"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml
index 9aa1ed8..20f9cf1 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"작업 제한에 도달함\n계속 진행하면 앱이 강제 종료됩니다"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"작업 단계 %1$d/%2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"클릭하여 진행"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"다른 템플릿을 방문하여 자동차가 운전 모드인지 확인하세요."</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"전환 버튼 데모"</string>
<string name="toggle_test_title" msgid="924485265152862631">"전환 테스트"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"스테이트풀(Stateful) 변경은 허용됨"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"뒤로 작업에서 업데이트가 허용되었습니다."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"전환 테스트 사용 설정됨"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"전환 테스트 사용 중지됨"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"보조 작업 및 장식 데모"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"보조 작업 테스트"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"보조 작업만 선택할 수 있습니다."</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"장식 테스트"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"보조 작업 및 장식"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"행도 선택할 수 있습니다."</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"보조 작업 선택됨"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"행 기본 작업 선택됨"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"기타 템플릿 데모"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"쇼케이스 데모"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"템플릿 레이아웃 데모"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"음성 액세스 데모 화면"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"사용자 상호작용"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"권한 요청 데모"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"애플리케이션 오버플로 검사기"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"변경하는 동안 다음 템플릿을 테스트하세요."</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"차량을 주차 상태에서 운전 상태로"</string>
<string name="perm_group" msgid="3834918337351876270">"권한 그룹"</string>
<string name="perm_group_description" msgid="7348847631139139024">"쇼케이스 앱에 대한 권한 그룹"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"정확한 위치 액세스"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
index 486cba8..4c8ad5c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Тапшырманын чегине жетти\nАлдыга жылсаңыз, колдонмо мажбурлап токтотулат"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Тапшырма кадамы %1$d of %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Алдыга өтүү үчүн чыкылдатыңыз"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Башка үлгүлөргө өтүп, унаа айдоо режиминде экенин текшериңиз"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Күйгүзүү/өчүрүү баскычынын демосу"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Өчүрүү/күйгүзүү сыноосу"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Абалы сакталган өзгөртүүлөргө уруксат берилген"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Артка операцияларында жаңыртууларга уруксат берет."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Өчүрүү/күйгүзүү сыноосу иштетилди"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Өчүрүү/күйгүзүү сыноосу өчүрүлдү"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Кошумча аракет жана Жасалгалоо демосу"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Кошумча аракетти сыноо"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Кошумча аракетти гана тандоого болот"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Жасалгалоону сыноо"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Кошумча аракеттер жана Жасалгалоо"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Сапты да тандоого болот"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Кошумча аракет тандалды"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Негизги аракет сабы тандалды"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Башка үлгүлөрдүн демолору"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Showcase демолору"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Үлгү калыптарынын демолору"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access демо экраны"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Колдонуучунун мамилелери"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демо версияларга уруксат суроо"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Ашыкча колдонмолорду текшергич"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Өзгөртүп жатканда төмөнкү үлгүлөрдү сынап көрүңүз:"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"токтоп турган унааны айдоо абалы"</string>
<string name="perm_group" msgid="3834918337351876270">"Уруксаттар тобу"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Колдонмону көрсөтүү үчүн уруксаттар тобу"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Так жайгашкан жерди көрүү"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml
index c1ad5d5..1b5d9ec 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"ຮອດຂີດຈຳກັດໜ້າວຽກແລ້ວ\nຕໍ່ໄປຈະບັງຄັບຢຸດແອັບ"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"ຂັ້ນຕອນໜ້າວຽກທີ %1$d ຈາກທັງໝົດ %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ຄລິກເພື່ອໄປໜ້າ"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"ກະລຸນາເຂົ້າໄປແມ່ແບບອື່ນ ແລະ ກວດໃຫ້ແນ່ໃຈວ່າລົດຢູ່ໃນໂໝດຂັບຂີ່"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"ເດໂມປຸ່ມເປີດປິດ"</string>
<string name="toggle_test_title" msgid="924485265152862631">"ສະຫຼັບການທົດສອບ"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"ອະນຸຍາດໃຫ້ມີການປ່ຽນແປງສະຖານະໄດ້"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"ອະນຸຍາດໃຫ້ອັບເດດໃນການດຳເນີນການເບື້ອງຫຼັງ."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"ເປີດການນຳໃຊ້ການທົດສອບການສະຫຼັບ"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"ປິດການນຳໃຊ້ການທົດສອບການສະຫຼັບ"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ການສາທິດຄຳສັ່ງສຳຮອງ ແລະ ການຕົກແຕ່ງ"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"ການທົດສອບຄຳສັ່ງສຳຮອງ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ສາມາດເລືອກໄດ້ສະເພາະຄຳສັ່ງສຳຮອງເທົ່ານັ້ນ"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"ການທົດສອບການຕົກແຕ່ງ"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ຄຳສັ່ງສຳຮອງ ແລະ ການຕົກແຕ່ງ"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ນອກຈາກນັ້ນຍັງເລືອກແຖວໄດ້ນຳ"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"ເລືອກຄຳສັ່ງສຳຮອງຂອງແຖວແລ້ວ"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"ເລືອກຄຳສັ່ງຫຼັກຂອງແຖວແລ້ວ"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"ເດໂມແມ່ແບບອື່ນໆ"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"ເດໂມ Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"ເດໂມໂຄງຮ່າງແມ່ແບບ"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"ໜ້າຈໍເດໂມການເຂົ້າເຖິງສຽງ"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"ການໂຕ້ຕອບຂອງຜູ້ໃຊ້"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ຮ້ອງຂໍການອະນຸຍາດເດໂມ"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ໂປຣແກຣມກວດສອບລາຍການເພີ່ມເຕີມຂອງແອັບພລິເຄຊັນ"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"ກະລຸນາທົດສອບແມ່ແບບຕໍ່ໄປນີ້ໃນຂະນະທີ່ປ່ຽນແປງ"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ສະຖານະຂອງພາຫະນະຈາກຈອດຢູ່ເປັນກຳລັງຂັບຂີ່"</string>
<string name="perm_group" msgid="3834918337351876270">"ກຸ່ມການອະນຸຍາດ"</string>
<string name="perm_group_description" msgid="7348847631139139024">"ກຸ່ມການອະນຸຍາດສຳລັບແອັບ Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"ສິດເຂົ້າເຖິງສະຖານທີ່ແບບລະອຽດ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml
index ac2a584..31e4669 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Pasiektas užduočių apribojimas\nTęsiant programa bus priverstinai sustabdyta"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%1$d užduoties veiksmas iš %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Spustelėkite, jei norite tęsti"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Peržiūrėkite kitus šablonus ir įsitikinkite, kad automobilis veikia vairavimo režimu"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Perjungimo mygtuko demonstracinė versija"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Perjungti bandymą"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Leidžiami būsenos pakeitimai"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Naujiniai leidžiami ankstesnėms operacijoms."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Bandymo perjungimas įgalintas"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Bandymo perjungimas išjungtas"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Antrinio veiksmo ir dekoracijų demonstracinė versija"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Antrinio veiksmo bandymas"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Galima pasirinkti tik antrinį veiksmą"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Dekoracijų bandymas"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Antriniai veiksmai ir dekoracijos"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Taip pat galima pasirinkti eilutę"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Pasirinktas antrinis veiksmas"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Pasirinktas pagrindinis eilutės veiksmas"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Įvairių šablonų demonstracinės versijos"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Rodyti demonstracines versijas"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Šablonų išdėstymo demonstracinės versijos"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Prieigos balsu demonstracinės versijos ekranas"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Naudotojo sąveikos"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Leidimų prašymo demonstracinės versijos"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Programos perpildymo patvirtinimo priemonė"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Išbandykite toliau nurodytus šablonus įkraudami"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"transporto priemonė iš stovėjimo į važiavimo būseną"</string>
<string name="perm_group" msgid="3834918337351876270">"Leidimų grupė"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Demonstracinės programos leidimų grupė"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Prieiga prie tikslios vietovės"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml
index baa904c..6b4b746 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Sasniegts uzdevumu ierobežojums\nTurpinot lietotnes darbība tiks apturēta piespiedu kārtā."</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Uzdevuma darbība: %1$d. no %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Noklikšķiniet, lai dotos tālāk"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Lūdzu, skatiet dažādas veidnes un nodrošiniet, ka automašīna ir braukšanas režīmā."</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Pārslēgšanas pogas demonstrācija"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Pārslēgšanas tests"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Stāvokļa izmaiņas ir atļautas"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Pārejai atpakaļ ir atļauti atjauninājumi."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Pārslēgšanas tests ir iespējots"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Pārslēgšanas tests ir atspējots"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Sekundārās darbības un noformējuma demonstrācija"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Sekundārās darbības tests"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Atlasīt var tikai sekundāro darbību"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Noformējuma tests"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundārās darbības un noformējums"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Arī rindu var atlasīt"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Atlasīta sekundārā darbība"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Atlasīta rindas primārā darbība"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Dažādu veidņu demonstrācijas"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Showcase demonstrācijas"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Veidņu izkārtojumu demonstrācijas"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Balss piekļuves demonstrācijas ekrāns"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Lietotāju mijiedarbība"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demonstrācija: atļauju pieprasīšana"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Lietojumprogrammu pārpildes pārbaudes rīks"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Lūdzu, testējiet tālāk norādītās veidnes, kamēr maināt"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"transportlīdzekļa režīmu no apturēta uz braukšanas režīmu."</string>
<string name="perm_group" msgid="3834918337351876270">"Atļauju grupa"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Atļauju grupa demonstrācijas lietotnei"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Piekļuve precīzai atrašanās vietai"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
index 8bbd60e..4eab700 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Достигнато е ограничувањето за задачи\nАко продолжите, апликацијата ќе се исклучи присилно"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Преземете чекор %1$d од %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Кликнете за да одите нанапред"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Одете на различните шаблони и погрижете се автомобилот да е во режим на возење"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Демо на копче за вклучување/исклучување"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Тест за вклучување/исклучување"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Дозволени се промени на зачувувањето на состојбата"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Ажурирањата се дозволени при операции на враќање назад."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Овозможено тест за вклучување/исклучување"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Оневозможено тест за вклучување/исклучување"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демо за секундарно дејство и украсување"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Проба за секундарно дејство"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Само секундарното дејство може да се избере"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Проба за украсување"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Секундарни дејства и украсување"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Редот исто така може да се избере"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Избрано е секундарно дејство"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Избрано е примарно дејство на ред"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Разни демоа за шаблони"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Демоа за прикажување"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Демоа за распоред на шаблони"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Екран за демо за „Пристап со глас“"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Кориснички интеракции"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Побарајте дозволи за демо"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Алатка за потврдување прелевање на апликацијата"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Тестирајте ги следните шаблони додека менувате"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"возилото од паркирана во состојба на возење"</string>
<string name="perm_group" msgid="3834918337351876270">"Група дозволи"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Група дозволи за апликација за вести избрани од издавачи"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Пристап до точна локација"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml
index 0cfe4d8..1fe8d05 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"ടാസ്ക്ക് പരിധിയെത്തി\nമുന്നോട്ട് പോകുന്നത് ആപ്പ് നിർബന്ധിതമായി നിർത്താൻ കാരണമാകും"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%2$d ടാസ്ക്ക് ഘട്ടങ്ങളിൽ %1$d -ാമത്തേത്"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"മുന്നോട്ട് പോകാൻ ക്ലിക്ക് ചെയ്യുക"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"വ്യത്യസ്ത ടെംപ്ലേറ്റുകൾ സന്ദർശിച്ച്, ഡ്രൈവിംഗ് മോഡിലാണ് കാർ ഉള്ളതെന്ന് ഉറപ്പാക്കുക"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"ടോഗിൾ ബട്ടൺ ഡെമോ"</string>
<string name="toggle_test_title" msgid="924485265152862631">"ടെസ്റ്റ് മാറ്റുക"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"സ്റ്റേറ്റ്ഫുള്ളായ മാറ്റങ്ങൾ അനുവദനീയമാണ്"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access ഡെമോ സ്ക്രീൻ"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"ഉപയോക്തൃ ഇടപഴകലുകൾ"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"അനുമതി ഡെമോകൾ അഭ്യർത്ഥിക്കുക"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ആപ്ലിക്കേഷൻ ഓവർഫ്ലോ വാലിഡേറ്റർ"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"പാർക്കിംഗിൽ നിന്ന് ഡ്രൈവിംഗ് നിലയിലേക്ക് വാഹനം"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"മാറ്റുമ്പോൾ, ഇനിപ്പറയുന്ന ടെംപ്ലേറ്റുകൾ പരിശോധിക്കുക"</string>
<string name="perm_group" msgid="3834918337351876270">"അനുമതി ഗ്രൂപ്പ്"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase ആപ്പിനുള്ള അനുമതി ഗ്രൂപ്പ്"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"ഫൈൻ ലൊക്കേഷനിലേക്കുള്ള ആക്സസ്"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml
index faf65e2..512e67c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Ажлын хязгаарт хүрсэн\nҮргэлжлүүлснээр аппыг хүчээр зогсооно"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Ажлын %2$d-н %1$d-р алхам"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Үргэлжлүүлэхийн тулд товшино уу"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Өөр загварт зочилж, машин нь втомашин жолоодох горимд байгаа эсэхийг баталгаажуулна уу"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Асаах товчийн демо"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Асаах/Унтраах туршилт"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Төлөвтэй өөрчлөлтийг зөвшөөрнө"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Шинэчлэлтүүдийг арын үйл ажиллагаанд хийхийг зөвшөөрдөг."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Асаах/Унтраах туршилтыг идэвхжүүлсэн"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Асаах/Унтраах туршилтыг идэвхгүй болгосон"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Хоёрдогч үйлдэл ба чимэглэлийн демо"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Хоёрдогч үйлдлийн тест"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Зөвхөн хоёрдогч үйлдлийг сонгох боломжтой"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Чимэглэлийн туршилт"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Хоёрдогч үйлдлүүд ба чимэглэл"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Мөрийг мөн сонгож болно"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Хоёрдогч үйлдлийг сонгосон"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Мөрийн үндсэн үйлдлийг сонгосон"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Холимог загварын демо"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Showcase-н демо"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Загварын бүдүүвч бүхий демо"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access-н демо дэлгэц"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Хэрэглэгчийн харилцан үйлдлүүд"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Зөвшөөрлийн демогийн хүсэлт тавих"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Аппликэйшний илүү ашиглалтыг баталгаажуулагч"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Цэнэглэх үедээ дараах загварыг туршина уу"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"зогсоолд байснаас жолоодох төлөв рүү сэлгэсэн тээврийн хэрэгсэл"</string>
<string name="perm_group" msgid="3834918337351876270">"Зөвшөөрлийн бүлэг"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase аппын зөвшөөрлийн бүлэг"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Нарийвчилсан байршилд хандах"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml
index d95f041..36e204a 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"टास्कची मर्यादा गाठली आहे\nपुढे सुरू ठेवल्यास, अॅप सक्तीने थांबवले जाईल"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"टास्कची %1$d पैकी %2$d पायरी"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"पुढे जाण्यासाठी क्लिक करा"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"कृपया विविध टेंप्लेटना भेट द्या आणि कार ही ड्रायव्हिंग मोडमध्ये असल्याची खात्री करा"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"टॉगल बटण डेमो"</string>
<string name="toggle_test_title" msgid="924485265152862631">"टॉगल करण्याशी संबंधित चाचणी"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"स्थितीशी संबंधित बदलांना अनुमती आहे"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"बॅक ऑपरेशनवर अपडेटना अनुमती दिली आहे."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"टॉगल करण्याशी संबंधित चाचणी सुरू केली"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"टॉगल करण्याशी संबंधित चाचणी बंद केली"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"दुय्यम कृती आणि सजावट डेमो"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"दुय्यम कृती चाचणी"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"फक्त दुय्यम कृती निवडली जाऊ शकते"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"सजावट चाचणी"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"दुय्यम कृती आणि सजावट"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"पंक्तीदेखील निवडली जाऊ शकते"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"दुय्यम कृती निवडली आहे"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"पंक्तीशी संबंधित प्राथमिक कृती निवडली आहे"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"इतर टेंप्लेटचे डेमो"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"डेमो दाखवा"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"टेंप्लेट लेआउट डेमो"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access डेमो स्क्रीन"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"वापरकर्ता संवाद"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"परवानग्या डेमोची विनंती करा"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"अॅप्लिकेशन ओव्हरफ्लो व्हॅलिडेटर"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"बदलताना कृपया पुढील टेंप्लेटची चाचणी घ्या"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"कार पार्क करण्यापासून ते ड्रायव्हिंगच्या परिस्थितीपर्यंत"</string>
<string name="perm_group" msgid="3834918337351876270">"परवानगी गट"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase अॅपसाठी परवानगी गट"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"अचूक स्थान याचा अॅक्सेस"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml
index 1530d05..afd924c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Had tugasan dicapai\nMaju ke hadapan akan henti paksa apl"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Langkah tugasan %1$d daripada %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Klik untuk maju"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Sila lawati templat yang berbeza dan pastikan kereta berada dalam mod memandu"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo Butang Togol"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Togol ujian"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Perubahan keadaan dibenarkan"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Kemaskinian dibenarkan pada operasi belakang."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Ujian Togol Didayakan"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Ujian Togol Dilumpuhkan"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Tindakan Sekunder dan Demonstrasi Perhiasan"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Ujian Tindakan Sekunder"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Hanya tindakan sekunder boleh dipilih"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Ujian Perhiasan"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Tindakan Sekunder dan Perhiasan"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Baris ini juga boleh dipilih"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Tindakan Sekunder dipilih"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Tindakan utama baris dipilih"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Pelbagai Demo Templat"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demo Wadah"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demo Reka Letak Templat"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Skrin Demo Akses Suara"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interaksi Pengguna"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demo Minta Kebenaran"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Pengesah Limpahan Aplikasi"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Sila uji templat yang berikut semasa membuat penukaran"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"kenderaan daripada keadaan diletakkan kepada memandu"</string>
<string name="perm_group" msgid="3834918337351876270">"Kumpulan Kebenaran"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Kumpulan Kebenaran untuk Apl Wadah"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Akses kepada Lokasi Halus"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml
index da967bc0c..f005fa7 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"လုပ်ဆောင်စရာကန့်သတ်ချက် ရောက်သွားပြီ\nရှေ့ဆက်သွားခြင်းက အက်ပ်ကို မဖြစ်မနေရပ်ခိုင်းမည်"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"လုပ်ဆောင်စရာအဆင့် %2$d ခုအနက် %1$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ရှေ့ဆက်သွားရန် နှိပ်ပါ"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"ပုံစံအမျိုးမျိုးကို ဝင်ကြည့်ပြီး ကားသည် မောင်းနှင်မုဒ်တွင်ရှိကြောင်း သေချာပါစေ"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"ဖွင့်ပိတ် ခလုတ် သရုပ်ပြချက်"</string>
<string name="toggle_test_title" msgid="924485265152862631">"စစ်ဆေးမှု ပြောင်းခြင်း"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"အနေအထားအပြည့် အပြောင်းအလဲများ ခွင့်ပြုထားသည်"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"ပြန်ရွှေ့ခြင်းများတွင် အပ်ဒိတ်များကို ခွင့်ပြုသည်။"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"စမ်းသပ်ခလုတ် ဖွင့်ထားသည်"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"စမ်းသပ်ခလုတ် ပိတ်ထားသည်"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ဒုတိယဦးစားပေး လုပ်ဆောင်ချက်နှင့် တန်ဆာဆင်မှု သရုပ်ပြခြင်း"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"ဒုတိယဦးစားပေးလုပ်ဆောင်ချက် စမ်းသပ်ခြင်း"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ဒုတိယဦးစားပေးလုပ်ဆောင်ချက်ကိုသာ ရွေးနိုင်သည်"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"တန်ဆာဆင်မှု စမ်းသပ်ခြင်း"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ဒုတိယဦးစားပေးလုပ်ဆောင်ချက်များနှင့် တန်ဆာဆင်ခြင်း"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"အတန်းကိုလည်း ရွေးနိုင်သည်"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"ဒုတိယဦးစားပေး လုပ်ဆောင်ချက်ကို ရွေးထားသည်"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"အတန်း၏ ပထမဦးစားပေး လုပ်ဆောင်ချက်ကို ရွေးထားသည်"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"အထွေထွေ ပုံစံသရုပ်ပြချက်များ"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Showcase သရုပ်ပြချက်များ"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"နမူနာပုံစံ သရုပ်ပြချက်များ"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access သရုပ်ပြချက်စခရင်"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"အသုံးပြုသူ ပြန်လှန်တုံ့ပြန်မှုများ"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ခွင့်ပြုချက်များ တောင်းဆိုရန် သရုပ်ပြချက်"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"အပလီကေးရှင်းအပို စိစစ်မှုစနစ်"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"အားသွင်းစဉ်အတွင်း အောက်ပါပုံစံများကို စမ်းသပ်ပါ"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ယာဉ်ရပ်နားသည်မှ မောင်းနှင်သည့် အခြေအနေသို့"</string>
<string name="perm_group" msgid="3834918337351876270">"ခွင့်ပြုချက်အုပ်စု"</string>
<string name="perm_group_description" msgid="7348847631139139024">"‘စင်မြင့် အက်ပ်’ အတွက် ခွင့်ပြုချက်အုပ်စု"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"‘တည်နေရာအချော’ သုံးခွင့်"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml
index 44e9320..8936507 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Oppgavegrensen er nådd\nHvis du fortsetter, blir appen tvunget til å avslutte"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Oppgavetrinn %1$d av %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Klikk for å fortsette"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Gå til de ulike malene, og forsikre deg om at bilen er i kjøremodus"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo av av/på-knapp"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Test av/på-knapp"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Dynamiske endringer er tillatt"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Oppdateringer tillates ved tilbakeoperasjoner."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Test av av/på-knapp er aktivert"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Test av av/på-knapp er deaktivert"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo av sekundærhandling og dekorasjon"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test av sekundærhandling"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Bare sekundærhandlingen kan velges"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Dekorasjonstest"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundærhandling og dekorasjon"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Raden kan også velges"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Sekundærhandlingen er valgt"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Hovedhandlingen til raden er valgt"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demoer av diverse maler"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demoer i fokus"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demoer av mallayout"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Demoskjerm for Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Brukerinteraksjoner"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demoer for forespørsler om tillatelser"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Valideringsverktøy for appoverflyt"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Test disse malene når du bytter"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"fra parkerings- til kjøretilstand for kjøretøyet"</string>
<string name="perm_group" msgid="3834918337351876270">"Tillatelsesgruppe"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Tillatelsesgruppe for Showcase-appen"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Tilgang til nøyaktig posisjon"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml
index 19fcf1e..82567d5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"कार्यको सीमा सकियो\nतपाईं अगाडि बढ्नुभयो भने एप जबरजस्ती रोकिने छ"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%2$d कार्यको %1$d औँ चरण"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"अगाडि बढ्न क्लिक गर्नुहोस्"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"कृपया फरक-फरक टेम्प्लेट प्रयोग गर्नुहोस् र कार ड्राइभिङ मोडमा छ भन्ने कुरा सुनिश्चित गर्नुहोस्"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"टगल गर्ने बटनको डेमो"</string>
<string name="toggle_test_title" msgid="924485265152862631">"टगल टेस्ट"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"स्टेटफुल परिवर्तन गर्ने अनुमति छ"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"अघिल्लो पेजमा गई परिवर्तन गर्न सकिन्छ।"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"टगल टेस्ट अन गरिएको छ"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"टगल टेस्ट अफ गरिएको छ"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"सेकेन्डरी एक्सन तथा डेकोरेसन डेमो"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"सेकेन्डरी एक्सन टेस्ट"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"सेकेन्डरी एक्सन मात्र चयन गर्न सकिन्छ"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"डेकोरेसन टेस्ट"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"सेकेन्डरी एक्सन तथा डेकोरेसन"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"रो पनि चयन गर्न सकिन्छ"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"सेकेन्डरी एक्सन चयन गरिएको छ"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"रोको प्राइमरी एक्सन चयन गरिएको छ"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"टेम्प्लेटका विविध डेमोहरू"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"सोकेसहरूको डेमो"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"टेम्प्लेट लेआउटका डेमोहरू"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access को डेमो स्क्रिन"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"प्रयोगकर्ताले गरेका अन्तर्क्रिया"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"अनुमतिहरूका डेमोहरू माग्नुहोस्"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ऐप्लिकेसन ओभरफ्लो भ्यालिडेटर"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"कृपया SIM बदलक्ने बेलामा निम्न टेम्प्लेटहरूको परिक्षण गर्नुहोस्"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"पार्क गरिएको अवस्थाबाट चलाउने अवस्थामा लगिएको कार"</string>
<string name="perm_group" msgid="3834918337351876270">"अनुमतिको समूह"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase एपलाई दिइएको अनुमतिको समूह"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"सटीक लोकेसन प्रयोग गर्ने अनुमति"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml
index 076d9d2..41c1879 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Taaklimiet bereikt\nAls je doorgaat, wordt de app geforceerd gestopt"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Taakstap %1$d van %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Klik om verder te gaan"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Open de verschillende templates en zorg dat de auto in de rijstand staat"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo voor schakelaar"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Test aan-/uitzetten"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Stateful wijzigingen zijn toegestaan"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Updates toegestaan bij teruggaan in de weergavestapel."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Schakeltest aangezet"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Schakeltest uitgezet"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo voor secundaire actie en decoratie"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test voor secundaire actie"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Alleen de secundaire actie kan worden geselecteerd"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Test voor decoratie"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Secundaire acties en decoratie"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"De rij kan ook worden geselecteerd"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Secundaire actie is geselecteerd"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Rij voor primaire actie is geselecteerd"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Diverse templatedemo\'s"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demo\'s laten zien"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demo\'s voor opmaaktemplate"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Demoscherm voor Spraaktoegang"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Gebruikersinteracties"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Rechtendemo\'s aanvragen"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Application Overflow Validator"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Test de volgende templates terwijl je het voertuig"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"van de parkeerstand in de rijstand zet"</string>
<string name="perm_group" msgid="3834918337351876270">"Rechtengroep"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Rechtengroep voor Showcase-app"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Toegang tot exacte locatie"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml
index 93bc94e..4a7ea67 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"ଟାସ୍କର ସୀମାରେ ପହଞ୍ଚିଯାଇଛି\nଆଗକୁ ଗଲେ ଆପଟି ବାଧ୍ୟତାର ସହ ବନ୍ଦ ହୋଇଯିବ"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%2$dଟିରୁ %1$d ନମ୍ବର ଟାସ୍କର ଷ୍ଟେପ"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ଆଗକୁ ଯିବା ପାଇଁ କ୍ଲିକ କରନ୍ତୁ"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"ଦୟାକରି ବିଭିନ୍ନ ଟେମ୍ପଲେଟକୁ ଭିଜିଟ କରନ୍ତୁ ଏବଂ କାରଟି ଡ୍ରାଇଭିଂ ମୋଡରେ ଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"ଟୋଗଲ ବଟନର ଡେମୋ"</string>
<string name="toggle_test_title" msgid="924485265152862631">"ପରୀକ୍ଷା ଟୋଗଲ କରନ୍ତୁ"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"ଷ୍ଟେଟଫୁଲ ପରିବର୍ତ୍ତନଗୁଡ଼ିକୁ ଅନୁମତି ଦିଆଯାଇଛି"</string>
@@ -307,35 +306,24 @@
<string name="additional_data_text" msgid="2846223398214158872">"ପୃଷ୍ଠପଟର ଅପରେସନଗୁଡ଼ିକରେ ଅପଡେଟଗୁଡ଼ିକୁ ଅନୁମତି ଦିଆଯାଇଛି।"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"\"ପରୀକ୍ଷା ଟୋଗଲ କରିବା\" ସକ୍ଷମ କରାଯାଇଛି"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"\"ପରୀକ୍ଷା ଟୋଗଲ କରିବା\" ଅକ୍ଷମ କରାଯାଇଛି"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟ ଏବଂ ଡେକୋରେସନ ଡେମୋ"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟ ଟେଷ୍ଟ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"କେବଳ ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟକୁ ଚୟନ କରାଯାଇପାରିବ"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"ଡେକୋରେସନ ଟେଷ୍ଟ"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟ ଏବଂ ଡେକୋରେସନ"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ଏହି ଧାଡ଼ିଟିକୁ ମଧ୍ୟ ଚୟନ କରାଯାଇପାରିବ"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"ଦ୍ୱିତୀୟ କାର୍ଯ୍ୟ ଚୟନ କରାଯାଇଛି"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"ଧାଡ଼ିର ପ୍ରାଥମିକ କାର୍ଯ୍ୟ ଚୟନ କରାଯାଇଛି"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"ବିବିଧ ଟେମ୍ପଲେଟର ଡେମୋ"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Showcase ଡେମୋ"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"ଟେମ୍ପଲେଟ ଲେଆଉଟର ଡେମୋଗୁଡ଼ିକ"</string>
<string name="grid_template_menu_demo_title" msgid="7096285873490705119">"ଗ୍ରିଡ ଟେମ୍ପଲେଟର ଡେମୋଗୁଡ଼ିକ"</string>
<string name="voice_access_demo_title" msgid="3825223890895361496">"ଡେମୋ ସ୍କ୍ରିନକୁ ଭଏସ ଆକ୍ସେସ"</string>
- <string name="user_interactions_demo_title" msgid="1356952319161314986">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ଇଣ୍ଟରାକ୍ସନ"</string>
+ <string name="user_interactions_demo_title" msgid="1356952319161314986">"ୟୁଜର ଇଣ୍ଟରାକ୍ସନ"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ଅନୁମତିର ଡେମୋ ପାଇଁ ଅନୁରୋଧ"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ଆପ୍ଲିକେସନ ଓଭରଫ୍ଲୋ ଭାଲିଡେଟର"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"ପରିବର୍ତ୍ତନ କରିବା ବେଳେ ଦୟାକରି ନିମ୍ନୋକ୍ତ ଟେମ୍ପଲେଟଗୁଡ଼ିକୁ ପରୀକ୍ଷା କରନ୍ତୁ"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ପାର୍କ କରାଯାଇଥିବାରୁ ଡ୍ରାଇଭିଂ ସ୍ଥିତି ପର୍ଯ୍ୟନ୍ତ ଗାଡ଼ି"</string>
<string name="perm_group" msgid="3834918337351876270">"ଅନୁମତି ଗୋଷ୍ଠୀ"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase ଆପ ପାଇଁ ଅନୁମତି ଗୋଷ୍ଠୀ"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"ସଠିକ୍ ଲୋକେସନକୁ ଆକ୍ସେସ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml
index 3fb771d..80e73db 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"ਕਾਰਜ ਸੀਮਾ ਪੂਰੀ ਹੋ ਗਈ\nਜਾਰੀ ਰੱਖਣ \'ਤੇ ਐਪ ਜ਼ਬਰਦਸਤੀ ਬੰਦ ਹੋ ਜਾਵੇਗੀ"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%2$d ਵਿੱਚੋਂ %1$d ਕਾਰਜ ਪੜਾਅ"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ਅੱਗੇ ਜਾਣ ਲਈ ਕਲਿੱਕ ਕਰੋ"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"ਕਿਰਪਾ ਕਰਕੇ ਵੱਖ-ਵੱਖ ਟੈਮਪਲੇਟਾਂ \'ਤੇ ਜਾਓ ਅਤੇ ਪੱਕਾ ਕਰੋ ਕਿ ਕਾਰ ਡਰਾਈਵਿੰਗ ਮੋਡ ਵਿੱਚ ਹੈ"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"ਟੌਗਲ ਬਟਨ ਦਾ ਡੈਮੋ"</string>
<string name="toggle_test_title" msgid="924485265152862631">"ਟੈਸਟ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"ਸਪਸ਼ਟ ਤਬਦੀਲੀਆਂ ਕਰਨ ਦੀ ਆਗਿਆ ਹੈ"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"ਪਿਛਲੇ ਪੰਨੇ \'ਤੇ ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਆਗਿਆ ਹੈ।"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"\'ਟੈਸਟ ਨੂੰ ਟੌਗਲ ਕਰੋ\' ਚਾਲੂ ਹੈ"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"\'ਟੈਸਟ ਨੂੰ ਟੌਗਲ ਕਰੋ\' ਬੰਦ ਹੈ"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ਸੈਕੰਡਰੀ ਕਾਰਵਾਈ ਅਤੇ ਸਜਾਵਟ ਦਾ ਡੈਮੋ"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"ਸੈਕੰਡਰੀ ਕਾਰਵਾਈ ਜਾਂਚ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ਸਿਰਫ਼ ਸੈਕੰਡਰੀ ਕਾਰਵਾਈ ਨੂੰ ਹੀ ਚੁਣਿਆ ਜਾ ਸਕਦਾ ਹੈ"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"ਸਜਾਵਟ ਜਾਂਚ"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ਸੈਕੰਡਰੀ ਕਾਰਵਾਈਆਂ ਅਤੇ ਸਜਾਵਟ"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"ਕਤਾਰ ਨੂੰ ਵੀ ਚੁਣਿਆ ਜਾ ਸਕਦਾ ਹੈ"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"ਸੈਕੰਡਰੀ ਕਾਰਵਾਈ ਚੁਣੀ ਗਈ ਹੈ"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"ਕਤਾਰ ਨਾਲ ਸੰਬੰਧਿਤ ਪ੍ਰਾਇਮਰੀ ਕਾਰਵਾਈ ਚੁਣੀ ਗਈ ਹੈ"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"ਫੁਟਕਲ ਟੈਮਪਲੇਟਾਂ ਦੇ ਡੈਮੋ"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"ਸ਼ੋਅਕੇਸ ਦੇ ਡੈਮੋ"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"ਟੈਮਪਲੇਟ ਖਾਕੇ ਦੇ ਡੈਮੋ"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"ਅਵਾਜ਼ੀ ਪਹੁੰਚ ਡੈਮੋ ਸਕ੍ਰੀਨ"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"ਵਰਤੋਂਕਾਰ ਦੀਆਂ ਅੰਤਰਕਿਰਿਆਵਾਂ"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"ਇਜਾਜ਼ਤਾਂ ਦੀ ਬੇਨਤੀ ਦਾ ਡੈਮੋ"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ਐਪਲੀਕੇਸ਼ਨ ਓਵਰਫ਼ਲੋ ਵੈਲੀਡੇਟਰ"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"ਕਿਰਪਾ ਕਰਕੇ ਵਾਹਨ ਨੂੰ ਪਾਰਕ ਤੋਂ ਡਰਾਈਵਿੰਗ ਸਥਿਤੀ ਵਿੱਚ ਬਦਲਦੇ ਸਮੇਂ"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ਅੱਗੇ ਦਿੱਤੇ ਟੈਮਪਲੇਟਾਂ ਨੂੰ ਅਜ਼ਮਾਓ"</string>
<string name="perm_group" msgid="3834918337351876270">"ਇਜਾਜ਼ਤ ਗਰੁੱਪ"</string>
<string name="perm_group_description" msgid="7348847631139139024">"ਸ਼ੋਅਕੇਸ ਐਪ ਲਈ ਇਜਾਜ਼ਤ ਗਰੁੱਪ"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"ਸਟੀਕ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml
index a355377..bb11ae2 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Osiągnięto limit zadań\nKontynuowanie spowoduje wymuszenie zatrzymania aplikacji"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Krok zadania %1$d z %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Kliknij, aby przejść dalej"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Sprawdź różne szablony i upewnij się, że samochód jest w trybie jazdy"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Wersja demonstracyjna przycisku przełączania"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Test przełączania"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Zmiany stanu są dozwolone"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Aktualizacje dozwolone na operacjach cofania."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Test przełączania został włączony"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Test przełączania został wyłączony"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Działanie alternatywne i wersja demonstracyjna dekoracji"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test działania alternatywnego"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Można wybrać tylko działanie alternatywne"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Test dekoracji"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Działania alternatywne i dekoracja"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Można wybrać również wiersz"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Wybrano działanie alternatywne"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Wybrano wiersz z działaniem głównym"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Wersje demonstracyjne różnych szablonów"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Wersje demonstracyjne do prezentacji"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Wersje demonstracyjne szablonów układu"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Ekran wersji demonstracyjnej Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interakcje użytkownika"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Wersje demonstracyjne próśb o uprawnienia"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Walidator przepełnienia aplikacji"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Przetestuj te szablony, gdy przełączasz"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"samochód ze stanu zaparkowania do stanu jazdy"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupa uprawnień"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupa uprawnień dotyczących aplikacji Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Dostęp do dokładnej lokalizacji"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
index e06208c..3ab19fd 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite da tarefa atingido\nContinuar vai forçar a parada do app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Etapa da tarefa: %1$d de %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Clique para continuar"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Visite os diferentes modelos e confira se o veículo está no modo carro"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstração de botão de ativação"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Alternar teste"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Mudanças com estado são permitidas"</string>
@@ -307,35 +306,24 @@
<string name="additional_data_text" msgid="2846223398214158872">"Atualizações permitidas em operações de retorno."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Alternância de teste ativada"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Alternância de teste desativada"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demonstração de ação secundária e de decoração"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Teste de ação secundária"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Apenas a ação secundária pode ser selecionada"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Teste de decoração"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Ações secundárias e decoração"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"A linha também pode ser selecionada"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"A ação secundária está selecionada"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"A ação principal da linha está selecionada"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstração de modelos diversos"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrações em destaque"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrações de modelos de layout"</string>
<string name="grid_template_menu_demo_title" msgid="7096285873490705119">"Demonstrações de modelo de grade"</string>
- <string name="voice_access_demo_title" msgid="3825223890895361496">"Tela de demonstração do Acesso por voz"</string>
+ <string name="voice_access_demo_title" msgid="3825223890895361496">"Tela de demonstração do Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interações do usuário"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Solicitar demonstração de permissões"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validador do menu flutuante do aplicativo"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Teste os seguintes modelos durante a mudança"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"o veículo do estado de estacionamento para o de condução"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupo de permissões"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupo de permissões do app Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Acesso à localização precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml
index de619c2..90157c4 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"O limite de tarefas foi atingido\nSe avançar, vai forçar a paragem da app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Passo da tarefa %1$d de %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Clique para avançar"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Visite os diferentes modelos e certifique-se de que o carro está no modo de condução"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstração do botão ativar/desativar"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Ativar/desativar teste"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"São permitidas alterações com estado"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Ecrã de demonstração do Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interações do utilizador"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demonstrações sobre como pedir autorizações"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validador de menu adicional da aplicação"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Teste os seguintes modelos durante o carregamento"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"o veículo de estacionado para o estado de condução"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupo de autorizações"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupo de autorizações da app Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Acesso à localização exata"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
index e06208c..3ab19fd 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite da tarefa atingido\nContinuar vai forçar a parada do app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Etapa da tarefa: %1$d de %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Clique para continuar"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Visite os diferentes modelos e confira se o veículo está no modo carro"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstração de botão de ativação"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Alternar teste"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Mudanças com estado são permitidas"</string>
@@ -307,35 +306,24 @@
<string name="additional_data_text" msgid="2846223398214158872">"Atualizações permitidas em operações de retorno."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Alternância de teste ativada"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Alternância de teste desativada"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demonstração de ação secundária e de decoração"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Teste de ação secundária"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Apenas a ação secundária pode ser selecionada"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Teste de decoração"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Ações secundárias e decoração"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"A linha também pode ser selecionada"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"A ação secundária está selecionada"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"A ação principal da linha está selecionada"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstração de modelos diversos"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrações em destaque"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrações de modelos de layout"</string>
<string name="grid_template_menu_demo_title" msgid="7096285873490705119">"Demonstrações de modelo de grade"</string>
- <string name="voice_access_demo_title" msgid="3825223890895361496">"Tela de demonstração do Acesso por voz"</string>
+ <string name="voice_access_demo_title" msgid="3825223890895361496">"Tela de demonstração do Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interações do usuário"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Solicitar demonstração de permissões"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validador do menu flutuante do aplicativo"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Teste os seguintes modelos durante a mudança"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"o veículo do estado de estacionamento para o de condução"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupo de permissões"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupo de permissões do app Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Acesso à localização precisa"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml
index fb087ba..8e0262c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"A fost atinsă limita pentru activități\nDacă alegi să continui, aplicația se va opri forțat"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Pasul activității: %1$d din %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Dă clic pentru a merge înainte"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Accesează diferitele șabloane și asigură-te că mașina este în modul Cu mașina"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstrație cu buton de comutare"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Activează / dezactivează testul"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Modificările cu menținere de stare sunt permise"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Sunt permise actualizările operațiunilor în fundal."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Testul de comutare este activat"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Testul de comutare este dezactivat"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demonstrație pentru acțiunea secundară și decorație"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test pentru acțiunea secundară"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Numai acțiunea secundară poate fi selectată"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Test pentru decorație"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Acțiuni secundare și decorație"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Și rândul poate fi selectat"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Acțiunea secundară a fost selectată"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Acțiunea principală de pe rând a fost selectată"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrații diverse cu șabloane"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrații pentru Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrații cu aspecte de șablon"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Ecran demonstrativ pentru accesul vocal"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interacțiunile utilizatorilor"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Solicită demonstrații privind permisiunile"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Program de validare a meniului suplimentar al aplicației"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testează următoarele șabloane în timpul schimbării"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"stării vehiculului din Parcat în Cu mașina"</string>
<string name="perm_group" msgid="3834918337351876270">"Grup de permisiuni"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grup de permisiuni pentru aplicația Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Acces la locația fină"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml
index 940b3c6..8bd8eff 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Достигнут предел по числу задач.\nЕсли продолжить, работа приложения будет остановлена."</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Шаг задачи: %1$d из %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Нажмите, чтобы продолжить"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Посмотрите разные шаблоны и убедитесь, что автомобиль находится в режиме вождения"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Переключатель вида \"включено/отключено\" (режим демонстрации)"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Проверка переключения"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Изменения с отслеживанием состояния разрешены"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Обновления разрешены для обратных операций."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Проверка переключения включена"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Проверка переключения отключена"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демонстрация второстепенного действия и оформления"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Тест второстепенного действия"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Можно выбрать только второстепенное действие"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Тест оформления"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Второстепенные действия и оформление"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Можно также выбрать строку"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Выбрано второстепенное действие."</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Выбрано главное действие для строки."</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрации прочих шаблонов"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Демонстрации Showcase"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Макет шаблона (режим демонстрации)"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Экран демонстрации Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Взаимодействие пользователя"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демонстрации запроса разрешений"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Проверка дополнительного меню приложения"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Протестируйте следующие шаблоны при переходе"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"транспортного средства из режима парковки в режим вождения"</string>
<string name="perm_group" msgid="3834918337351876270">"Группа разрешений"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Группа разрешений для приложения \"Витрина\""</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Доступ к данным о точном местоположении"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml
index 612fcd6..8ffa434 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"කාර්ය සීමාව ළඟා විය\nඉදිරියට යාම යෙදුම බලෙන් නවත්වනු ඇත"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%2$d කින් %1$dවන කාර්ය පියවර"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ඉදිරියට යාමට ක්ලික් කරන්න"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"විවිධ අච්චු වෙත පැමිණ මෝටර් රථය ධාවන ප්රකාරයේ ඇති බව තහවුරු කර ගන්න"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"බොත්තම් ආදර්ශනය ටොගල් කරන්න"</string>
<string name="toggle_test_title" msgid="924485265152862631">"ටොගල පරීක්ෂාව"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"රාජ්ය වෙනස් කිරීම්වලට ඉඩ දේ"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"පසු මෙහෙයුම්වල යාවත්කාලීන කිරීම් ඉඩ දෙනු ලැබේ."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"ටොගල පරීක්ෂාව සබලයි"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"ටොගල පරීක්ෂාව අබලයි"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ද්විතීයික ක්රියා සහ සැරසිලි ආදර්ශනය"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"ද්විතියික ක්රියා පරීක්ෂණය"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"ද්විතියික ක්රියාව පමණක් තෝරා ගත හැක"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"සැරසිලි පරීක්ෂණය"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ද්විතියික ක්රියා සහ සැරසිලි"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"පේළිය ද තෝරා ගත හැක"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"ද්විතියික ක්රියාව තෝරා ඇත"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"පේළියේ මූලික ක්රියාව තෝරා ඇත"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"විවිධ අච්චු ආදර්ශන"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"ප්රකාශක තේරූ ආදර්ශන"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"අච්චු පිරිසැලසුම් ආදර්ශන"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"හඬ ප්රවේශ ආදර්ශන තිරය"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"පරිශීලක අන්තර්ක්රියා"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"අවසර ආදර්ශන ඉල්ලන්න"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"යෙදුම් ඉතිරියෑම් වලංගුකරණය"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"වෙනස් කරන අතරතුර පහත සඳහන් අච්චු පරීක්ෂා කරන්න"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"වාහනය ගාල් කළ තත්ත්වයේ සිට ධාවන තත්ත්වයට"</string>
<string name="perm_group" msgid="3834918337351876270">"අවසර සමූහය"</string>
<string name="perm_group_description" msgid="7348847631139139024">"ප්රදර්ශන යෙදුම සඳහා අවසර සමූහය"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"නිවැරදි ස්ථානයට ප්රවේශය"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml
index e09222bf..d039c09 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Bolo dosiahnuté obmedzenie úlohy\nAk budete pokračovať, aplikácia sa vynútene zastaví"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Krok úlohy: %1$d z %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Pokračujte kliknutím"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Prejdite na rôzne šablóny a uistite sa, že auto je režime v aute"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo prepínača"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Test prepínača"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Zmeny so stavom sú povolené"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Aktualizácie sú povolené pre operácie na pozadí."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Test prepínača bol aktivovaný"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Test prepínača bol deaktivovaný"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Sekundárna akcia a demo dekorácie"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test sekundárnej akcie"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Vybrať sa dá iba sekundárna akcia"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Test dekorácie"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundárne akcie a dekorácia"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Môžete vybrať aj daný riadok"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Bola vybraná sekundárna akcia"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Bola vybraná primárna akcia riadka"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Rôzne šablóny – demá"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Výber – demá"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demo rozložení šablóny"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Obrazovka demo verzie aplikácie Voice Access"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Interakcie používateľov"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Vyžadovať demá povolení"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Nástroj na overenie nadbytku aplikácií"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Otestujte tieto šablóny pri zmene"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"vozidla zo zaparkovaného stavu na stav jazdy"</string>
<string name="perm_group" msgid="3834918337351876270">"Skupina povolení"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Skupina povolení pre aplikáciu Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Prístup k presnej polohe"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml
index c210245..8a2083c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Dosežena je omejitev opravil.\nČe nadaljujete, bo aplikacija prisilno ustavljena."</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Korak opravila %1$d od %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Kliknite, če želite nadaljevati."</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Odprite različne predloge in poskrbite, da je avtomobil v načinu vožnje."</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Predstavitvena različica preklopnega gumba"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Preizkus preklopa"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Spremembe stanj so dovoljene."</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Posodobitve so dovoljene pri pomikih nazaj."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Preizkus preklopa je omogočen."</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Preizkus preklopa je onemogočen."</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Predstavitvena različica sekundarnega dejanja in okrasitve"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Preizkus sekundarnega dejanja"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Izbrati je mogoče samo sekundarno dejanje."</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Preizkus okrasitve"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundarna dejanja in okrasitve"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Izbrati je mogoče tudi vrstico."</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Sekundarno dejanje je izbrano."</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Glavno dejanje vrstice je izbrano."</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Predstavitvene različice različnih predlog"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Predstavitvene različice izpostavljenih stvari"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Predstavitvene različice postavitev predlog"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Predstavitvena različica zaslona za glasovni dostop"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Uporabniške interakcije"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Predstavitvene različice zahtev za dovoljenja"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Orodje za preverjanje presežkov aplikacije"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Preizkusite te predloge,"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ko preklapljate vozilo iz stanja parkiranosti v stanje vožnje."</string>
<string name="perm_group" msgid="3834918337351876270">"Skupina dovoljenj"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Skupina dovoljenj za aplikacijo Predstavitev"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Dostop do natančne lokacije"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
index 4e5475c..588afb4 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"U arrit kufiri i detyrës\nVazhdimi përpara do ta ndalojë aplikacionin me forcë"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Hapi i detyrës: %1$d nga %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Kliko për të vazhduar përpara"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Vizito shabllonet e ndryshme dhe sigurohu që makina të jetë në modalitetin e lëvizjes"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demonstrimi i butonit të aktivizimit/çaktivizimit"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Testimi i butonit aktivizo/çaktivizo"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Lejohen ndryshimet në monitorim"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Përditësimet lejohen në veprimet e mëparshme."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Testimi i butonit aktivizo/çaktivizo u aktivizua"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Testimi i butonit aktivizo/çaktivizo u çaktivizua"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Veprimi dytësor dhe demonstrimi i dekorimit"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Testimi i veprimit dytësor"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Mund të zgjidhet vetëm veprimi dytësor"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Testimi i dekorimit"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Veprimet dytësore dhe dekorimi"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Rreshti mund të zgjidhet gjithashtu"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"U zgjodh veprimi dytësor"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"U zgjodh veprimi kryesor i rreshtit"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrime shabllonesh të ndryshme"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrime të prezantimit"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrimet e strukturës së shabllonit"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Ekrani demonstrues i \"Qasjes me zë\""</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Ndërveprimet e përdoruesit"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demonstrimet e kërkesës për leje"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Verifikuesi i tejkalimit të aplikacionit"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testo shabllonet e mëposhtme gjatë ndryshimit"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"të automjetit nga gjendja e parkimit në gjendjen e lëvizjes"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupi i lejeve"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupi i lejeve për aplikacionin Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Qasje në vendndodhjen e saktë"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
index ec69183..c0d4d7f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Достигнуто је ограничење задатака\nАко наставите, апликација ће се принудно зауставити"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%1$d. корак задатка од %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Кликните да бисте ишли напред"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Посетите различите шаблоне и уверите се да је аутомобил у режиму вожње"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Демонстрација дугмета за укључивање/искључивање"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Укључи/искључи тест"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Промене стања су дозвољене"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Ажурирања су дозвољена за операције у позадини."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Тест укључивања/искључивања је омогућен"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Тест укључивања/искључивања је онемогућен"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демонстрација секундарне радње и декорације"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Тест секундарне радње"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Може да се изабере само секундарна радња"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Тест декорације"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Секундарне радње и декорација"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Може да се изабере и ред"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Секундарна радња је изабрана"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Примарна радња реда је изабрана"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрације различитих шаблона"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Демонстрације приказивања"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Демонстрације изгледа шаблона"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Екран демонстрације приступа гласу"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Корисничке интеракције"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демонстрације захтева за дозволе"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Валидатор прекорачења апликације"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Тестирајте следеће шаблоне током пуњења"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"статус возила из паркираног у вожња"</string>
<string name="perm_group" msgid="3834918337351876270">"Група дозвола"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Група дозвола за истицање апликације"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Приступ прецизној локацији"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml
index 135c174..8aa06dc 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Gränsen för antal uppgifter har uppnåtts\nOm du fortsätter tvingas appen att avslutas"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Uppgiftssteg %1$d av %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Klicka för att gå vidare"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Öppna de olika mallarna och se till att bilen är i körläge"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo för på/av-knapp"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Test för att aktivera och inaktivera"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Tillståndsbaserade ändringar är tillåtna"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Uppdateringar tillåtet i bakåtåtgärder."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Test av aktivera/inaktivera har aktiverats"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Test av aktivera/inaktivera har inaktiverats"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Demo av den sekundära åtgärden och dekoration"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Test av den sekundära åtgärden"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Det går endast att välja den sekundära åtgärden"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Dekorationstest"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Sekundära åtgärder och dekoration"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Det går även att välja raden"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Den sekundära åtgärden har valts"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Radens primära åtgärd har valts"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Demor för övriga mallar"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Visa demor"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Demor för mallayouter"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Demoskärm för röststyrning"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Användarinteraktioner"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demor av behörighetsförfrågningar"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validering av appspill"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Testa följande mallar när du byter"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"från parkeringsläge till körläge i fordonet"</string>
<string name="perm_group" msgid="3834918337351876270">"Behörighetsgrupp"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Behörighetsgrupp för exempelapp"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Åtkomst till exakt plats"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml
index d49aa0a..e9b8e2f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Kikomo cha shughuli kimefikiwa\nKuendelea kutalazimisha kuzima programu"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Hatua ya shughuli %1$d kati ya %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Bofya ili uendelee"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Tafadhali tembelea violezo tofauti na uhakikishe kuwa gari liko katika hali ya kuendesha gari"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Onyesho la Kitufe cha Kugeuza"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Jaribio la kipengele cha kuwasha/kuzima"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Mabadiliko yanayoweza kurudiwa mara kwa mara yanaruhusiwa"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Masasisho yameruhusiwa kwenye shughuli za nyuma."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Jaribio la Kipengele cha Kuwasha/Kuzima Limewashwa"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Jaribio la Kipengele cha Kuwasha/Kuzima Limezimwa"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Onyesho la Kitendo cha Upili na Jaribio la Usanifu"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Jaribio la Kitendo cha Upili"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Kitendo cha upili tu ndiyo kinaweza kuchaguliwa"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Jaribio la Usanifu"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Matendo ya Upili na Usanifu"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Safu mlalo pia inaweza kuchaguliwa"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Kitendo cha Upili kimechaguliwa"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Kitendo cha msingi cha safu mlalo kimechaguliwa"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Maonyesho ya Violezo Anuwai"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Maonyesho ya Kuangazia"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Maonyesho ya Kiolezo cha Muundo"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Skrini ya Toleo la Kujaribu la Kufikia kwa Kutamka"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Shughuli za Mtumiaji"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Omba Matoleo ya Kujaribu ya Ruhusa"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Kithibitishaji cha Vipengee vya ziada vya Programu"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Tafadhali jaribu violezo vifuatavyo huku ukibadilisha"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"gari limeondolewa katika hali ya kuegeshwa na kuwekwa katika hali ya kuendesha"</string>
<string name="perm_group" msgid="3834918337351876270">"Kikundi cha Ruhusa"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Kikundi cha Ruhusa cha Programu ya Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Ufikiaji wa Eneo Mahususi"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml
index 801c17c..81fa068 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"பணி வரம்பை எட்டிவிட்டது.\nமேலும் தொடர்ந்தால் ஆப்ஸ் உடனே நிறுத்தப்படும்"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%1$d / %2$d படியை எடுங்கள்"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"முன்னேறிச் செல்ல கிளிக் செய்யவும்"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"வெவ்வேறு டெம்ப்ளேட்களைப் பாருங்கள். கார் \'வாகனம் ஓட்டும் பயன்முறையில்\' இருப்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"நிலைமாற்றும் பட்டனின் டெமோ"</string>
<string name="toggle_test_title" msgid="924485265152862631">"நிலைமாற்றப் பரிசோதனை"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"நிலை மாற்றங்கள் அனுமதிக்கப்படுகின்றன"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"முந்தைய பக்கத்திற்குச் சென்று மாற்றங்களைச் செய்யலாம்."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"நிலைமாற்றப் பரிசோதனை இயக்கப்பட்டது"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"நிலைமாற்றப் பரிசோதனை முடக்கப்பட்டது"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"இரண்டாம்நிலைச் செயல் மற்றும் அலங்காரத்திற்கான டெமோ"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"இரண்டாம்நிலைச் செயலுக்கான பரிசோதனை"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"இரண்டாம்நிலைச் செயலை மட்டுமே தேர்ந்தெடுக்க முடியும்"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"அலங்காரப் பரிசோதனை"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"இரண்டாம்நிலைச் செயல்கள் மற்றும் அலங்காரம்"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"வரிசையையும் தேர்ந்தெடுக்க முடியும்"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"இரண்டாம்நிலைச் செயல் தேர்ந்தெடுக்கப்பட்டது"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"வரிசையின் முதன்மைச் செயல் தேர்ந்தெடுக்கப்பட்டது"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"மற்ற டெம்ப்ளேட்டுகளின் டெமோக்கள்"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"ஷோகேஸின் டெமோக்கள்"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"டெம்ப்ளேட் தளவமைப்பின் டெமோக்கள்"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"குரல் அணுகல் டெமோ திரை"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"பயனர் செயல்பாடுகள்"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"அனுமதிகள் டெமோக்களைக் கோருங்கள்"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ஆப்ஸ் ஓவர்ஃப்லோ பரிசோதிப்பான்"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"மாற்றும்போது பின்வரும் டெம்ப்ளேட்களைப் பரிசோதித்துப் பாருங்கள்"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"வாகனத்தின் நிலையைப் பார்க்கிங்கில் இருந்து இயக்கத்திற்கு மாற்றுங்கள்"</string>
<string name="perm_group" msgid="3834918337351876270">"அனுமதிக் குழு"</string>
<string name="perm_group_description" msgid="7348847631139139024">"ஷோகேஸ் ஆப்ஸிற்கான அனுமதிக் குழு"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"சரியான இருப்பிடத்திற்கான அணுகல்"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml
index c440829..556538c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"టాస్క్ల పరిమితిని చేరుకున్నారు\nఇంకా టాస్క్లను ఎంచుకుంటే యాప్ ఫోర్స్ స్టాప్ అవుతుంది"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%2$dలో %1$d టాస్క్ దశ"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"ముందుకు వెళ్లడానికి క్లిక్ చేయండి"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"దయచేసి వివిధ టెంప్లేట్లను సందర్శించండి, కారు డ్రైవింగ్ మోడ్లో ఉందని నిర్ధారించుకోండి"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"టోగుల్ బటన్ డెమో"</string>
<string name="toggle_test_title" msgid="924485265152862631">"టోగుల్ టెస్ట్"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"స్టేట్ఫుల్ మార్పులు అనుమతించబడతాయి"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"బ్యాక్గ్రౌండ్ ఆపరేషన్ల కోసం అప్డేట్లు అనుమతించబడ్డాయి."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"టోగుల్ టెస్ట్ ఎనేబుల్ చేయబడింది"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"టోగుల్ టెస్ట్ డిజేబుల్ చేయబడింది"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"రెండవ చర్య, డెకరేషన్ డెమో"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"రెండవ చర్య పరీక్ష"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"రెండవ చర్యను మాత్రమే ఎంచుకోవచ్చు"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"డెకరేషన్ టెస్ట్"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"రెండవ చర్య, డెకరేషన్"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"అడ్డు వరుసను కూడా ఎంచుకోవచ్చు"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"రెండవ చర్య ఎంచుకోబడింది"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"అడ్డు వరుస ప్రాథమిక చర్య ఎంచుకోబడింది"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"ఇతర టెంప్లేట్ల డెమోలు"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"డెమోలను ప్రదర్శించండి"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"టెంప్లేట్ లేఅవుట్ డెమోలు"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access డెమో స్క్రీన్"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"యూజర్ ఇంటరాక్షన్లు"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"అనుమతుల డెమోలను రిక్వెస్ట్ చేయండి"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"అప్లికేషన్ ఓవర్ఫ్లో వాలిడేటర్"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"దయచేసి మార్చేటప్పుడు కింది టెంప్లేట్లను పరీక్షించండి"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"వాహనం పార్క్ నుండి డ్రైవింగ్ స్టేట్కు మారండి"</string>
<string name="perm_group" msgid="3834918337351876270">"అనుమతి గ్రూప్"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Showcase యాప్ కోసం అనుమతి గ్రూప్"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"ఖచ్చితమైన లొకేషన్ యాక్సెస్"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml
index 409e74f..e6fcd8f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"ถึงขีดจำกัดงานแล้ว\nการดำเนินการต่อจะบังคับให้แอปหยุด"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"งานขั้นตอนที่ %1$d จาก %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"คลิกเพื่อดำเนินการต่อ"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"โปรดไปที่เทมเพลตอื่นและตรวจสอบว่ารถอยู่ในโหมดขับรถ"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"การสาธิตปุ่มเปิด/ปิด"</string>
<string name="toggle_test_title" msgid="924485265152862631">"การทดสอบการสลับ"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"อนุญาตการเปลี่ยนแปลงแบบเก็บสถานะ"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"หน้าจอสาธิตการเข้าถึงด้วยเสียง"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"การโต้ตอบของผู้ใช้"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"การสาธิตการขอสิทธิ์"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"โปรแกรมตรวจสอบรายการเพิ่มเติมของแอปพลิเคชัน"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"โปรดทดสอบเทมเพลตต่อไปนี้ขณะเปลี่ยน"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"สถานะยานพาหนะจากจอดอยู่เป็นกำลังขับ"</string>
<string name="perm_group" msgid="3834918337351876270">"กลุ่มสิทธิ์"</string>
<string name="perm_group_description" msgid="7348847631139139024">"กลุ่มสิทธิ์สำหรับแอป Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"เข้าถึงตำแหน่งอย่างละเอียด"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml
index bcc8aba..554ce6c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Naabot na ang limitasyon ng gawain\nSapilitang ihihinto ang app kapag nagpatuloy"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Gawin ang hakbang %1$d sa %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Mag-click para magpatuloy"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Pakibisita ang iba\'t ibang template at tiyaking nasa driving mode ang kotse"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo ng Toggle Button"</string>
<string name="toggle_test_title" msgid="924485265152862631">"I-toggle ang test"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Pinapayagan ang mga pagbabago sa status"</string>
@@ -322,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Screen ng Demo ng Pag-access gamit ang Boses"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Mga Pakikipag-ugnayan ng User"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Humiling ng Mga Demo ng Pahintulot"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Validator ng Overflow ng Application"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Pakisubukan ang mga sumusunod na template habang binabago"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"ang status ng sasakyan mula naka-park patungong nagmamaneho"</string>
<string name="perm_group" msgid="3834918337351876270">"Grupo ng Pahintulot"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Grupo ng Pahintulot para sa Showcase App"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Access sa Eksaktong Lokasyon"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml
index 5dc17de..28e0de1 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Görev sınırına ulaşıldı\nDevam ederseniz uygulama zorla durdurulacak"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%1$d/%2$d görev adımı"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Devam etmek için tıklayın"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Lütfen farklı şablonları inceleyip arabanın sürüş modunda olduğundan emin olun"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Açma/Kapatma Düğmesi Demosu"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Açma/kapatma testi"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Durum bilgili değişikliklere izin verilir"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Önceki işlemlerde güncellemelere izin verilir."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Açma/Kapatma Testi Etkin"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Açma/Kapatma Testi Devre Dışı"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"İkincil İşlem ve Süsleme Demosu"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"İkincil İşlem Testi"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Yalnızca ikincil işlem seçilebilir"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Süsleme Testi"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"İkincil İşlemler ve Süsleme"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Satır da seçilebilir"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"İkincil işlem seçildi"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Satırdaki birincil işlem seçildi"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Çeşitli Şablon Demoları"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Öne Çıkan Demoları"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Şablon Düzeni Demoları"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Sesli Erişim Demo Ekranı"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Kullanıcı Etkileşimleri"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"İzin İsteme Demoları"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Uygulama Taşma Doğrulayıcı"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Park halindeki aracı sürüş durumuna geçirirken"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"lütfen aşağıdaki şablonları test edin"</string>
<string name="perm_group" msgid="3834918337351876270">"İzin Grubu"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Vitrin Uygulamasının İzin Grubu"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Hassas Konuma Erişim"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml
index 1b79a87..937028f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Досягнуто ліміту завдань\nЯкщо продовжити, програму буде примусово зупинено"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Крок завдання %1$d з %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Натисніть, щоб продовжити"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Перегляньте різні шаблони й переконайтеся, що автомобіль перебуває в режимі кермування"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Демонстрація перемикача"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Змінити перевірку"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Дозволено змінювати стан"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Оновлення дозволено для операцій повернення."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Перевірку перемикача ввімкнено"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Перевірку перемикача вимкнено"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Демонстрація додаткової дії й оформлення"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Перевірка додаткової дії"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Можна вибрати лише додаткову дію"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Перевірка оформлення"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Додаткові дії й оформлення"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Рядок також можна вибрати"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Вибрано додаткову дію"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Вибрано основну дію рядка"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрації інших шаблонів"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Увімкнути демонстрації"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Демонстрації макетів шаблонів"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Демонстрація екрана Голосового доступу"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Взаємодія з користувачем"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Демонстрації запитів на отримання дозволів"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Перевірка додаткового меню додатка"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Перевірте наведені далі шаблони,"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"коли змінюватимете режим паркування на кермування автомобілем"</string>
<string name="perm_group" msgid="3834918337351876270">"Група дозволів"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Група дозволів для демонстраційного додатка"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Доступ до даних про точне місцезнаходження"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml
index 9a3b163..0608c26 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml
@@ -296,8 +296,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"ٹاسک کی حد پوری ہو گئی\nآگے بڑھنے سے ایپ کو زبردستی روک دیا جائے گا"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"%2$d میں سے %1$d ٹاسک کا مرحلہ"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"آگے بڑھنے کے لیے کلک کریں"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"براہ کرم مختلف ٹیمپلیٹس دیکھیں اور یقینی بنائیں کہ کار ڈرائیونگ موڈ میں ہے"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"ٹوگل بٹن کا ڈیمو"</string>
<string name="toggle_test_title" msgid="924485265152862631">"ٹیسٹ کو ٹوگل کریں"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"اسٹیٹ فل تبدیلیوں کی اجازت ہے"</string>
@@ -311,22 +310,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"اپ ڈیٹس پچھلے صفحے پر جانے کی اجازت دی گئی۔"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"ٹیسٹ ٹوگل کرنے کی سہولت فعال ہے"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"ٹیسٹ ٹوگل کرنے کی سہولت غیر فعال ہے"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"ثانوی کارروائی اور ڈیکوریشن ڈیمو"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"ثانوی کارروائی ٹیسٹ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"صرف ثانوی کارروائی کا انتخاب کیا جا سکتا ہے"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"ڈیکوریشن ٹیسٹ"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"ثانوی کارروائیاں اور ڈیکوریشن"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"قطار بھی منتخب کی جا سکتی ہے"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"ثانوی کارروائی کا انتخاب کیا گیا ہے"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"قطار کی بنیادی کارروائی کا انتخاب کیا گیا ہے"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"متفرق تمثیلات کے ڈیموز"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"ڈیموز دکھائیں"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"تمثیل کے لے آؤٹ کے ڈیموز"</string>
@@ -334,12 +325,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"صوتی رسائی کی ڈیمو سکرین"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"صارف کے تعاملات"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"اجازتوں کے ڈیموز کی درخواست کریں"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"ایپلیکیشن اوور فلو کا تصدیق کنندہ"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"براہ کرم تبدیل کرتے وقت درج ذیل ٹیمپلیٹس کی جانچ کریں"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"گاڑی پارک کرنے سے ڈرائیونگ کی حالت تک"</string>
<string name="perm_group" msgid="3834918337351876270">"اجازت کا گروپ"</string>
<string name="perm_group_description" msgid="7348847631139139024">"شوکیس ایپ کے لیے اجازت کا گروپ"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"درست مقام تک رسائی"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml
index 5c718b9..b2801a5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Vazifa chekloviga yetildi\nDavom etish ilovani majburiy toʻxtatadi."</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Vazifa qadami: %1$d / %2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Oldinga oʻtish uchun bosing"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Turli andozalarni ochib koʻring va avtomobil haydash rejimida ekanini tekshiring"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Demo ekran tugmasi"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Almashtirish tekshiruvi"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Vaziyatni oʻzgartirishga ruxsat beriladi"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Teskari amallarda yangilanishlarga ruxsat berilgan."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Testni oʻchirish/yoqish faollashtirildi"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Testni oʻchirish/yoqish faolsizlantirildi"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Ikkilamchi amal va bezak demosi"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Ikkilamchi amal testi"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Faqat ikkilamchi amal tanlanishi mumkin"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Bezak testi"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Ikkilamchi amallar va bezak"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Qator ham tanlanishi mumkin"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Ikkilamchi amal tanlandi"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Asosiy amal qatori tanlandi"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Aralash andozalar demolari"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Namoyish demolari"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Andoza dizayni demolari"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Demo ekranida ovozli boshqaruv"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Foydalanuvchi harakatlari"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demolarga ruxsatlarni talab qilish"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Ilovani chuqur tekshirish vositasi"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Tahrirlash vaqtida quyidagi andozalarni sinang"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"parklangan avtomobil rejimidan haydash rejimiga"</string>
<string name="perm_group" msgid="3834918337351876270">"Ruxsatlar guruhi"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Vitrina ilovasi uchun ruxsatlar guruhi"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Aniq joylashuv axborotiga ruxsat"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml
index 807875a1..2bd3d63 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Đã đạt đến giới hạn nhiệm vụ\nNếu tiếp tục sẽ buộc phải dừng ứng dụng"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Bước %1$d trong số %2$d của nhiệm vụ"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Nhấp để tiếp tục"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Vui lòng truy cập nhiều mẫu riêng biệt và đảm bảo xe đang ở chế độ lái"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Bản minh hoạ nút bật tắt"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Kiểm tra chuyển đổi"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Các thay đổi rõ ràng được phép"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Cho phép cập nhật đối với các thiết bị chạy ngầm."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Đã bật tính năng chuyển đổi"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Đã tắt tính năng kiểm tra chuyển đổi"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Bản minh hoạ phần trang trí và hành động phụ"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Thử nghiệm hành động phụ"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Bạn chỉ có thể chọn hành động phụ"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Thử nghiệm phần trang trí"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Phần trang trí và hành động phụ"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Bạn cũng có thể chọn hàng"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Đã chọn hành động phụ"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Đã chọn hành động chính cho hàng"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Bản demo biểu mẫu Misc"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Bản demo nổi bật"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Bản minh hoạ bố cục mẫu"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Màn hình minh hoạ chức năng Điều khiển bằng giọng nói"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Hoạt động tương tác của người dùng"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Bản minh hoạ về yêu cầu quyền"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Trình xác thực chế độ kết xuất bổ sung của ứng dụng"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Vui lòng kiểm tra các mẫu sau trong khi thay đổi"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"chiếc xe từ trạng thái đỗ sang trạng thái lái"</string>
<string name="perm_group" msgid="3834918337351876270">"Nhóm quyền"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Nhóm quyền dành cho ứng dụng Showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Truy cập vào thông tin vị trí chính xác"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml
index db8258f..5c20e7e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"已达到任务限制\n继续会强制停止应用"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"任务第 %1$d 步(共 %2$d 步)"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"点击以继续"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"请访问不同模板并确保此车辆处于驾驶模式"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"切换按钮演示"</string>
<string name="toggle_test_title" msgid="924485265152862631">"切换测试"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"允许有状态更改"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"返回操作允许更新。"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"切换测试已启用"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"切换测试已停用"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"次要操作和装饰演示"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"次要操作测试"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"只能选择次要操作"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"装饰测试"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"次要操作和装饰"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"也可以选择行"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"次要操作处于选中状态"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"行主要操作处于选中状态"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"其他模板演示"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"展示演示"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"模板布局演示"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access 演示屏幕"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"用户互动"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"请求权限演示"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"应用溢出验证器"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"更改时请测试以下模板"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"从停车状态变为驾驶状态的车辆"</string>
<string name="perm_group" msgid="3834918337351876270">"权限组"</string>
<string name="perm_group_description" msgid="7348847631139139024">"“展示”应用的权限组"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"访问精确位置信息"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
index d62fa2d..b5b7c32 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"工作已達上限\n繼續使用將強制停止應用程式"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"工作步驟 %1$d,共 %2$d 個步驟"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"點擊即可繼續使用"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"請查看各種範本,並確保汽車處於駕駛模式"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"「切換按鈕」示範"</string>
<string name="toggle_test_title" msgid="924485265152862631">"切換測試"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"允許有狀態的變更"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"允許在返回時更新。"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"已啟用切換測試"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"已停用切換測試"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"次要動作及裝飾示範"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"次要動作測試"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"只可選取次要動作"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"裝飾測試"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"次要動作及裝飾"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"亦可選取列"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"已選取次要動作"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"已選取列的主要動作"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"其他範本示範"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"展示示範"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"「範本版面配置」示範"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"語音操控示範畫面"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"使用者互動"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"「要求權限」示範"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"應用程式展開式驗證工具"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"請在變更時測試以下範本"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"從泊車狀態轉為行駛狀態的汽車"</string>
<string name="perm_group" msgid="3834918337351876270">"權限群組"</string>
<string name="perm_group_description" msgid="7348847631139139024">"「展示」應用程式的權限群組"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"存取精確位置"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml
index 5fed181..8e2921b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"工作已達上限\n繼續使用將強制停止應用程式"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"工作步驟 %1$d,共 %2$d 個步驟"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"點選即可繼續使用"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"請前往不同範本並確認車輛處於行車模式"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"「切換鈕」示範"</string>
<string name="toggle_test_title" msgid="924485265152862631">"切換測試"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"允許有狀態的變更"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"允許返回上一頁更新。"</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"切換測試已啟用"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"切換測試已停用"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"次要動作和裝飾示範"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"次要動作測試"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"只可以選取次要動作"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"裝飾測試"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"次要動作與裝飾"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"也可以選取列"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"已選取次要動作"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"已選取列的主要動作"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"其他範本示範"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"「展示」示範"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"「範本版面配置」示範"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Voice Access 示範畫面"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"使用者互動"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"要求權限示範"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"應用程式溢位驗證工具"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"請在變更時測試以下範本"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"從停車狀態轉為行駛狀態的車輛"</string>
<string name="perm_group" msgid="3834918337351876270">"權限群組"</string>
<string name="perm_group_description" msgid="7348847631139139024">"「展示」應用程式的權限群組"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"精確位置存取權"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml
index 2adeb6d..45c51e3 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml
@@ -292,8 +292,7 @@
<string name="task_limit_reached_msg" msgid="6038763366777119364">"Umkhawulo womsebenzi ufinyelelwe\nUkuqhubekela phambili kuzophoqa ukumisa i-app"</string>
<string name="task_step_of_title" msgid="2791717962535723839">"Isinyathelo somsebenzi %1$d kwengu-%2$d"</string>
<string name="task_step_of_text" msgid="4646729781462227219">"Chofoza ukuze uye phambili"</string>
- <!-- no translation found for task_content_allowed (545061986612431190) -->
- <skip />
+ <string name="task_content_allowed" msgid="545061986612431190">"Sicela uvakashele izifanekiso ezahlukene futhi uqinisekise ukuthi imoto ikumodi yokushayela"</string>
<string name="toggle_button_demo_title" msgid="3179103600967398928">"Inkinobho Yokuguqula I-demo"</string>
<string name="toggle_test_title" msgid="924485265152862631">"Guqula ukuhlola"</string>
<string name="toggle_test_text" msgid="8107217216013312857">"Izinguquko ezisemthethweni zivunyelwe"</string>
@@ -307,22 +306,14 @@
<string name="additional_data_text" msgid="2846223398214158872">"Izibuyekezo zivunyelwe ukusebenza ngemuva."</string>
<string name="toggle_test_enabled" msgid="982370904182034076">"Ukuhlola Kokuguqula Kunikwe Amandla"</string>
<string name="toggle_test_disabled" msgid="8366040658408451664">"Ukuhlola Kokuguqula Kukhutshaziwe"</string>
- <!-- no translation found for secondary_actions_decoration_button_demo_title (3710817648501132309) -->
- <skip />
- <!-- no translation found for secondary_actions_test_title (3664453747553733613) -->
- <skip />
- <!-- no translation found for secondary_actions_test_subtitle (6985282813402073703) -->
- <skip />
- <!-- no translation found for decoration_test_title (8450127046762442244) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_title (6282873404859209490) -->
- <skip />
- <!-- no translation found for secondary_actions_decoration_test_subtitle (155884606592724532) -->
- <skip />
- <!-- no translation found for secondary_action_toast (5076434693504006565) -->
- <skip />
- <!-- no translation found for row_primary_action_toast (756516694751965204) -->
- <skip />
+ <string name="secondary_actions_decoration_button_demo_title" msgid="3710817648501132309">"Idemo Yesibili Yesenzo Nokuhlobisa"</string>
+ <string name="secondary_actions_test_title" msgid="3664453747553733613">"Ukuhlolwa Kwesenzo Kwesibili"</string>
+ <string name="secondary_actions_test_subtitle" msgid="6985282813402073703">"Isenzo sesibili kuphela esingakhethwa"</string>
+ <string name="decoration_test_title" msgid="8450127046762442244">"Ukuhlolwa Kokuhlobisa"</string>
+ <string name="secondary_actions_decoration_test_title" msgid="6282873404859209490">"Izenzo Zesibili Nokuhlobisa"</string>
+ <string name="secondary_actions_decoration_test_subtitle" msgid="155884606592724532">"Umugqa ungabuye ukhethwe"</string>
+ <string name="secondary_action_toast" msgid="5076434693504006565">"Isenzo Sesibili Sikhethiwe"</string>
+ <string name="row_primary_action_toast" msgid="756516694751965204">"Isenzo esiyinhloko somugqa sikhethiwe"</string>
<string name="misc_templates_demos_title" msgid="6077169010255928114">"Amademo Ezifanekiso Ezixubile"</string>
<string name="showcase_demos_title" msgid="1542092687878113304">"Bonisa Amademo"</string>
<string name="template_layouts_demo_title" msgid="788249269446087847">"Ama-demo Esakhiwo Sesifanekiso"</string>
@@ -330,12 +321,9 @@
<string name="voice_access_demo_title" msgid="3825223890895361496">"Isikrini Sedemo Sokufinyelela Ngezwi"</string>
<string name="user_interactions_demo_title" msgid="1356952319161314986">"Ukusebenzisana Komsebenzisi"</string>
<string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Cela Amademo Ezimvume"</string>
- <!-- no translation found for application_overflow_title (396427940886169325) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle1 (7429415605726615529) -->
- <skip />
- <!-- no translation found for application_overflow_subtitle2 (4385123036846369714) -->
- <skip />
+ <string name="application_overflow_title" msgid="396427940886169325">"Isiqinisekisi sokuchichima se-app"</string>
+ <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Sicela uhlole izifanekiso ezilandelayo ngenkathi ushintsha"</string>
+ <string name="application_overflow_subtitle2" msgid="4385123036846369714">"imoto isuka lapho imile iye esimweni sokushayela"</string>
<string name="perm_group" msgid="3834918337351876270">"Iqembu Lemvume"</string>
<string name="perm_group_description" msgid="7348847631139139024">"Iqembu Lemvume Le-app Ye-showcase"</string>
<string name="perm_fine_location" msgid="5438874642600304118">"Ukufinyelela Endaweni Enhle"</string>
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index aab5ed0..7aac595 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -350,6 +350,9 @@
fun Offset.reverseIfNeeded(): Offset = if (reverseDirection) this * -1f else this
+ /**
+ * @return the amount of scroll that was consumed
+ */
fun ScrollScope.dispatchScroll(availableDelta: Offset, source: NestedScrollSource): Offset {
val scrollDelta = availableDelta.singleAxisOffset()
val overscrollPreConsumed = overscrollPreConsumeDelta(scrollDelta, source)
@@ -376,7 +379,7 @@
source
)
- return leftForParent - parentConsumed
+ return overscrollPreConsumed + preConsumedByParent + axisConsumed + parentConsumed
}
fun overscrollPreConsumeDelta(
@@ -447,8 +450,7 @@
var result: Velocity = available
scrollableState.scroll {
val outerScopeScroll: (Offset) -> Offset = { delta ->
- val consumed = this.dispatchScroll(delta.reverseIfNeeded(), Fling)
- delta - consumed.reverseIfNeeded()
+ dispatchScroll(delta.reverseIfNeeded(), Fling).reverseIfNeeded()
}
val scope = object : ScrollScope {
override fun scrollBy(pixels: Float): Float {
diff --git a/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconSourceTasks.kt b/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconSourceTasks.kt
index be6e663..4e62a3d 100644
--- a/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconSourceTasks.kt
+++ b/compose/material/material/icons/generator/src/main/kotlin/androidx/compose/material/icons/generator/tasks/IconSourceTasks.kt
@@ -119,6 +119,10 @@
val sourceSet = project.getMultiplatformSourceSet(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
val generatedSrcMainDirectory = buildDirectory.resolve(IconGenerationTask.GeneratedSrcMain)
sourceSet.kotlin.srcDir(project.files(generatedSrcMainDirectory).builtBy(task))
+ // add it to the multiplatform sources as well.
+ project.tasks.named("multiplatformSourceJar", Jar::class.java).configure {
+ it.from(task.map { generatedSrcMainDirectory })
+ }
project.addToSourceJar(generatedSrcMainDirectory, task)
}
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2StateTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2StateTest.kt
index 10da79f..1258e7e 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2StateTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/swipeable/SwipeableV2StateTest.kt
@@ -91,6 +91,18 @@
rule.waitForIdle()
assertThat(state.targetValue).isEqualTo(B)
+ // Assert that swipe below threshold upward settles at current state
+ rule.onNodeWithTag(swipeableTestTag)
+ .performTouchInput { swipeUp(endY = bottom * 0.95f, durationMillis = 1000) }
+ rule.waitForIdle()
+ assertThat(state.targetValue).isEqualTo(B)
+
+ // Assert that swipe below threshold downward settles at current state
+ rule.onNodeWithTag(swipeableTestTag)
+ .performTouchInput { swipeDown(endY = bottom * 0.05f) }
+ rule.waitForIdle()
+ assertThat(state.targetValue).isEqualTo(B)
+
rule.onNodeWithTag(swipeableTestTag)
.performTouchInput { swipeDown(endY = bottom * 0.9f) }
rule.waitForIdle()
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt
index a9df38a..70c2766 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeableV2.kt
@@ -368,7 +368,13 @@
val distance = abs(currentAnchor - currentAnchors.getValue(lower))
val relativeThreshold = abs(positionalThreshold(density, distance))
val absoluteThreshold = abs(currentAnchor - relativeThreshold)
- if (offset > absoluteThreshold) currentValue else lower
+ if (offset < 0) {
+ // For negative offsets, larger absolute thresholds are closer to lower anchors
+ // than smaller ones.
+ if (abs(offset) < absoluteThreshold) currentValue else lower
+ } else {
+ if (offset > absoluteThreshold) currentValue else lower
+ }
}
}
}
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index bc57514..f96b55a 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -103,6 +103,9 @@
method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
}
+ public final class CalendarModelKt {
+ }
+
@androidx.compose.runtime.Immutable public final class CardColors {
}
@@ -681,6 +684,9 @@
package androidx.compose.material3.internal {
+ public final class AndroidDatePickerModel_androidKt {
+ }
+
public final class ExposedDropdownMenuPopupKt {
}
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index 906e393..c815b0b 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -132,6 +132,9 @@
method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
}
+ public final class CalendarModelKt {
+ }
+
@androidx.compose.runtime.Immutable public final class CardColors {
}
@@ -1013,6 +1016,9 @@
package androidx.compose.material3.internal {
+ public final class AndroidDatePickerModel_androidKt {
+ }
+
public final class ExposedDropdownMenuPopupKt {
}
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index bc57514..f96b55a 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -103,6 +103,9 @@
method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
}
+ public final class CalendarModelKt {
+ }
+
@androidx.compose.runtime.Immutable public final class CardColors {
}
@@ -681,6 +684,9 @@
package androidx.compose.material3.internal {
+ public final class AndroidDatePickerModel_androidKt {
+ }
+
public final class ExposedDropdownMenuPopupKt {
}
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CalendarModelTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CalendarModelTest.kt
new file mode 100644
index 0000000..9b5997e
--- /dev/null
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CalendarModelTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.material3.internal.CalendarModelImpl
+import androidx.compose.material3.internal.LegacyCalendarModelImpl
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import java.util.Locale
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@MediumTest
+@RunWith(Parameterized::class)
+@OptIn(ExperimentalMaterial3Api::class)
+@RequiresApi(Build.VERSION_CODES.O)
+internal class CalendarModelTest(private val model: CalendarModel) {
+
+ @Test
+ fun dateCreation() {
+ val date = model.getDate(January2022Millis) // 1/1/2022
+ assertThat(date.year).isEqualTo(2022)
+ assertThat(date.month).isEqualTo(1)
+ assertThat(date.dayOfMonth).isEqualTo(1)
+ assertThat(date.utcTimeMillis).isEqualTo(January2022Millis)
+ }
+
+ @Test
+ fun dateRestore() {
+ val date =
+ CalendarDate(year = 2022, month = 1, dayOfMonth = 1, utcTimeMillis = January2022Millis)
+ assertThat(model.getDate(date.utcTimeMillis)).isEqualTo(date)
+ }
+
+ @Test
+ fun monthCreation() {
+ val date =
+ CalendarDate(year = 2022, month = 1, dayOfMonth = 1, utcTimeMillis = January2022Millis)
+ val monthFromDate = model.getMonth(date)
+ val monthFromMilli = model.getMonth(January2022Millis)
+ val monthFromYearMonth = model.getMonth(year = 2022, month = 1)
+ assertThat(monthFromDate).isEqualTo(monthFromMilli)
+ assertThat(monthFromDate).isEqualTo(monthFromYearMonth)
+ }
+
+ @Test
+ fun monthRestore() {
+ val month = model.getMonth(year = 1999, month = 12)
+ assertThat(model.getMonth(month.startUtcTimeMillis)).isEqualTo(month)
+ }
+
+ @Test
+ fun plusMinusMonth() {
+ val month = model.getMonth(January2022Millis) // 1/1/2022
+ val expectedNextMonth = model.getMonth(month.endUtcTimeMillis + 1) // 2/1/2022
+ val plusMonth = model.plusMonths(from = month, addedMonthsCount = 1)
+ assertThat(plusMonth).isEqualTo(expectedNextMonth)
+ assertThat(model.minusMonths(from = plusMonth, subtractedMonthsCount = 1)).isEqualTo(month)
+ }
+
+ @Test
+ fun parseDate() {
+ val expectedDate =
+ CalendarDate(year = 2022, month = 1, dayOfMonth = 1, utcTimeMillis = January2022Millis)
+ val parsedDate = model.parse("1/1/2022", "M/d/yyyy")
+ assertThat(parsedDate).isEqualTo(expectedDate)
+ }
+
+ @Test
+ fun formatDate() {
+ val date =
+ CalendarDate(year = 2022, month = 1, dayOfMonth = 1, utcTimeMillis = January2022Millis)
+ val month = model.plusMonths(model.getMonth(date), 2)
+ assertThat(model.format(date, "MM/dd/yyyy")).isEqualTo("01/01/2022")
+ assertThat(model.format(month, "MM/dd/yyyy")).isEqualTo("03/01/2022")
+ }
+
+ @Test
+ fun weekdayNames() {
+ // Ensure we are running on a US locale for this test.
+ Locale.setDefault(Locale.US)
+ val weekDays = model.weekdayNames
+ assertThat(weekDays).hasSize(DaysInWeek)
+ // Check that the first day is always "Monday", per ISO-8601 standard.
+ assertThat(weekDays.first().first).ignoringCase().contains("Monday")
+ weekDays.forEach {
+ assertThat(it.second.first().lowercaseChar()).isEqualTo(
+ it.first.first().lowercaseChar()
+ )
+ }
+ }
+
+ @Test
+ fun equalModelsOutput() {
+ // Note: This test ignores the parameters and just runs a few equality tests for the output.
+ // It will execute twice, but that should to tolerable :)
+ val newModel = CalendarModelImpl()
+ val legacyModel = LegacyCalendarModelImpl()
+
+ val date = newModel.getDate(January2022Millis) // 1/1/2022
+ val legacyDate = legacyModel.getDate(January2022Millis)
+ val month = newModel.getMonth(date)
+ val legacyMonth = legacyModel.getMonth(date)
+
+ assertThat(newModel.today).isEqualTo(legacyModel.today)
+ assertThat(month).isEqualTo(legacyMonth)
+ assertThat(newModel.plusMonths(month, 3)).isEqualTo(legacyModel.plusMonths(month, 3))
+ assertThat(date).isEqualTo(legacyDate)
+ assertThat(newModel.getDayOfWeek(date)).isEqualTo(legacyModel.getDayOfWeek(date))
+ assertThat(newModel.format(date, "MMM d, yyyy")).isEqualTo(
+ legacyModel.format(
+ date,
+ "MMM d, yyyy"
+ )
+ )
+ assertThat(newModel.format(month, "MMM yyyy")).isEqualTo(
+ legacyModel.format(
+ month,
+ "MMM yyyy"
+ )
+ )
+ }
+
+ internal companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun parameters() = arrayOf(
+ CalendarModelImpl(),
+ LegacyCalendarModelImpl()
+ )
+ }
+}
+
+private const val January2022Millis = 1640995200000
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AndroidDatePickerModel.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AndroidDatePickerModel.android.kt
new file mode 100644
index 0000000..089aeb0
--- /dev/null
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AndroidDatePickerModel.android.kt
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.internal
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.material3.CalendarDate
+import androidx.compose.material3.CalendarModel
+import androidx.compose.material3.CalendarMonth
+import androidx.compose.material3.DaysInWeek
+import androidx.compose.material3.ExperimentalMaterial3Api
+import java.text.DateFormatSymbols
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.time.DayOfWeek
+import java.time.Instant
+import java.time.LocalDate
+import java.time.LocalTime
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+import java.time.format.DateTimeParseException
+import java.time.format.TextStyle
+import java.time.temporal.WeekFields
+import java.util.Calendar
+import java.util.Locale
+import java.util.TimeZone
+
+/**
+ * Creates a [CalendarModel] to be used by the date picker.
+ */
+@ExperimentalMaterial3Api
+internal fun createDefaultCalendarModel(): CalendarModel {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ CalendarModelImpl()
+ } else {
+ LegacyCalendarModelImpl()
+ }
+}
+
+/**
+ * A [CalendarModel] implementation for API < 26.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+internal class LegacyCalendarModelImpl : CalendarModel {
+
+ override val today
+ get(): CalendarDate {
+ val systemCalendar = Calendar.getInstance()
+ systemCalendar[Calendar.HOUR_OF_DAY] = 0
+ systemCalendar[Calendar.MINUTE] = 0
+ systemCalendar[Calendar.SECOND] = 0
+ systemCalendar[Calendar.MILLISECOND] = 0
+ val utcOffset =
+ systemCalendar.get(Calendar.ZONE_OFFSET) + systemCalendar.get(Calendar.DST_OFFSET)
+ return CalendarDate(
+ year = systemCalendar[Calendar.YEAR],
+ month = systemCalendar[Calendar.MONTH] + 1,
+ dayOfMonth = systemCalendar[Calendar.DAY_OF_MONTH],
+ utcTimeMillis = systemCalendar.timeInMillis + utcOffset
+ )
+ }
+
+ override val firstDayOfWeek: Int = dayInISO8601(Calendar.getInstance().firstDayOfWeek)
+
+ override val weekdayNames: List<Pair<String, String>> = buildList {
+ val weekdays = DateFormatSymbols(Locale.getDefault()).weekdays
+ val shortWeekdays = DateFormatSymbols(Locale.getDefault()).shortWeekdays
+ // Skip the first item, as it's empty, and the second item, as it represents Sunday while it
+ // should be last according to ISO-8601.
+ weekdays.drop(2).forEachIndexed { index, day ->
+ add(Pair(day, shortWeekdays[index + 2]))
+ }
+ // Add Sunday to the end.
+ add(Pair(weekdays[1], shortWeekdays[1]))
+ }
+
+ override fun getDate(timeInMillis: Long): CalendarDate {
+ val calendar = Calendar.getInstance(utcTimeZone)
+ calendar.timeInMillis = timeInMillis
+ return CalendarDate(
+ year = calendar[Calendar.YEAR],
+ month = calendar[Calendar.MONTH] + 1,
+ dayOfMonth = calendar[Calendar.DAY_OF_MONTH],
+ utcTimeMillis = timeInMillis
+ )
+ }
+
+ override fun getMonth(timeInMillis: Long): CalendarMonth {
+ val firstDayCalendar = Calendar.getInstance(utcTimeZone)
+ firstDayCalendar.timeInMillis = timeInMillis
+ firstDayCalendar[Calendar.DAY_OF_MONTH] = 1
+ return getMonth(firstDayCalendar)
+ }
+
+ override fun getMonth(date: CalendarDate): CalendarMonth {
+ return getMonth(date.year, date.month)
+ }
+
+ override fun getMonth(year: Int, month: Int): CalendarMonth {
+ val firstDayCalendar = Calendar.getInstance(utcTimeZone)
+ firstDayCalendar.clear()
+ firstDayCalendar[Calendar.YEAR] = year
+ firstDayCalendar[Calendar.MONTH] = month - 1
+ firstDayCalendar[Calendar.DAY_OF_MONTH] = 1
+ return getMonth(firstDayCalendar)
+ }
+
+ override fun getDayOfWeek(date: CalendarDate): Int {
+ return dayInISO8601(date.toCalendar(TimeZone.getDefault())[Calendar.DAY_OF_WEEK])
+ }
+
+ override fun plusMonths(from: CalendarMonth, addedMonthsCount: Int): CalendarMonth {
+ if (addedMonthsCount <= 0) return from
+
+ val laterMonth = from.toCalendar()
+ laterMonth.add(Calendar.MONTH, addedMonthsCount)
+ return getMonth(laterMonth)
+ }
+
+ override fun minusMonths(from: CalendarMonth, subtractedMonthsCount: Int): CalendarMonth {
+ if (subtractedMonthsCount <= 0) return from
+
+ val earlierMonth = from.toCalendar()
+ earlierMonth.add(Calendar.MONTH, -subtractedMonthsCount)
+ return getMonth(earlierMonth)
+ }
+
+ override fun format(month: CalendarMonth, pattern: String): String {
+ val dateFormat = SimpleDateFormat(pattern, Locale.getDefault())
+ dateFormat.timeZone = utcTimeZone
+ dateFormat.isLenient = false
+ return dateFormat.format(month.toCalendar().timeInMillis)
+ }
+
+ override fun format(date: CalendarDate, pattern: String): String {
+ val dateFormat = SimpleDateFormat(pattern, Locale.getDefault())
+ dateFormat.timeZone = utcTimeZone
+ dateFormat.isLenient = false
+ return dateFormat.format(date.toCalendar(utcTimeZone).timeInMillis)
+ }
+
+ override fun parse(date: String, pattern: String): CalendarDate? {
+ val dateFormat = SimpleDateFormat(pattern)
+ dateFormat.timeZone = utcTimeZone
+ dateFormat.isLenient = false
+ return try {
+ val parsedDate = dateFormat.parse(date) ?: return null
+ val calendar = Calendar.getInstance(utcTimeZone)
+ calendar.time = parsedDate
+ CalendarDate(
+ year = calendar[Calendar.YEAR],
+ month = calendar[Calendar.MONTH] + 1,
+ dayOfMonth = calendar[Calendar.DAY_OF_MONTH],
+ utcTimeMillis = calendar.timeInMillis
+ )
+ } catch (pe: ParseException) {
+ null
+ }
+ }
+
+ /**
+ * Returns a given [Calendar] day number as a day representation under ISO-8601, where the first
+ * day is defined as Monday.
+ */
+ private fun dayInISO8601(day: Int): Int {
+ val shiftedDay = (day + 6) % 7
+ return if (shiftedDay == 0) return /* Sunday */ 7 else shiftedDay
+ }
+
+ private fun getMonth(firstDayCalendar: Calendar): CalendarMonth {
+ val difference = dayInISO8601(firstDayCalendar[Calendar.DAY_OF_WEEK]) - firstDayOfWeek
+ val daysFromStartOfWeekToFirstOfMonth = if (difference < 0) {
+ difference + DaysInWeek
+ } else {
+ difference
+ }
+ return CalendarMonth(
+ year = firstDayCalendar[Calendar.YEAR],
+ month = firstDayCalendar[Calendar.MONTH] + 1,
+ numberOfDays = firstDayCalendar.getActualMaximum(Calendar.DAY_OF_MONTH),
+ daysFromStartOfWeekToFirstOfMonth = daysFromStartOfWeekToFirstOfMonth,
+ startUtcTimeMillis = firstDayCalendar.timeInMillis
+ )
+ }
+
+ private fun CalendarMonth.toCalendar(): Calendar {
+ val calendar = Calendar.getInstance(utcTimeZone)
+ calendar.timeInMillis = this.startUtcTimeMillis
+ return calendar
+ }
+
+ private fun CalendarDate.toCalendar(timeZone: TimeZone): Calendar {
+ val calendar = Calendar.getInstance(timeZone)
+ calendar.clear()
+ calendar[Calendar.YEAR] = this.year
+ calendar[Calendar.MONTH] = this.month - 1
+ calendar[Calendar.DAY_OF_MONTH] = this.dayOfMonth
+ return calendar
+ }
+
+ private var utcTimeZone = TimeZone.getTimeZone("UTC")
+}
+
+/**
+ * A [CalendarModel] implementation for API >= 26.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@RequiresApi(Build.VERSION_CODES.O)
+internal class CalendarModelImpl : CalendarModel {
+
+ override val today
+ get(): CalendarDate {
+ val systemLocalDate = LocalDate.now()
+ return CalendarDate(
+ year = systemLocalDate.year,
+ month = systemLocalDate.monthValue,
+ dayOfMonth = systemLocalDate.dayOfMonth,
+ utcTimeMillis = systemLocalDate.atTime(LocalTime.MIDNIGHT)
+ .atZone(utcTimeZoneId).toInstant().toEpochMilli()
+ )
+ }
+
+ override val firstDayOfWeek: Int = WeekFields.of(Locale.getDefault()).firstDayOfWeek.value
+
+ override val weekdayNames: List<Pair<String, String>> =
+ // This will start with Monday as the first day, according to ISO-8601.
+ with(Locale.getDefault()) {
+ DayOfWeek.values().map {
+ it.getDisplayName(
+ TextStyle.FULL,
+ /* locale = */ this
+ ) to it.getDisplayName(
+ TextStyle.NARROW,
+ /* locale = */ this
+ )
+ }
+ }
+
+ override fun getDate(timeInMillis: Long): CalendarDate {
+ val localDate =
+ Instant.ofEpochMilli(timeInMillis).atZone(utcTimeZoneId).toLocalDate()
+ return CalendarDate(
+ year = localDate.year,
+ month = localDate.monthValue,
+ dayOfMonth = localDate.dayOfMonth,
+ utcTimeMillis = timeInMillis
+ )
+ }
+
+ override fun getMonth(timeInMillis: Long): CalendarMonth {
+ return getMonth(
+ Instant.ofEpochMilli(timeInMillis).atZone(utcTimeZoneId).toLocalDate()
+ )
+ }
+
+ override fun getMonth(date: CalendarDate): CalendarMonth {
+ return getMonth(LocalDate.of(date.year, date.month, 1))
+ }
+
+ override fun getMonth(year: Int, month: Int): CalendarMonth {
+ return getMonth(LocalDate.of(year, month, 1))
+ }
+
+ override fun getDayOfWeek(date: CalendarDate): Int {
+ return date.toLocalDate().dayOfWeek.value
+ }
+
+ override fun plusMonths(from: CalendarMonth, addedMonthsCount: Int): CalendarMonth {
+ if (addedMonthsCount <= 0) return from
+
+ val firstDayLocalDate = from.toLocalDate()
+ val laterMonth = firstDayLocalDate.plusMonths(addedMonthsCount.toLong())
+ return getMonth(laterMonth)
+ }
+
+ override fun minusMonths(from: CalendarMonth, subtractedMonthsCount: Int): CalendarMonth {
+ if (subtractedMonthsCount <= 0) return from
+
+ val firstDayLocalDate = from.toLocalDate()
+ val earlierMonth = firstDayLocalDate.minusMonths(subtractedMonthsCount.toLong())
+ return getMonth(earlierMonth)
+ }
+
+ override fun format(month: CalendarMonth, pattern: String): String {
+ val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern(pattern)
+ return month.toLocalDate().format(formatter)
+ }
+
+ override fun format(date: CalendarDate, pattern: String): String {
+ val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern(pattern)
+ return date.toLocalDate().format(formatter)
+ }
+
+ override fun parse(date: String, pattern: String): CalendarDate? {
+ // TODO: A DateTimeFormatter can be reused.
+ val formatter = DateTimeFormatter.ofPattern(pattern)
+ return try {
+ val localDate = LocalDate.parse(date, formatter)
+ CalendarDate(
+ year = localDate.year,
+ month = localDate.month.value,
+ dayOfMonth = localDate.dayOfMonth,
+ utcTimeMillis = localDate.atTime(LocalTime.MIDNIGHT)
+ .atZone(utcTimeZoneId).toInstant().toEpochMilli()
+ )
+ } catch (pe: DateTimeParseException) {
+ null
+ }
+ }
+
+ private fun getMonth(firstDayLocalDate: LocalDate): CalendarMonth {
+ val difference = firstDayLocalDate.dayOfWeek.value - firstDayOfWeek
+ val daysFromStartOfWeekToFirstOfMonth = if (difference < 0) {
+ difference + DaysInWeek
+ } else {
+ difference
+ }
+ val firstDayEpochMillis =
+ firstDayLocalDate.atTime(LocalTime.MIDNIGHT).atZone(utcTimeZoneId).toInstant()
+ .toEpochMilli()
+ return CalendarMonth(
+ year = firstDayLocalDate.year,
+ month = firstDayLocalDate.monthValue,
+ numberOfDays = firstDayLocalDate.lengthOfMonth(),
+ daysFromStartOfWeekToFirstOfMonth = daysFromStartOfWeekToFirstOfMonth,
+ startUtcTimeMillis = firstDayEpochMillis
+ )
+ }
+
+ private fun CalendarMonth.toLocalDate(): LocalDate {
+ return Instant.ofEpochMilli(startUtcTimeMillis).atZone(utcTimeZoneId).toLocalDate()
+ }
+
+ private fun CalendarDate.toLocalDate(): LocalDate {
+ return LocalDate.of(
+ this.year,
+ this.month,
+ this.dayOfMonth
+ )
+ }
+
+ private val utcTimeZoneId = ZoneId.of("UTC")
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/CalendarModel.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/CalendarModel.kt
new file mode 100644
index 0000000..54cbd7b
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/CalendarModel.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+@ExperimentalMaterial3Api
+internal interface CalendarModel {
+
+ /**
+ * A [CalendarDate] representing the current day.
+ */
+ val today: CalendarDate
+
+ /**
+ * Hold the first day of the week at the current `Locale` as an integer. The integer value
+ * follows the ISO-8601 standard and refer to Monday as 1, and Sunday as 7.
+ */
+ val firstDayOfWeek: Int
+
+ /**
+ * Holds a list of weekday names, starting from Monday as the first day in the list.
+ *
+ * Each item in this list is a [Pair] that holds the full name of the day, and its short
+ * abbreviation letter(s).
+ *
+ * Newer APIs (i.e. API 26+), a [Pair] will hold a full name and the first letter of the
+ * day.
+ * Older APIs that predate API 26 will hold a full name and the first three letters of the day.
+ */
+ val weekdayNames: List<Pair<String, String>>
+
+ /**
+ * Returns a [CalendarDate] from a given _UTC_ time in milliseconds.
+ *
+ * @param timeInMillis UTC milliseconds from the epoch
+ */
+ fun getDate(timeInMillis: Long): CalendarDate
+
+ /**
+ * Returns a [CalendarMonth] from a given _UTC_ time in milliseconds.
+ *
+ * @param timeInMillis UTC milliseconds from the epoch for the first day the month
+ */
+ fun getMonth(timeInMillis: Long): CalendarMonth
+
+ /**
+ * Returns a [CalendarMonth] from a given [CalendarDate].
+ *
+ * Note: This function ignores the [CalendarDate.dayOfMonth] value and just uses the date's
+ * year and month to resolve a [CalendarMonth].
+ *
+ * @param date a [CalendarDate] to resolve into a month
+ */
+ fun getMonth(date: CalendarDate): CalendarMonth
+
+ /**
+ * Returns a [CalendarMonth] from a given [year] and [month].
+ *
+ * @param year the month's year
+ * @param month an integer representing a month (e.g. JANUARY as 1, December as 12)
+ */
+ fun getMonth(year: Int, /* @IntRange(from = 1, to = 12) */ month: Int): CalendarMonth
+
+ /**
+ * Returns a day of week from a given [CalendarDate].
+ *
+ * @param date a [CalendarDate] to resolve
+ */
+ fun getDayOfWeek(date: CalendarDate): Int
+
+ /**
+ * Returns a [CalendarMonth] that is computed by adding a number of months, given as
+ * [addedMonthsCount], to a given month.
+ *
+ * @param from the [CalendarMonth] to add to
+ * @param addedMonthsCount the number of months to add
+ */
+ fun plusMonths(from: CalendarMonth, addedMonthsCount: Int): CalendarMonth
+
+ /**
+ * Returns a [CalendarMonth] that is computed by subtracting a number of months, given as
+ * [subtractedMonthsCount], from a given month.
+ *
+ * @param from the [CalendarMonth] to subtract from
+ * @param subtractedMonthsCount the number of months to subtract
+ */
+ fun minusMonths(from: CalendarMonth, subtractedMonthsCount: Int): CalendarMonth
+
+ /**
+ * Formats a [CalendarMonth] into a string with a given date format pattern.
+ *
+ * @param month a [CalendarMonth] to format
+ * @param pattern a date format pattern
+ */
+ fun format(month: CalendarMonth, pattern: String): String
+
+ /**
+ * Formats a [CalendarDate] into a string with a given date format pattern.
+ *
+ * @param date a [CalendarDate] to format
+ * @param pattern a date format pattern
+ */
+ fun format(date: CalendarDate, pattern: String): String
+
+ /**
+ * Parses a date string into a [CalendarDate].
+ *
+ * @param date a date string
+ * @param pattern the expected date pattern to be used for parsing the date string
+ * @return a [CalendarDate], or a `null` in case the parsing failed
+ */
+ fun parse(date: String, pattern: String): CalendarDate?
+}
+
+/**
+ * Represents a calendar date.
+ *
+ * @param year the date's year
+ * @param month the date's month
+ * @param dayOfMonth the date's day of month
+ * @param utcTimeMillis the date representation in _UTC_ milliseconds from the epoch
+ */
+@ExperimentalMaterial3Api
+internal data class CalendarDate(
+ val year: Int,
+ val month: Int,
+ val dayOfMonth: Int,
+ val utcTimeMillis: Long
+) : Comparable<CalendarDate> {
+ override operator fun compareTo(other: CalendarDate): Int =
+ this.utcTimeMillis.compareTo(other.utcTimeMillis)
+}
+
+/**
+ * Represents a calendar month.
+ *
+ * @param year the month's year
+ * @param month the calendar month as an integer (e.g. JANUARY as 1, December as 12)
+ * @param numberOfDays the number of days in the month
+ * @param daysFromStartOfWeekToFirstOfMonth the number of days from the start of the week to the
+ * first day of the month
+ * @param startUtcTimeMillis the first day of the month in _UTC_ milliseconds from the epoch
+ */
+@ExperimentalMaterial3Api
+internal data class CalendarMonth(
+ val year: Int,
+ val month: Int,
+ val numberOfDays: Int,
+ val daysFromStartOfWeekToFirstOfMonth: Int,
+ val startUtcTimeMillis: Long
+) {
+
+ /**
+ * The last _UTC_ milliseconds from the epoch of the month (i.e. the last millisecond of the
+ * last day of the month)
+ */
+ val endUtcTimeMillis: Long = startUtcTimeMillis + (numberOfDays * MillisecondsIn24Hours) - 1
+
+ /**
+ * Returns the position of a [CalendarMonth] within given years range.
+ */
+ internal fun indexIn(years: IntRange): Int {
+ return (year - years.first) * 12 + month - 1
+ }
+}
+
+internal const val DaysInWeek: Int = 7
+internal const val MillisecondsIn24Hours = 86400000L
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
index d7bd6ef..4849ec9 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
@@ -369,7 +369,13 @@
val distance = abs(currentAnchor - currentAnchors.getValue(lower))
val relativeThreshold = abs(positionalThreshold(density, distance))
val absoluteThreshold = abs(currentAnchor - relativeThreshold)
- if (offset > absoluteThreshold) currentState else lower
+ if (offset < 0) {
+ // For negative offsets, larger absolute thresholds are closer to lower anchors
+ // than smaller ones.
+ if (abs(offset) < absoluteThreshold) currentState else lower
+ } else {
+ if (offset > absoluteThreshold) currentState else lower
+ }
}
}
}
diff --git a/compose/runtime/runtime/api/current.ignore b/compose/runtime/runtime/api/current.ignore
index 801ddf6..e6959b9 100644
--- a/compose/runtime/runtime/api/current.ignore
+++ b/compose/runtime/runtime/api/current.ignore
@@ -1,4 +1,6 @@
// Baseline format: 1.0
+AddedAbstractMethod: androidx.compose.runtime.Composer#disableSourceInformation():
+ Added method androidx.compose.runtime.Composer.disableSourceInformation()
AddedAbstractMethod: androidx.compose.runtime.Composer#endToMarker(int):
Added method androidx.compose.runtime.Composer.endToMarker(int)
AddedAbstractMethod: androidx.compose.runtime.Composer#getCurrentMarker():
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 4210434..4e98ddb 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -113,6 +113,7 @@
method @androidx.compose.runtime.ComposeCompilerApi public <T> void createNode(kotlin.jvm.functions.Function0<? extends T> factory);
method @androidx.compose.runtime.ComposeCompilerApi public void deactivateToEndGroup(boolean changed);
method @androidx.compose.runtime.ComposeCompilerApi public void disableReusing();
+ method public void disableSourceInformation();
method @androidx.compose.runtime.ComposeCompilerApi public void enableReusing();
method @androidx.compose.runtime.ComposeCompilerApi public void endDefaults();
method @androidx.compose.runtime.ComposeCompilerApi public void endMovableGroup();
@@ -984,14 +985,18 @@
@kotlin.jvm.JvmDefaultWithCompatibility public interface CompositionGroup extends androidx.compose.runtime.tooling.CompositionData {
method public Iterable<java.lang.Object> getData();
+ method public default int getGroupSize();
method public default Object? getIdentity();
method public Object getKey();
method public Object? getNode();
+ method public default int getSlotsSize();
method public String? getSourceInfo();
property public abstract Iterable<java.lang.Object> data;
+ property public default int groupSize;
property public default Object? identity;
property public abstract Object key;
property public abstract Object? node;
+ property public default int slotsSize;
property public abstract String? sourceInfo;
}
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index ce05da6..3688ea9 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -120,6 +120,7 @@
method @androidx.compose.runtime.ComposeCompilerApi public <T> void createNode(kotlin.jvm.functions.Function0<? extends T> factory);
method @androidx.compose.runtime.ComposeCompilerApi public void deactivateToEndGroup(boolean changed);
method @androidx.compose.runtime.ComposeCompilerApi public void disableReusing();
+ method public void disableSourceInformation();
method @androidx.compose.runtime.ComposeCompilerApi public void enableReusing();
method @androidx.compose.runtime.ComposeCompilerApi public void endDefaults();
method @androidx.compose.runtime.ComposeCompilerApi public void endMovableGroup();
@@ -1060,14 +1061,18 @@
@kotlin.jvm.JvmDefaultWithCompatibility public interface CompositionGroup extends androidx.compose.runtime.tooling.CompositionData {
method public Iterable<java.lang.Object> getData();
+ method public default int getGroupSize();
method public default Object? getIdentity();
method public Object getKey();
method public Object? getNode();
+ method public default int getSlotsSize();
method public String? getSourceInfo();
property public abstract Iterable<java.lang.Object> data;
+ property public default int groupSize;
property public default Object? identity;
property public abstract Object key;
property public abstract Object? node;
+ property public default int slotsSize;
property public abstract String? sourceInfo;
}
diff --git a/compose/runtime/runtime/api/restricted_current.ignore b/compose/runtime/runtime/api/restricted_current.ignore
index 801ddf6..e6959b9 100644
--- a/compose/runtime/runtime/api/restricted_current.ignore
+++ b/compose/runtime/runtime/api/restricted_current.ignore
@@ -1,4 +1,6 @@
// Baseline format: 1.0
+AddedAbstractMethod: androidx.compose.runtime.Composer#disableSourceInformation():
+ Added method androidx.compose.runtime.Composer.disableSourceInformation()
AddedAbstractMethod: androidx.compose.runtime.Composer#endToMarker(int):
Added method androidx.compose.runtime.Composer.endToMarker(int)
AddedAbstractMethod: androidx.compose.runtime.Composer#getCurrentMarker():
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 6fcdd3c..5b1424f 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -115,6 +115,7 @@
method @androidx.compose.runtime.ComposeCompilerApi public <T> void createNode(kotlin.jvm.functions.Function0<? extends T> factory);
method @androidx.compose.runtime.ComposeCompilerApi public void deactivateToEndGroup(boolean changed);
method @androidx.compose.runtime.ComposeCompilerApi public void disableReusing();
+ method public void disableSourceInformation();
method @androidx.compose.runtime.ComposeCompilerApi public void enableReusing();
method @androidx.compose.runtime.ComposeCompilerApi public void endDefaults();
method @androidx.compose.runtime.ComposeCompilerApi public void endMovableGroup();
@@ -1025,14 +1026,18 @@
@kotlin.jvm.JvmDefaultWithCompatibility public interface CompositionGroup extends androidx.compose.runtime.tooling.CompositionData {
method public Iterable<java.lang.Object> getData();
+ method public default int getGroupSize();
method public default Object? getIdentity();
method public Object getKey();
method public Object? getNode();
+ method public default int getSlotsSize();
method public String? getSourceInfo();
property public abstract Iterable<java.lang.Object> data;
+ property public default int groupSize;
property public default Object? identity;
property public abstract Object key;
property public abstract Object? node;
+ property public default int slotsSize;
property public abstract String? sourceInfo;
}
diff --git a/compose/runtime/runtime/integration-tests/build.gradle b/compose/runtime/runtime/integration-tests/build.gradle
index 36e4ef4..b9b65e5 100644
--- a/compose/runtime/runtime/integration-tests/build.gradle
+++ b/compose/runtime/runtime/integration-tests/build.gradle
@@ -107,3 +107,75 @@
freeCompilerArgs += "-Xcontext-receivers"
}
}
+
+public File findFile() {
+ project.file("src/androidAndroidTest/kotlin/androidx/compose/runtime/GroupSizeTests.kt")
+}
+
+class UpdateExpectedGroupSizes extends DefaultTask {
+ @Internal
+ File source
+
+ @Internal
+ String sizes
+
+ @TaskAction
+ def exec() {
+ def newExpected = sizes.split(",")
+ if (newExpected.length != 3) {
+ if (newExpected.length < 3)
+ parameterError("Not enough parameters")
+ parameterError("Too many parameters")
+ }
+ if (!newExpected[1].isInteger()) {
+ parameterError("Groups field is not an integer")
+ }
+ if (!newExpected[1].isInteger()) {
+ parameterError("Slots field is not an integer")
+ }
+ def testName = newExpected[0]
+ def newGroups = newExpected[1] as Integer
+ def newSlots = newExpected[2] as Integer
+
+ def lines = source.readLines()
+ def modified = false
+
+ def namePattern = "\"$testName\""
+ for (int i = 0; i < lines.size(); i++) {
+ String line = lines[i]
+ if (line.contains(namePattern)) {
+ def newGroupsIndex = lines[i + 1].indexOf("noMoreGroupsThan")
+ if (newGroupsIndex < 0) error("Group line not found for test $namePattern")
+ lines[i + 1] = lines[i + 1].replaceFirst(/[0-9]+/, "$newGroups")
+ def newSlotsIndex = lines[i + 2].indexOf("noMoreSlotsThan")
+ if (newSlotsIndex < 0) error("Group line not found for test $namePattern")
+ lines[i + 2] = lines[i + 2].replaceFirst(/[0-9]+/, "$newSlots")
+ modified = true
+ }
+ }
+ if (!modified) error("Could not find test $namePattern")
+
+ // Update the file
+ def writer = source.newWriter()
+ lines.forEach {line ->
+ writer.write("$line\n")
+ }
+ writer.close()
+ }
+
+ def parameterError(String message) {
+ error("$message, expected newExpectedGroups to look like " +
+ "<testsName>,<newGroups>,<newSlots>")
+ }
+
+ def error(String message) {
+ throw new GradleException(message)
+ }
+}
+
+afterEvaluate {
+ tasks.register("updateExpectedGroupSizes", UpdateExpectedGroupSizes) { task ->
+ task.source = findFile()
+ task.sizes = project.findProperty("compose.newExpectedSizes")
+ }
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/GroupSizeTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/GroupSizeTests.kt
new file mode 100644
index 0000000..4731f73
--- /dev/null
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/GroupSizeTests.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.material.Checkbox
+import androidx.compose.material.Text
+import androidx.compose.runtime.tooling.CompositionData
+import androidx.compose.runtime.tooling.CompositionGroup
+import androidx.compose.ui.Modifier
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class GroupSizeTests : BaseComposeTest() {
+ @get:Rule
+ override val activityRule = makeTestActivityRule()
+
+ @Test
+ @MediumTest
+ @Ignore("Only run explicitly to check framework")
+ fun spacerSize() {
+ slotExpect(
+ "spacerSize",
+ noMoreGroupsThan = 8,
+ noMoreSlotsThan = 10
+ ) {
+ Spacer(Modifier)
+ }
+ }
+
+ @Test
+ @MediumTest
+ @Ignore("Only run explicitly to check framework")
+ fun checkboxSize() {
+ slotExpect(
+ "checkboxSize",
+ noMoreGroupsThan = 154,
+ noMoreSlotsThan = 179,
+ ) {
+ Checkbox(true, })
+ }
+ }
+
+ @Test
+ @MediumTest
+ @Ignore("Only run explicitly to check framework")
+ fun textSize() {
+ slotExpect(
+ "textSize",
+ noMoreGroupsThan = 13,
+ noMoreSlotsThan = 12
+ ) {
+ Text("")
+ }
+ }
+
+ @Test
+ @MediumTest
+ @Ignore("Only run explicitly to check framework")
+ fun boxSize() {
+ slotExpect(
+ "boxSize",
+ noMoreGroupsThan = 9,
+ noMoreSlotsThan = 10
+ ) {
+ Box { }
+ }
+ }
+
+ @Test
+ @MediumTest
+ @Ignore("Only run explicitly to check framework")
+ fun buttonSize() {
+ slotExpect(
+ "buttonSize",
+ noMoreGroupsThan = 165,
+ noMoreSlotsThan = 193,
+ ) {
+ androidx.compose.material.Button({ }) {
+ Text("Click me")
+ }
+ }
+ }
+
+ @Test
+ @MediumTest
+ @Ignore("Only run explicitly to check framework")
+ fun columnSize() {
+ slotExpect(
+ "columnSize",
+ noMoreGroupsThan = 9,
+ noMoreSlotsThan = 13
+ ) {
+ Column { }
+ }
+ }
+
+ private fun slotExpect(
+ name: String,
+ noMoreGroupsThan: Int,
+ noMoreSlotsThan: Int,
+ content: @Composable () -> Unit
+ ) {
+ var compositionData: CompositionData? = null
+ compose {
+ compositionData = currentComposer.compositionData
+ currentComposer.disableSourceInformation()
+ Marker { content() }
+ }.then {
+ val group = findMarkerGroup(compositionData!!)
+ val receivedGroups = group.groupSize
+ val receivedSlots = group.slotsSize
+
+ if (receivedGroups > noMoreGroupsThan || receivedSlots > noMoreSlotsThan) {
+ error("Expected $noMoreGroupsThan groups and $noMoreSlotsThan slots " +
+ "but received $receivedGroups and $receivedSlots\n" +
+ "If this was expected execute the gradlew command:\n ${
+ updateTestCommand(name, receivedGroups, receivedSlots)
+ }"
+ )
+ }
+ if (receivedSlots < noMoreSlotsThan || receivedGroups < noMoreGroupsThan) {
+ println(
+ "WARNING: Improvement detected. Update test GroupSizeTests.$name to\n" +
+ "If this was expected, running the gradle command:\n\n" +
+ " ${updateTestCommand(name, receivedGroups, receivedSlots)}\n\n" +
+ "is recommended"
+ )
+ }
+ }
+ }
+}
+
+private fun updateTestCommand(name: String, newGroups: Int, newSlots: Int) =
+ "./gradlew -P \"compose.newExpectedSizes=$name,$newGroups,$newSlots\" " +
+ ":compose:runtime:runtime:integration-test:updateExpectedGroupSizes"
+
+private const val MarkerGroup = -441660990
+
+private fun findMarkerGroup(compositionData: CompositionData): CompositionGroup {
+ fun findGroup(groups: Iterable<CompositionGroup>, key: Int): CompositionGroup? {
+ for (group in groups) {
+ if (group.key == key) return group
+ findGroup(group.compositionGroups, key)?.let { return it }
+ }
+ return null
+ }
+
+ return findGroup(compositionData.compositionGroups, MarkerGroup)
+ ?.compositionGroups
+ ?.firstOrNull()
+ ?: error("Could not find marker")
+}
+
+@Composable
+private inline fun Marker(content: @Composable () -> Unit) = content()
+
+// left unused for debugging. This is useful for debugging differences in the slot table
+@Suppress("unused")
+private fun CompositionGroup.asString(): String {
+ fun stringOf(group: CompositionGroup, indent: String): String =
+ "$indent ${group.key} ${group.groupSize}:${group.slotsSize}:\n${
+ group.compositionGroups.joinToString("") {
+ stringOf(it, "$indent ")
+ }}"
+ return stringOf(this, "")
+}
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 dcbc8dc..03e25a0 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
@@ -1058,6 +1058,8 @@
val composition: ControlledComposition
@TestOnly get
+ fun disableSourceInformation()
+
companion object {
/**
* A special value used to represent no value was stored (e.g. an empty slot). This is
@@ -1254,6 +1256,7 @@
private var childrenComposing: Int = 0
private var snapshot = currentSnapshot()
private var compositionToken: Int = 0
+ private var sourceInformationEnabled = true
private val invalidateStack = Stack<RecomposeScopeImpl>()
@@ -3202,19 +3205,25 @@
@ComposeCompilerApi
override fun sourceInformation(sourceInformation: String) {
- if (inserting) {
+ if (inserting && sourceInformationEnabled) {
writer.insertAux(sourceInformation)
}
}
@ComposeCompilerApi
override fun sourceInformationMarkerStart(key: Int, sourceInformation: String) {
- start(key, objectKey = null, isNode = false, data = sourceInformation)
+ if (sourceInformationEnabled)
+ start(key, objectKey = null, isNode = false, data = sourceInformation)
}
@ComposeCompilerApi
override fun sourceInformationMarkerEnd() {
- end(isNode = false)
+ if (sourceInformationEnabled)
+ end(isNode = false)
+ }
+
+ override fun disableSourceInformation() {
+ sourceInformationEnabled = false
}
/**
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
index 3ce6db5..bff9d04 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
@@ -3025,6 +3025,16 @@
)
}
+ override val groupSize: Int get() = table.groups.groupSize(group)
+
+ override val slotsSize: Int
+ get() {
+ val nextGroup = group + groupSize
+ val nextSlot = if (nextGroup < table.groupsSize) table.groups.dataAnchor(nextGroup)
+ else table.slotsSize
+ return nextSlot - table.groups.dataAnchor(group)
+ }
+
private fun validateRead() {
if (table.version != version) {
throw ConcurrentModificationException()
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/CompositionData.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/CompositionData.kt
index 16e9076..0bad0ed 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/CompositionData.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/CompositionData.kt
@@ -92,4 +92,11 @@
*/
val identity: Any?
get() = null
-}
\ No newline at end of file
+
+ /**
+ * The total number of groups, including itself, that this group contains.
+ */
+ val groupSize: Int get() = 0
+
+ val slotsSize: Int get() = 0
+}
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
new file mode 100644
index 0000000..8945723
--- /dev/null
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
@@ -0,0 +1,629 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("RemoveEmptyClassBody")
+
+package androidx.compose.runtime
+
+import androidx.compose.runtime.mock.CompositionTestScope
+import androidx.compose.runtime.mock.compositionTest
+import androidx.compose.runtime.tooling.CompositionData
+import androidx.compose.runtime.tooling.CompositionGroup
+import androidx.compose.runtime.mock.View
+
+import kotlin.test.Test
+
+class GroupSizeValidationTests {
+
+ @Test
+ fun spacerLike() = compositionTest {
+ slotExpect(
+ name = "SpacerLike",
+ noMoreGroupsThan = 6,
+ noMoreSlotsThan = 9,
+ ) {
+ SpacerLike(Modifier)
+ }
+ }
+
+ @Test
+ fun columnLikeSize() = compositionTest {
+ slotExpect(
+ name = "ColumnLike",
+ noMoreGroupsThan = 9,
+ noMoreSlotsThan = 8,
+ ) {
+ ColumnLike { }
+ }
+ }
+
+ @Test
+ fun textLikeSize() = compositionTest {
+ slotExpect(
+ name = "TextLike",
+ noMoreGroupsThan = 13,
+ noMoreSlotsThan = 15
+ ) {
+ TextLike("")
+ }
+ }
+
+ @Test
+ fun checkboxLike() = compositionTest {
+ slotExpect(
+ name = "CheckboxLike",
+ noMoreGroupsThan = 13,
+ noMoreSlotsThan = 20
+ ) {
+ CheckboxLike(checked = false, })
+ }
+ }
+}
+
+// The following are a sketch of how compose ui uses composition to produce some important
+// composable functions. These are derived from the implementation as of Oct 2022.
+
+// The slot usage should be validated against the actual usage in GroupSizeTests in the
+// integration-tests periodically to avoid these skewing too far.
+
+@Stable
+private fun interface MeasurePolicy {
+ fun measure()
+}
+
+private val LocalDensity = staticCompositionLocalOf { 0 }
+private val LocalLayoutDirection = staticCompositionLocalOf { 0 }
+private val LocalViewConfiguration = staticCompositionLocalOf { 0 }
+
+private object ViewHelper {
+ val Constructor = ::View
+ val SetModifier: View.(Modifier) -> Unit = { attributes["modifier"] = it }
+ val SetMeasurePolicy: View.(MeasurePolicy) -> Unit = { attributes["measurePolicy"] = it }
+ val SetDensity: View.(Int) -> Unit = { attributes["density"] = it }
+ val SetLayoutDirection: View.(Int) -> Unit = { attributes["layoutDirection"] = it }
+ val SetViewConfiguration: View.(Int) -> Unit = { attributes["viewConfiguration"] = it }
+}
+
+@Composable
+private inline fun LayoutLike(
+ content: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ measurePolicy: MeasurePolicy
+) {
+ val density = LocalDensity.current
+ val layoutDirection = LocalLayoutDirection.current
+ val viewConfiguration = LocalViewConfiguration.current
+ ReusableComposeNode<View, Applier<Any>>(
+ factory = ViewHelper.Constructor,
+ update = {
+ set(modifier, ViewHelper.SetModifier)
+ set(measurePolicy, ViewHelper.SetMeasurePolicy)
+ set(density, ViewHelper.SetDensity)
+ set(layoutDirection, ViewHelper.SetLayoutDirection)
+ set(viewConfiguration, ViewHelper.SetViewConfiguration)
+ },
+ content = content
+ )
+}
+
+@Composable
+@NonRestartableComposable
+private fun LayoutLike(modifier: Modifier, measurePolicy: MeasurePolicy) {
+ val density = LocalDensity.current
+ val layoutDirection = LocalLayoutDirection.current
+ val viewConfiguration = LocalViewConfiguration.current
+ ReusableComposeNode<View, Applier<Any>>(
+ factory = ViewHelper.Constructor,
+ update = {
+ set(modifier, ViewHelper.SetModifier)
+ set(measurePolicy, ViewHelper.SetMeasurePolicy)
+ set(density, ViewHelper.SetDensity)
+ set(layoutDirection, ViewHelper.SetLayoutDirection)
+ set(viewConfiguration, ViewHelper.SetViewConfiguration)
+ }
+ )
+}
+
+@Stable
+private interface Modifier {
+ companion object : Modifier
+}
+
+@Immutable
+private object Arrangement {
+ @Stable
+ interface Vertical
+
+ @Stable
+ val Top = object : Vertical { }
+}
+
+@Immutable
+private object Alignment {
+ @Stable
+ interface Horizontal
+
+ @Stable
+ val Start = object : Horizontal { }
+}
+
+private object SpacerMeasurePolicy : MeasurePolicy {
+ override fun measure() { }
+}
+
+@Composable
+private fun SpacerLike(modifier: Modifier) {
+ LayoutLike(measurePolicy = SpacerMeasurePolicy, modifier = modifier)
+}
+
+@Immutable
+private interface ColumnScope
+
+private object ColumnScopeInstance : ColumnScope
+
+// A stable version of Column used for group size tests
+@Composable
+private inline fun ColumnLike(
+ modifier: Modifier = Modifier,
+ verticalArrangement: Arrangement.Vertical = Arrangement.Top,
+ horizontalAlignment: Alignment.Horizontal = Alignment.Start,
+ content: @Composable ColumnScope.() -> Unit
+) {
+ val measurePolicy =
+ columnMeasurePolicy(verticalArrangement, horizontalAlignment)
+ LayoutLike(
+ content = { ColumnScopeInstance.content() },
+ measurePolicy = measurePolicy,
+ modifier = modifier
+ )
+}
+
+private object DefaultColumnRowMeasurePolicy : MeasurePolicy {
+ override fun measure() { }
+}
+
+@Composable private fun columnMeasurePolicy(
+ verticalArrangement: Arrangement.Vertical,
+ horizontalAlignment: Alignment.Horizontal
+) = if (verticalArrangement == Arrangement.Top && horizontalAlignment == Alignment.Start) {
+ DefaultColumnRowMeasurePolicy
+} else {
+ remember(verticalArrangement, horizontalAlignment) {
+ DefaultColumnRowMeasurePolicy
+ }
+}
+
+@Immutable
+@JvmInline
+private value class Color(val value: ULong) {
+
+ @Stable
+ @Suppress("UNUSED_PARAMETER")
+ fun copy(
+ alpha: Float = 0f,
+ red: Float = 0f,
+ green: Float = 0f,
+ blue: Float = 0f
+ ): Color = this
+
+ companion object {
+ @Stable
+ val Unspecified = Color(0u)
+ }
+}
+private val Color.isSpecified: Boolean get() = this != Color.Unspecified
+
+private inline fun Color.takeOrElse(block: () -> Color): Color = if (isSpecified) this else block()
+
+@Immutable
+@JvmInline
+private value class TextUnit(val packedValue: Long) {
+ companion object {
+ @Stable
+ val Unspecified = TextUnit(0)
+ }
+}
+
+@JvmInline
+value class FontStyle(val value: Int)
+
+@Immutable
+@Suppress("unused")
+private class FontWeight(val weight: Int)
+
+@Immutable
+@Suppress("UNUSED_PARAMETER")
+private sealed class FontFamily(canLoadSynchronously: Boolean) {
+ @Stable
+ interface Resolver {
+ companion object {
+ val Default = object : Resolver { }
+ }
+ }
+}
+
+private val LocalFontFamilyResolver = staticCompositionLocalOf { FontFamily.Resolver.Default }
+
+@Immutable
+@Suppress("unused")
+private class TextDecoration(val mask: Int)
+
+@JvmInline
+@Suppress("unused")
+private value class TextAlign(val value: Int)
+
+@JvmInline
+private value class TextOverflow(val value: Int) {
+ companion object {
+ @Stable
+ val Clip = TextOverflow(1)
+ }
+}
+
+private class TextLayoutResult
+
+@Immutable
+@Suppress("unused")
+private class TextStyle(
+ val color: Color = Color.Unspecified,
+ val fontSize: TextUnit = TextUnit.Unspecified,
+ val fontWeight: FontWeight? = null,
+ val textAlign: TextAlign? = null,
+ val lineHeight: TextUnit = TextUnit.Unspecified,
+ val fontFamily: FontFamily? = null,
+ val textDecoration: TextDecoration? = null,
+ val fontStyle: FontStyle? = null,
+ val letterSpacing: TextUnit = TextUnit.Unspecified
+) {
+ @Stable
+ @Suppress("UNUSED_PARAMETER")
+ fun merge(other: TextStyle? = null) = this
+
+ companion object {
+ val Default = TextStyle()
+ }
+}
+
+private interface SelectionRegistrar {
+ fun nextSelectableId(): Long
+
+ companion object {
+ const val InvalidSelectableId = 0L
+ }
+}
+
+private object DefaultSelectionRegister : SelectionRegistrar {
+ override fun nextSelectableId() = 0L
+}
+
+private val DefaultTextStyle = TextStyle()
+private val LocalTextStyle = staticCompositionLocalOf { DefaultTextStyle }
+private val LocalContentColor = staticCompositionLocalOf { Color.Unspecified }
+private val LocalContentAlpha = staticCompositionLocalOf { 1f }
+private val LocalSelectionRegistrar = staticCompositionLocalOf<SelectionRegistrar?> {
+ DefaultSelectionRegister
+}
+
+@Composable
+private fun TextLike(
+ text: String,
+ modifier: Modifier = Modifier,
+ color: Color = Color.Unspecified,
+ fontSize: TextUnit = TextUnit.Unspecified,
+ fontStyle: FontStyle? = null,
+ fontWeight: FontWeight? = null,
+ fontFamily: FontFamily? = null,
+ letterSpacing: TextUnit = TextUnit.Unspecified,
+ textDecoration: TextDecoration? = null,
+ textAlign: TextAlign? = null,
+ lineHeight: TextUnit = TextUnit.Unspecified,
+ overflow: TextOverflow = TextOverflow.Clip,
+ softWrap: Boolean = true,
+ maxLines: Int = Int.MAX_VALUE,
+ minLines: Int = 1,
+ onTextLayout: (TextLayoutResult) -> Unit = {},
+ style: TextStyle = LocalTextStyle.current
+) {
+
+ val textColor = color.takeOrElse {
+ style.color.takeOrElse {
+ LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
+ }
+ }
+
+ val mergedStyle = style.merge(
+ TextStyle(
+ color = textColor,
+ fontSize = fontSize,
+ fontWeight = fontWeight,
+ textAlign = textAlign,
+ lineHeight = lineHeight,
+ fontFamily = fontFamily,
+ textDecoration = textDecoration,
+ fontStyle = fontStyle,
+ letterSpacing = letterSpacing
+ )
+ )
+
+ BasicTextLike(
+ text = text,
+ modifier = modifier,
+ style = mergedStyle,
+ >
+ overflow = overflow,
+ softWrap = softWrap,
+ maxLines = maxLines,
+ minLines = minLines
+ )
+}
+
+private fun CompositionTestScope.slotExpect(
+ name: String,
+ noMoreGroupsThan: Int,
+ noMoreSlotsThan: Int,
+ content: @Composable () -> Unit
+) {
+ var compositionData: CompositionData? = null
+ compose {
+ compositionData = currentComposer.compositionData
+ currentComposer.disableSourceInformation()
+ Marker { content() }
+ }
+
+ val group = findMarkerGroup(compositionData!!)
+ val receivedGroups = group.groupSize
+ val receivedSlots = group.slotsSize
+
+ if (receivedGroups > noMoreGroupsThan || receivedSlots > noMoreSlotsThan) {
+ error("Expected $noMoreGroupsThan groups and $noMoreSlotsThan slots " +
+ "but received $receivedGroups and $receivedSlots\n"
+ )
+ }
+ if (receivedSlots < noMoreSlotsThan || receivedGroups < noMoreGroupsThan) {
+ println(
+ "WARNING: Improvement detected. Update test GroupSizeTests.$name to " +
+ "$receivedGroups groups and $receivedSlots slots"
+ )
+ }
+}
+
+@Suppress("unused")
+private class AnnotatedString(val text: String)
+
+@Suppress("unused")
+private class TextState(
+ val textDelegate: TextDelegate,
+ val selectionId: Long
+) {
+ var selectionBackgroundColor: Color = Color.Unspecified
+ var onTextLayout: (TextLayoutResult) -> Unit = { }
+}
+
+@Suppress("unused")
+private class TextDelegate(
+ val text: AnnotatedString,
+ val style: TextStyle,
+ val maxLines: Int = Int.MAX_VALUE,
+ val minLines: Int = 1,
+ val softWrap: Boolean = true,
+ val overflow: TextOverflow = TextOverflow.Clip,
+ val density: Int,
+ val fontFamilyResolver: FontFamily.Resolver
+)
+
+@Suppress("UNUSED_PARAMETER")
+private fun updateTextDelegate(
+ current: TextDelegate,
+ text: String,
+ style: TextStyle,
+ density: Int,
+ fontFamilyResolver: FontFamily.Resolver,
+ softWrap: Boolean = true,
+ overflow: TextOverflow = TextOverflow.Clip,
+ maxLines: Int = Int.MAX_VALUE,
+ minLines: Int = 1,
+): TextDelegate = current
+
+@Suppress("UNUSED_PARAMETER")
+private class TextController(val state: TextState) {
+ val measurePolicy: MeasurePolicy = DefaultColumnRowMeasurePolicy
+ fun setTextDelegate(updateTextDelegate: TextDelegate) { }
+ fun update(selectionRegistrar: SelectionRegistrar?) { }
+}
+
+@Immutable
+@Suppress("unused")
+private class TextSelectionColors(
+ val handleColor: Color,
+ val backgroundColor: Color
+) {
+ companion object {
+ val Default = TextSelectionColors(Color.Unspecified, Color.Unspecified)
+ }
+}
+
+private val LocalTextSelectionColors = staticCompositionLocalOf { TextSelectionColors.Default }
+
+@Composable
+private fun BasicTextLike(
+ text: String,
+ modifier: Modifier = Modifier,
+ style: TextStyle = TextStyle.Default,
+ onTextLayout: (TextLayoutResult) -> Unit = {},
+ overflow: TextOverflow = TextOverflow.Clip,
+ softWrap: Boolean = true,
+ maxLines: Int = Int.MAX_VALUE,
+ minLines: Int = 1
+) {
+ // selection registrar, if no SelectionContainer is added ambient value will be null
+ val selectionRegistrar = LocalSelectionRegistrar.current
+ val density = LocalDensity.current
+ val fontFamilyResolver = LocalFontFamilyResolver.current
+
+ // The ID used to identify this CoreText. If this CoreText is removed from the composition
+ // tree and then added back, this ID should stay the same.
+ // Notice that we need to update selectable ID when the input text or selectionRegistrar has
+ // been updated.
+ // When text is updated, the selection on this CoreText becomes invalid. It can be treated
+ // as a brand new CoreText.
+ // When SelectionRegistrar is updated, CoreText have to request a new ID to avoid ID collision.
+
+ // NOTE(text-perf-review): potential bug. selectableId is regenerated here whenever text
+ // changes, but it is only saved in the initial creation of TextState.
+ val selectableId = if (selectionRegistrar == null) {
+ SelectionRegistrar.InvalidSelectableId
+ } else {
+ remember(text, selectionRegistrar) {
+ selectionRegistrar.nextSelectableId()
+ }
+ }
+
+ val controller = remember {
+ TextController(
+ TextState(
+ TextDelegate(
+ text = AnnotatedString(text),
+ style = style,
+ density = density,
+ softWrap = softWrap,
+ fontFamilyResolver = fontFamilyResolver,
+ overflow = overflow,
+ maxLines = maxLines,
+ minLines = minLines,
+ ),
+ selectableId
+ )
+ )
+ }
+ val state = controller.state
+ if (!currentComposer.inserting) {
+ controller.setTextDelegate(
+ updateTextDelegate(
+ current = state.textDelegate,
+ text = text,
+ style = style,
+ density = density,
+ softWrap = softWrap,
+ fontFamilyResolver = fontFamilyResolver,
+ overflow = overflow,
+ maxLines = maxLines,
+ minLines = minLines,
+ )
+ )
+ }
+ state.>
+ controller.update(selectionRegistrar)
+ if (selectionRegistrar != null) {
+ state.selectionBackgroundColor = LocalTextSelectionColors.current.backgroundColor
+ }
+
+ LayoutLike(modifier = modifier, measurePolicy = controller.measurePolicy)
+}
+
+// Unlike this above, this one is much more speculative as it removes the materialized modifiers
+// and interactions and focus just on the wrapper pattern used by Checkbox
+
+@Composable
+private fun CheckboxLike(
+ checked: Boolean,
+ onCheckedChange: ((Boolean) -> Unit)?,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true
+) {
+ TriStateCheckboxLike(
+ state = ToggleableState(checked),
+ (onCheckedChange != null) { { onCheckedChange(!checked) } } else null,
+ enabled = enabled,
+ modifier = modifier
+ )
+}
+
+@Suppress("unused")
+private enum class ToggleableState {
+ On,
+ Off,
+ Indeterminate
+}
+
+private fun ToggleableState(value: Boolean) = if (value) ToggleableState.On else ToggleableState.Off
+
+@Suppress("UNUSED_PARAMETER")
+@Composable
+private fun TriStateCheckboxLike(
+ state: ToggleableState,
+ onClick: (() -> Unit)?,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true
+) {
+ CheckboxImplLike(
+ enabled = enabled,
+ value = state,
+ modifier = modifier
+ )
+}
+
+@Suppress("UNUSED_EXPRESSION")
+@Composable
+private fun CheckboxImplLike(
+ enabled: Boolean,
+ value: ToggleableState,
+ modifier: Modifier
+) {
+ CanvasLike(modifier) {
+ enabled
+ value
+ }
+}
+
+private interface DrawScope
+
+@Suppress("UNUSED_PARAMETER")
+@Composable
+private fun CanvasLike(modifier: Modifier, onDraw: DrawScope.() -> Unit) =
+ SpacerLike(modifier)
+
+// Utility functions for the tests
+
+@Composable
+private inline fun Marker(content: @Composable () -> Unit) = content()
+
+// left unused for debugging. This is useful for debugging differences in the slot table
+@Suppress("unused")
+private fun CompositionGroup.asString(): String {
+ fun stringOf(group: CompositionGroup, indent: String): String =
+ "$indent ${group.key} ${group.groupSize}:${group.slotsSize}:\n${
+ group.compositionGroups.joinToString("") {
+ stringOf(it, "$indent ")
+ }}"
+ return stringOf(this, "")
+}
+
+private const val MarkerGroup = -340126117
+
+private fun findMarkerGroup(compositionData: CompositionData): CompositionGroup {
+ fun findGroup(groups: Iterable<CompositionGroup>, key: Int): CompositionGroup? {
+ for (group in groups) {
+ if (group.key == key) return group
+ findGroup(group.compositionGroups, key)?.let { return it }
+ }
+ return null
+ }
+
+ return findGroup(compositionData.compositionGroups, MarkerGroup)
+ ?.compositionGroups
+ ?.firstOrNull()
+ ?: error("Could not find marker:\n${compositionData.compositionGroups.first().asString()}")
+}
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
index 32cd5cf..2794899 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/View.kt
@@ -16,12 +16,18 @@
package androidx.compose.runtime.mock
+import androidx.compose.runtime.Stable
import androidx.compose.runtime.snapshots.fastForEach
fun indent(indent: Int, builder: StringBuilder) {
repeat(indent) { builder.append(' ') }
}
+@Stable
+interface Modifier {
+ companion object : Modifier { }
+}
+
open class View {
var name: String = ""
val children = mutableListOf<View>()
@@ -123,7 +129,7 @@
it.toString()
}
- fun findFirstOrNull(predicate: (view: View) -> Boolean): View? {
+ private fun findFirstOrNull(predicate: (view: View) -> Boolean): View? {
if (predicate(this)) return this
for (child in children) {
child.findFirstOrNull(predicate)?.let { return it }
diff --git a/compose/ui/ui-graphics/benchmark/src/androidTest/java/androidx/compose/ui/graphics/benchmark/VectorBenchmarkWithTracing.kt b/compose/ui/ui-graphics/benchmark/src/androidTest/java/androidx/compose/ui/graphics/benchmark/VectorBenchmarkWithTracing.kt
deleted file mode 100644
index 67f4487..0000000
--- a/compose/ui/ui-graphics/benchmark/src/androidTest/java/androidx/compose/ui/graphics/benchmark/VectorBenchmarkWithTracing.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.graphics.benchmark
-
-import androidx.benchmark.junit4.PerfettoTraceRule
-import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.LargeTest
-import org.junit.Rule
-import org.junit.runner.RunWith
-
-/**
- * Duplicate of [VectorBenchmark], but which adds tracing.
- */
-@Suppress("ClassName")
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class VectorBenchmarkWithTracing : VectorBenchmark() {
- @OptIn(ExperimentalPerfettoCaptureApi::class)
- @get:Rule
- val perfettoTraceRule = PerfettoTraceRule()
-}
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
index 663d460..f4b52f8 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
@@ -21,6 +21,7 @@
import android.text.TextPaint
import android.text.style.AbsoluteSizeSpan
import android.text.style.BackgroundColorSpan
+import android.text.style.CharacterStyle
import android.text.style.ForegroundColorSpan
import android.text.style.LeadingMarginSpan
import android.text.style.LocaleSpan
@@ -1447,6 +1448,18 @@
}
@Test
+ fun testSpanStyle_textDecoration_underline_appliedAsSpan() {
+ val text = "abc"
+ val paragraph = simpleParagraph(
+ text = text,
+ style = TextStyle(textDecoration = TextDecoration.Underline),
+ width = 0.0f
+ )
+
+ assertThat(paragraph.charSequence).hasSpan(CharacterStyle::class, 0, text.length)
+ }
+
+ @Test
fun testSpanStyle_textDecoration_lineThrough_appliedOnTextPaint() {
val paragraph = simpleParagraph(
text = "",
@@ -1970,6 +1983,38 @@
assertThat(shaderBrush.callCount).isEqualTo(1)
}
+ @Test
+ fun drawText_withUnderlineStyle_equalToUnderlinePaint() = with(defaultDensity) {
+ val fontSize = 30.sp
+ val fontSizeInPx = fontSize.toPx()
+ val text = "レンズ(単焦点)"
+ val spanStyle = SpanStyle(textDecoration = TextDecoration.Underline)
+ val paragraph = simpleParagraph(
+ text = text,
+ style = TextStyle(fontSize = fontSize),
+ spanStyles = listOf(AnnotatedString.Range(spanStyle, 0, text.length)),
+ width = fontSizeInPx * 20
+ )
+
+ val paragraph2 = simpleParagraph(
+ text = text,
+ style = TextStyle(
+ fontSize = fontSize,
+ textDecoration = TextDecoration.Underline
+ ),
+ width = fontSizeInPx * 20
+ )
+
+ val bitmapWithSpan = paragraph.bitmap()
+ // Our text rendering stack relies on the fact that given textstyle is also passed to draw
+ // functions of TextLayoutResult, MultiParagraph, Paragraph. If Underline is not specified
+ // here, it would be removed while drawing the MultiParagraph. We are simply mimicking
+ // what TextPainter does.
+ val bitmapNoSpan = paragraph2.bitmap(textDecoration = TextDecoration.Underline)
+
+ assertThat(bitmapWithSpan).isEqualToBitmap(bitmapNoSpan)
+ }
+
private fun simpleParagraph(
text: String = "",
spanStyles: List<AnnotatedString.Range<SpanStyle>> = listOf(),
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
index fce67fc..7088ec4 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
@@ -31,6 +31,7 @@
import androidx.compose.ui.text.matchers.isZero
import androidx.compose.ui.text.style.ResolvedTextDirection
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.unit.Constraints
@@ -1155,6 +1156,39 @@
}
@Test
+ fun drawText_withUnderlineStyle_equalToUnderlinePaint() = with(defaultDensity) {
+ val fontSize = 30.sp
+ val fontSizeInPx = fontSize.toPx()
+ val multiParagraph = simpleMultiParagraph(
+ text = buildAnnotatedString {
+ withStyle(SpanStyle(textDecoration = TextDecoration.Underline)) {
+ append("レンズ(単焦点)")
+ }
+ },
+ style = TextStyle(fontSize = fontSize),
+ width = fontSizeInPx * 20
+ )
+
+ val multiParagraph2 = simpleMultiParagraph(
+ text = AnnotatedString("レンズ(単焦点)"),
+ style = TextStyle(
+ fontSize = fontSize,
+ textDecoration = TextDecoration.Underline
+ ),
+ width = fontSizeInPx * 20
+ )
+
+ val bitmapWithSpan = multiParagraph.bitmap()
+ // Our text rendering stack relies on the fact that given textstyle is also passed to draw
+ // functions of TextLayoutResult, MultiParagraph, Paragraph. If Underline is not specified
+ // here, it would be removed while drawing the MultiParagraph. We are simply mimicking
+ // what TextPainter does.
+ val bitmapNoSpan = multiParagraph2.bitmap(textDecoration = TextDecoration.Underline)
+
+ assertThat(bitmapWithSpan).isEqualToBitmap(bitmapNoSpan)
+ }
+
+ @Test
fun textIndent_onFirstLine() {
with(defaultDensity) {
val text = createAnnotatedString("aaa", "\u05D0\u05D0\u05D0")
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/ApplySpanStyleTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/ApplySpanStyleTest.kt
index 3b2cca6..e4e8f33 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/ApplySpanStyleTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/ApplySpanStyleTest.kt
@@ -28,6 +28,7 @@
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.platform.extensions.applySpanStyle
import androidx.compose.ui.text.style.BaselineShift
+import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextGeometricTransform
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.TextUnit
@@ -257,8 +258,8 @@
assertThat(notApplied.background).isEqualTo(Color.Unspecified)
}
- /*@Test
- fun textDecorationUnderline_shouldBeLeftAsSpan() {
+ @Test
+ fun textDecorationUnderline_shouldBeAppliedToPaint() {
val textDecoration = TextDecoration.Underline
val spanStyle = SpanStyle(textDecoration = textDecoration)
val tp = AndroidTextPaint(0, density.density)
@@ -266,12 +267,12 @@
val notApplied = tp.applySpanStyle(spanStyle, resolveTypeface, density)
- assertThat(tp.isUnderlineText).isEqualTo(false)
- assertThat(notApplied.textDecoration).isEqualTo(textDecoration)
+ assertThat(tp.isUnderlineText).isEqualTo(true)
+ assertThat(notApplied.textDecoration).isEqualTo(null)
}
@Test
- fun textDecorationLineThrough_shouldBeLeftAsSpan() {
+ fun textDecorationLineThrough_shouldBeAppliedToPaint() {
val textDecoration = TextDecoration.LineThrough
val spanStyle = SpanStyle(textDecoration = textDecoration)
val tp = AndroidTextPaint(0, density.density)
@@ -279,13 +280,14 @@
val notApplied = tp.applySpanStyle(spanStyle, resolveTypeface, density)
- assertThat(tp.isStrikeThruText).isEqualTo(false)
- assertThat(notApplied.textDecoration).isEqualTo(textDecoration)
+ assertThat(tp.isStrikeThruText).isEqualTo(true)
+ assertThat(notApplied.textDecoration).isEqualTo(null)
}
@Test
- fun textDecorationNone_shouldNotBeLeftAsSpan() {
- val textDecoration = TextDecoration.None
+ fun textDecorationCombined_shouldBeAppliedToPaint() {
+ val textDecoration =
+ TextDecoration.combine(listOf(TextDecoration.LineThrough, TextDecoration.Underline))
val spanStyle = SpanStyle(textDecoration = textDecoration)
val tp = AndroidTextPaint(0, density.density)
tp.isUnderlineText = false
@@ -293,10 +295,10 @@
val notApplied = tp.applySpanStyle(spanStyle, resolveTypeface, density)
- assertThat(tp.isUnderlineText).isEqualTo(false)
- assertThat(tp.isStrikeThruText).isEqualTo(false)
- assertThat(notApplied.textDecoration).isNull()
- }*/
+ assertThat(tp.isUnderlineText).isEqualTo(true)
+ assertThat(tp.isStrikeThruText).isEqualTo(true)
+ assertThat(notApplied.textDecoration).isEqualTo(null)
+ }
@Test
fun shadow_shouldBeAppliedTo_shadowLayer() {
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
index 60832dd..9c943d0 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
@@ -18,6 +18,8 @@
import android.graphics.Typeface
import android.text.SpannableString
+import android.text.TextPaint
+import android.text.style.CharacterStyle
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.DefaultIncludeFontPadding
import androidx.compose.ui.text.ExperimentalTextApi
@@ -31,9 +33,11 @@
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.platform.extensions.setLineHeight
import androidx.compose.ui.text.platform.extensions.setPlaceholders
+import androidx.compose.ui.text.platform.extensions.setSpan
import androidx.compose.ui.text.platform.extensions.setSpanStyles
import androidx.compose.ui.text.platform.extensions.setTextIndent
import androidx.compose.ui.text.style.LineHeightStyle
+import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.isUnspecified
@@ -58,6 +62,15 @@
val spannableString = SpannableString(text)
+ // b/199939617
+ // Due to a bug in the platform's native drawText stack, some CJK characters cause a bolder
+ // than intended underline to be painted when TextDecoration is set to Underline.
+ // If there's a CharacterStyle span that takes the entire length of the text, even if
+ // it's no-op, it causes a different native call to render the text that prevents the bug.
+ if (contextTextStyle.textDecoration == TextDecoration.Underline) {
+ spannableString.setSpan(NoopSpan, 0, text.length)
+ }
+
if (contextTextStyle.isIncludeFontPaddingEnabled() &&
contextTextStyle.lineHeightStyle == null
) {
@@ -95,4 +108,8 @@
@Suppress("DEPRECATION")
internal fun TextStyle.isIncludeFontPaddingEnabled(): Boolean {
return platformStyle?.paragraphStyle?.includeFontPadding ?: DefaultIncludeFontPadding
+}
+
+private val NoopSpan = object : CharacterStyle() {
+ override fun updateDrawState(p0: TextPaint?) {}
}
\ No newline at end of file
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 82c974c..81a7dee 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -440,12 +440,12 @@
}
public static final class CompositingStrategy.Companion {
- method public int getAlways();
method public int getAuto();
method public int getModulateAlpha();
- property public final int Always;
+ method public int getOffscreen();
property public final int Auto;
property public final int ModulateAlpha;
+ property public final int Offscreen;
}
public final class GraphicsLayerModifierKt {
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 3651c6e..d68bb29 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -565,12 +565,12 @@
}
public static final class CompositingStrategy.Companion {
- method public int getAlways();
method public int getAuto();
method public int getModulateAlpha();
- property public final int Always;
+ method public int getOffscreen();
property public final int Auto;
property public final int ModulateAlpha;
+ property public final int Offscreen;
}
public final class GraphicsLayerModifierKt {
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 9c494ff..a921986 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -440,12 +440,12 @@
}
public static final class CompositingStrategy.Companion {
- method public int getAlways();
method public int getAuto();
method public int getModulateAlpha();
- property public final int Always;
+ method public int getOffscreen();
property public final int Auto;
property public final int ModulateAlpha;
+ property public final int Offscreen;
}
public final class GraphicsLayerModifierKt {
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index 8caae0c..a8403cb 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -53,8 +53,8 @@
implementation(libs.kotlinCoroutinesCore)
// when updating the runtime version please also update the runtime-saveable version
- implementation("androidx.compose.runtime:runtime:1.3.0-rc01")
- api("androidx.compose.runtime:runtime-saveable:1.3.0-rc01")
+ implementation(project(":compose:runtime:runtime"))
+ api(project(":compose:runtime:runtime-saveable"))
api(project(":compose:ui:ui-geometry"))
api(project(":compose:ui:ui-graphics"))
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/LayerModifierSamples.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/LayerModifierSamples.kt
index 21cf8c6..6089322 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/LayerModifierSamples.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/LayerModifierSamples.kt
@@ -70,7 +70,7 @@
// CompositingStrategy.ModulateAlpha ends up with the overlapping region
// of the 2 draw rect calls to blend transparent blue and transparent red
// against the black background instead of just transparent blue which is what would
- // occur with CompositingStrategy.Auto or CompositingStrategy.Always
+ // occur with CompositingStrategy.Auto or CompositingStrategy.Offscreen
inset(0f, 0f, size.width / 3, size.height / 3) {
drawRect(color = Color.Red)
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index 738a1e2..792181a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -280,28 +280,28 @@
// Use public RenderNode API
in Build.VERSION_CODES.Q..Int.MAX_VALUE ->
verifyRenderNode29CompositingStrategy(
- CompositingStrategy.Always,
+ CompositingStrategy.Offscreen,
expectedCompositing = true,
expectedOverlappingRendering = true
)
// Cannot access private APIs on P
Build.VERSION_CODES.P ->
verifyViewLayerCompositingStrategy(
- CompositingStrategy.Always,
+ CompositingStrategy.Offscreen,
View.LAYER_TYPE_HARDWARE,
true
)
// Use stub access to framework RenderNode API
in Build.VERSION_CODES.M..Int.MAX_VALUE ->
verifyRenderNode23CompositingStrategy(
- CompositingStrategy.Always,
+ CompositingStrategy.Offscreen,
expectedLayerType = View.LAYER_TYPE_HARDWARE,
expectedOverlappingRendering = true
)
// No RenderNodes, use Views instead
else ->
verifyViewLayerCompositingStrategy(
- CompositingStrategy.Always,
+ CompositingStrategy.Offscreen,
View.LAYER_TYPE_HARDWARE,
true
)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
index 38e267e..de8dd50 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
@@ -1210,7 +1210,7 @@
.size((dimen / LocalDensity.current.density).dp)
.background(Color.LightGray)
.graphicsLayer(
- compositingStrategy = CompositingStrategy.Always
+ compositingStrategy = CompositingStrategy.Offscreen
)
) {
inset(0f, 0f, size.width / 3, size.height / 3) {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi23.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi23.android.kt
index 0958df1..8b27e39 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi23.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi23.android.kt
@@ -216,7 +216,7 @@
get() = internalCompositingStrategy
set(value) {
when (value) {
- CompositingStrategy.Always -> {
+ CompositingStrategy.Offscreen -> {
renderNode.setLayerType(View.LAYER_TYPE_HARDWARE)
renderNode.setHasOverlappingRendering(true)
}
@@ -233,7 +233,7 @@
}
internal fun getLayerType(): Int = when (internalCompositingStrategy) {
- CompositingStrategy.Always -> View.LAYER_TYPE_HARDWARE
+ CompositingStrategy.Offscreen -> View.LAYER_TYPE_HARDWARE
else -> View.LAYER_TYPE_NONE
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi29.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi29.android.kt
index fd1c8cc..bddbb59 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi29.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi29.android.kt
@@ -160,7 +160,7 @@
set(value) {
with(renderNode) {
when (value) {
- CompositingStrategy.Always -> {
+ CompositingStrategy.Offscreen -> {
setUseCompositingLayer(true, null)
setHasOverlappingRendering(true)
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
index baadbdf..46f3139 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
@@ -193,7 +193,7 @@
}
mHasOverlappingRendering = when (compositingStrategy) {
- CompositingStrategy.Always -> {
+ CompositingStrategy.Offscreen -> {
setLayerType(LAYER_TYPE_HARDWARE, null)
true
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
index 77848a3..f3ce5e4 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
@@ -322,7 +322,7 @@
* an offscreen buffer. This is useful in order to optimize alpha usages with
* [CompositingStrategy.ModulateAlpha] which will skip the overhead of an offscreen buffer but can
* generate different rendering results depending on whether or not the contents of the layer are
- * overlapping. Similarly leveraging [CompositingStrategy.Always] is useful in situations where
+ * overlapping. Similarly leveraging [CompositingStrategy.Offscreen] is useful in situations where
* creating an offscreen buffer is preferred usually in conjunction with [BlendMode] usage.
*
* Note that if you provide a non-zero [shadowElevation] and if the passed [shape] is concave the
@@ -478,7 +478,7 @@
* For example, the contents can be drawn into this graphics layer and masked out by drawing
* additional shapes with [BlendMode.Clear]
*/
- val Always = CompositingStrategy(1)
+ val Offscreen = CompositingStrategy(1)
/**
* Modulates alpha for each of the drawing instructions recorded within the graphicsLayer.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope.kt
index 5871e7f..4241f32 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope.kt
@@ -214,7 +214,7 @@
* layer, any content rendered outside of the specified size will be clipped.
*/
val size: Size
- get() = Size.Zero
+ get() = Size.Unspecified
}
/**
@@ -239,7 +239,7 @@
override var shape: Shape = RectangleShape
override var clip: Boolean = false
override var compositingStrategy: CompositingStrategy = CompositingStrategy.Auto
- override var size: Size = Size.Zero
+ override var size: Size = Size.Unspecified
internal var graphicsDensity: Density = Density(1.0f)
@@ -269,6 +269,6 @@
clip = false
renderEffect = null
compositingStrategy = CompositingStrategy.Auto
- size = Size.Zero
+ size = Size.Unspecified
}
}
diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaLayer.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaLayer.skiko.kt
index 3b07d10..4091d4a 100644
--- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaLayer.skiko.kt
+++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaLayer.skiko.kt
@@ -264,7 +264,7 @@
val requiresLayer =
(alpha < 1 && compositingStrategy != CompositingStrategy.ModulateAlpha) ||
currentRenderEffect != null ||
- compositingStrategy == CompositingStrategy.Always
+ compositingStrategy == CompositingStrategy.Offscreen
if (requiresLayer) {
canvas.saveLayer(
bounds,
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/graphics/GraphicsLayerScopeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/graphics/GraphicsLayerScopeTest.kt
index 3aa9cbc..97e898c 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/graphics/GraphicsLayerScopeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/graphics/GraphicsLayerScopeTest.kt
@@ -96,6 +96,6 @@
assertThat(transformOrigin).isEqualTo(TransformOrigin.Center)
assertThat(shape).isEqualTo(RectangleShape)
assertThat(clip).isEqualTo(false)
- assertThat(size).isEqualTo(Size.Zero)
+ assertThat(size).isEqualTo(Size.Unspecified)
}
}
diff --git a/constraintlayout/constraintlayout-compose/api/current.txt b/constraintlayout/constraintlayout-compose/api/current.txt
index 888c103..0c83242 100644
--- a/constraintlayout/constraintlayout-compose/api/current.txt
+++ b/constraintlayout/constraintlayout-compose/api/current.txt
@@ -36,6 +36,7 @@
method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
method public androidx.constraintlayout.compose.Dimension getHeight();
+ method public float getHorizontalBias();
method public float getHorizontalChainWeight();
method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
method public float getPivotX();
@@ -50,6 +51,7 @@
method public float getTranslationX();
method public float getTranslationY();
method public float getTranslationZ();
+ method public float getVerticalBias();
method public float getVerticalChainWeight();
method public androidx.constraintlayout.compose.Visibility getVisibility();
method public androidx.constraintlayout.compose.Dimension getWidth();
@@ -60,6 +62,7 @@
method public void resetTransforms();
method public void setAlpha(float);
method public void setHeight(androidx.constraintlayout.compose.Dimension);
+ method public void setHorizontalBias(float);
method public void setHorizontalChainWeight(float);
method public void setPivotX(float);
method public void setPivotY(float);
@@ -71,6 +74,7 @@
method public void setTranslationX(float);
method public void setTranslationY(float);
method public void setTranslationZ(float);
+ method public void setVerticalBias(float);
method public void setVerticalChainWeight(float);
method public void setVisibility(androidx.constraintlayout.compose.Visibility);
method public void setWidth(androidx.constraintlayout.compose.Dimension);
@@ -81,6 +85,7 @@
property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
property public final androidx.constraintlayout.compose.VerticalAnchorable end;
property public final androidx.constraintlayout.compose.Dimension height;
+ property public final float horizontalBias;
property public final float horizontalChainWeight;
property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
property public final float pivotX;
@@ -95,6 +100,7 @@
property public final float translationX;
property public final float translationY;
property public final float translationZ;
+ property public final float verticalBias;
property public final float verticalChainWeight;
property public final androidx.constraintlayout.compose.Visibility visibility;
property public final androidx.constraintlayout.compose.Dimension width;
@@ -151,6 +157,9 @@
method public final androidx.constraintlayout.compose.VerticalChainReference createVerticalChain(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
method protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> getTasks();
method public void reset();
+ method public final androidx.constraintlayout.compose.LayoutReference withChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional float weight);
+ method public final androidx.constraintlayout.compose.LayoutReference withHorizontalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional float weight);
+ method public final androidx.constraintlayout.compose.LayoutReference withVerticalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional float weight);
property protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> tasks;
}
diff --git a/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt b/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
index 9bd9dc8..fd40d95 100644
--- a/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
@@ -67,6 +67,7 @@
method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
method public androidx.constraintlayout.compose.Dimension getHeight();
+ method public float getHorizontalBias();
method public float getHorizontalChainWeight();
method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
method public float getPivotX();
@@ -81,6 +82,7 @@
method public float getTranslationX();
method public float getTranslationY();
method public float getTranslationZ();
+ method public float getVerticalBias();
method public float getVerticalChainWeight();
method public androidx.constraintlayout.compose.Visibility getVisibility();
method public androidx.constraintlayout.compose.Dimension getWidth();
@@ -91,6 +93,7 @@
method public void resetTransforms();
method public void setAlpha(float);
method public void setHeight(androidx.constraintlayout.compose.Dimension);
+ method public void setHorizontalBias(float);
method public void setHorizontalChainWeight(float);
method public void setPivotX(float);
method public void setPivotY(float);
@@ -102,6 +105,7 @@
method public void setTranslationX(float);
method public void setTranslationY(float);
method public void setTranslationZ(float);
+ method public void setVerticalBias(float);
method public void setVerticalChainWeight(float);
method public void setVisibility(androidx.constraintlayout.compose.Visibility);
method public void setWidth(androidx.constraintlayout.compose.Dimension);
@@ -112,6 +116,7 @@
property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
property public final androidx.constraintlayout.compose.VerticalAnchorable end;
property public final androidx.constraintlayout.compose.Dimension height;
+ property public final float horizontalBias;
property public final float horizontalChainWeight;
property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
property public final float pivotX;
@@ -126,6 +131,7 @@
property public final float translationX;
property public final float translationY;
property public final float translationZ;
+ property public final float verticalBias;
property public final float verticalChainWeight;
property public final androidx.constraintlayout.compose.Visibility visibility;
property public final androidx.constraintlayout.compose.Dimension width;
@@ -182,6 +188,9 @@
method public final androidx.constraintlayout.compose.VerticalChainReference createVerticalChain(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
method protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> getTasks();
method public void reset();
+ method public final androidx.constraintlayout.compose.LayoutReference withChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional float weight);
+ method public final androidx.constraintlayout.compose.LayoutReference withHorizontalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional float weight);
+ method public final androidx.constraintlayout.compose.LayoutReference withVerticalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional float weight);
property protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> tasks;
}
diff --git a/constraintlayout/constraintlayout-compose/api/restricted_current.txt b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
index b2a7877..d11ace4 100644
--- a/constraintlayout/constraintlayout-compose/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
@@ -36,6 +36,7 @@
method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
method public androidx.constraintlayout.compose.Dimension getHeight();
+ method public float getHorizontalBias();
method public float getHorizontalChainWeight();
method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
method public float getPivotX();
@@ -50,6 +51,7 @@
method public float getTranslationX();
method public float getTranslationY();
method public float getTranslationZ();
+ method public float getVerticalBias();
method public float getVerticalChainWeight();
method public androidx.constraintlayout.compose.Visibility getVisibility();
method public androidx.constraintlayout.compose.Dimension getWidth();
@@ -60,6 +62,7 @@
method public void resetTransforms();
method public void setAlpha(float);
method public void setHeight(androidx.constraintlayout.compose.Dimension);
+ method public void setHorizontalBias(float);
method public void setHorizontalChainWeight(float);
method public void setPivotX(float);
method public void setPivotY(float);
@@ -71,6 +74,7 @@
method public void setTranslationX(float);
method public void setTranslationY(float);
method public void setTranslationZ(float);
+ method public void setVerticalBias(float);
method public void setVerticalChainWeight(float);
method public void setVisibility(androidx.constraintlayout.compose.Visibility);
method public void setWidth(androidx.constraintlayout.compose.Dimension);
@@ -81,6 +85,7 @@
property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
property public final androidx.constraintlayout.compose.VerticalAnchorable end;
property public final androidx.constraintlayout.compose.Dimension height;
+ property public final float horizontalBias;
property public final float horizontalChainWeight;
property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
property public final float pivotX;
@@ -95,6 +100,7 @@
property public final float translationX;
property public final float translationY;
property public final float translationZ;
+ property public final float verticalBias;
property public final float verticalChainWeight;
property public final androidx.constraintlayout.compose.Visibility visibility;
property public final androidx.constraintlayout.compose.Dimension width;
@@ -151,6 +157,9 @@
method public final androidx.constraintlayout.compose.VerticalChainReference createVerticalChain(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
method protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> getTasks();
method public void reset();
+ method public final androidx.constraintlayout.compose.LayoutReference withChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional float weight);
+ method public final androidx.constraintlayout.compose.LayoutReference withHorizontalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional float weight);
+ method public final androidx.constraintlayout.compose.LayoutReference withVerticalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional float weight);
property protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> tasks;
field @kotlin.PublishedApi internal int helpersHashCode;
}
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/build.gradle b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/build.gradle
new file mode 100644
index 0000000..7b273f9
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/build.gradle
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id("AndroidXPlugin")
+ id("AndroidXComposePlugin")
+ id("com.android.application")
+ id("org.jetbrains.kotlin.android")
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 26
+ }
+ namespace "androidx.constraintlayout.compose.integration.macrobenchmark.target"
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"),
+ 'proguard-rules.pro'
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.recyclerview:recyclerview:1.2.1'
+
+ implementation(libs.kotlinStdlib)
+ implementation(project(":activity:activity-compose"))
+ implementation("androidx.appcompat:appcompat:1.4.1")
+ implementation("androidx.cardview:cardview:1.0.0")
+ // old version of common-java8 conflicts with newer version, because both have
+ // DefaultLifecycleEventObserver.
+ // Outside of androidx this is resolved via constraint added to lifecycle-common,
+ // but it doesn't work in androidx.
+ // See aosp/1804059
+ implementation "androidx.lifecycle:lifecycle-common-java8:2.5.1"
+ implementation(project(":constraintlayout:constraintlayout-compose"))
+ implementation(project(":compose:foundation:foundation-layout"))
+ implementation(project(":compose:foundation:foundation"))
+ implementation(project(":compose:material:material"))
+ implementation(project(":compose:runtime:runtime"))
+ implementation(project(":compose:runtime:runtime-tracing"))
+ implementation(project(":compose:ui:ui"))
+ implementation(project(":compose:ui:ui-tooling"))
+ implementation(project(":profileinstaller:profileinstaller"))
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/proguard-rules.pro b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/proguard-rules.pro
new file mode 100644
index 0000000..0674e77
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/proguard-rules.pro
@@ -0,0 +1 @@
+-dontobfuscate
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a38483c
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<!--
+ Copyright 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <application
+ android:label="Jetpack Compose Macrobenchmark Target"
+ android:allowBackup="false"
+ android:supportsRtl="true"
+ android:icon="@mipmap/ic_launcher"
+ tools:ignore="GoogleAppIndexingWarning">
+
+ <!-- Profileable to enable macrobenchmark profiling -->
+ <profileable android:shell="true"/>
+
+ <!--
+ Activities need to be exported so the macrobenchmark can discover them
+ under the new package visibility changes for Android 11.
+ -->
+ <activity android:name=".MotionLayoutActivity"
+ android:exported="true"
+ android:theme="@android:style/Theme.Material.Light.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="androidx.constraintlayout.compose.integration.macrobenchmark.target.MOTION_LAYOUT_ACTIVITY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/ic_launcher-web.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/ic_launcher-web.png
new file mode 100644
index 0000000..88e5f3b
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/ic_launcher-web.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt
new file mode 100644
index 0000000..24ae735
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.integration.macrobenchmark.target
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.integration.macrobenchmark.target.motionlayout.newmessage.NewMotionMessagePreview
+import androidx.compose.integration.macrobenchmark.target.motionlayout.newmessage.NewMotionMessagePreviewWithDsl
+import androidx.compose.integration.macrobenchmark.target.motionlayout.toolbar.MotionCollapseToolbarPreview
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTagsAsResourceId
+
+class MotionLayoutActivity : ComponentActivity() {
+
+ @OptIn(ExperimentalComposeUiApi::class)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val name = intent.getStringExtra("ComposableName")
+ setContent {
+ MaterialTheme {
+ // A surface container using the 'background' color from the theme
+ Surface(
+ modifier = Modifier
+ .fillMaxSize()
+ // Required to reference UI elements by Macrobenchmark
+ .semantics { testTagsAsResourceId = true },
+ color = MaterialTheme.colors.background
+ ) {
+ // Here we resolve the Composable requested by Macrobenchark
+ when (name) {
+ "NewMessageJson" -> {
+ NewMotionMessagePreview()
+ }
+ "NewMessageDsl" -> {
+ NewMotionMessagePreviewWithDsl()
+ }
+ "CollapsibleToolbar" -> {
+ MotionCollapseToolbarPreview()
+ }
+ else -> {
+ throw IllegalArgumentException("No Composable with name: $name")
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/components/CardSample.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/components/CardSample.kt
new file mode 100644
index 0000000..30960cf
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/components/CardSample.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.common.components
+
+import androidx.annotation.DrawableRes
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.common.sampledata.LoremIpsum
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.common.sampledata.newHourMinuteTimeStamp
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.common.sampledata.randomAvatarId
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.common.sampledata.randomFullName
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.ConstraintSet
+import androidx.constraintlayout.compose.Dimension
+
+@Preview
+@Composable
+private fun CardSamplePreview() {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(Color.LightGray)
+ .verticalScroll(rememberScrollState()),
+ verticalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ for (i in 0 until 15) {
+ CardSample(
+ Modifier
+ .height(80.dp)
+ .background(Color.White, RoundedCornerShape(10.dp))
+ )
+ }
+ }
+}
+
+private val cardSampleConstraintSet = ConstraintSet {
+ val image = createRefFor("image")
+ val title = createRefFor("title")
+ val description = createRefFor("description")
+ val time = createRefFor("timestamp")
+
+ constrain(image) {
+ width = Dimension.ratio("1:1")
+ height = Dimension.fillToConstraints
+ top.linkTo(parent.top)
+ bottom.linkTo(parent.bottom)
+ start.linkTo(parent.start)
+ }
+ constrain(title) {
+ width = Dimension.preferredWrapContent
+ top.linkTo(parent.top)
+
+ linkTo(image.end, time.start, 8.dp, 8.dp, bias = 0f)
+ }
+ constrain(description) {
+ width = Dimension.fillToConstraints
+
+ linkTo(image.end, parent.end, 8.dp, bias = 0f)
+ bottom.linkTo(parent.bottom)
+ }
+ constrain(time) {
+ top.linkTo(parent.top)
+ end.linkTo(parent.end)
+ }
+}
+
+@Composable
+fun CardSample(
+ modifier: Modifier = Modifier,
+ @DrawableRes drawableRes: Int = randomAvatarId(),
+ title: String = randomFullName(),
+ description: String = LoremIpsum.words(50).shuffled().joinToString(" "),
+ timeStamp: String = newHourMinuteTimeStamp()
+) {
+ ConstraintLayout(
+ modifier = Modifier
+ .defaultMinSize(minWidth = 200.dp, minHeight = 50.dp)
+ .then(modifier)
+ .padding(4.dp),
+ constraintSet = cardSampleConstraintSet
+ ) {
+ Image(
+ modifier = Modifier
+ .layoutId("image")
+ .clip(RoundedCornerShape(10.dp)),
+ painter = painterResource(id = drawableRes),
+ contentDescription = null
+ )
+ Text(
+ modifier = Modifier.layoutId("title"),
+ text = title,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ Text(modifier = Modifier.layoutId("timestamp"), text = timeStamp)
+ Text(
+ modifier = Modifier.layoutId("description"),
+ text = description,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/components/SearchBar.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/components/SearchBar.kt
new file mode 100644
index 0000000..8cc4339
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/components/SearchBar.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.common.components
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Icon
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Text
+import androidx.compose.material.TextFieldDefaults
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+@Preview
+@Composable
+private fun SearchBarPreview() {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(10.dp)
+ ) {
+ SearchBar(Modifier.fillMaxWidth())
+ OutlinedSearchBar(Modifier.fillMaxWidth())
+ }
+}
+
+@Composable
+fun SearchBar(
+ modifier: Modifier = Modifier,
+ backgroundColor: Color = Color.LightGray
+) {
+ CommonSearchBar(
+ modifier = modifier,
+ outlined = false,
+ borderOrBackgroundColor = backgroundColor
+ )
+}
+
+@Composable
+fun OutlinedSearchBar(
+ modifier: Modifier = Modifier,
+ borderColor: Color = Color.LightGray
+) {
+ CommonSearchBar(
+ modifier = modifier,
+ outlined = true,
+ borderOrBackgroundColor = borderColor
+ )
+}
+
+@Composable
+private fun CommonSearchBar(
+ modifier: Modifier,
+ outlined: Boolean,
+ borderOrBackgroundColor: Color
+) {
+ var placeholder: String by remember { mutableStateOf("Search...") }
+ val backgroundModifier = if (outlined) {
+ Modifier.border(BorderStroke(2.dp, borderOrBackgroundColor), RoundedCornerShape(32.dp))
+ } else {
+ Modifier.background(borderOrBackgroundColor, RoundedCornerShape(32.dp))
+ }
+ OutlinedTextField(
+ modifier = modifier
+ .then(backgroundModifier)
+ .onFocusChanged {
+ placeholder = if (it.isFocused) {
+ "I'm not implemented yet!"
+ } else {
+ "Search..."
+ }
+ },
+ value = "",
+ _ ->
+ },
+ placeholder = {
+ Text(text = placeholder, maxLines = 1, overflow = TextOverflow.Clip)
+ },
+ trailingIcon = {
+ Icon(imageVector = Icons.Default.Search, contentDescription = null)
+ },
+ singleLine = true,
+ colors = TextFieldDefaults.textFieldColors(
+ focusedIndicatorColor = Color.Transparent,
+ unfocusedIndicatorColor = Color.Transparent,
+ backgroundColor = Color.Transparent,
+ )
+ )
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/components/TestableButton.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/components/TestableButton.kt
new file mode 100644
index 0000000..9beb710
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/components/TestableButton.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.common.components
+
+import androidx.compose.material.Button
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+
+/**
+ * Button with text.
+ *
+ * [text] is also applied to [Modifier.testTag] so that it's addressable by UI Automator.
+ */
+@Composable
+internal fun TestableButton(
+ onClick: () -> Unit,
+ text: String,
+ modifier: Modifier = Modifier,
+) {
+ Button(
+ modifier = modifier.testTag(text),
+ >
+ ) {
+ Text(text = text)
+ }
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/sampledata/Images.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/sampledata/Images.kt
new file mode 100644
index 0000000..08f4781
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/sampledata/Images.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.common.sampledata
+
+import android.content.ContentResolver
+import android.content.Context
+import android.net.Uri
+import androidx.annotation.DrawableRes
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.R
+
+@DrawableRes
+private val avatarsIdList: Array<Int> = arrayOf(
+ R.drawable.avatar_1,
+ R.drawable.avatar_2,
+ R.drawable.avatar_3,
+ R.drawable.avatar_4,
+ R.drawable.avatar_5,
+ R.drawable.avatar_6,
+ R.drawable.avatar_7,
+ R.drawable.avatar_8,
+ R.drawable.avatar_9,
+ R.drawable.avatar_10,
+ R.drawable.avatar_11,
+ R.drawable.avatar_12,
+ R.drawable.avatar_13,
+ R.drawable.avatar_14,
+ R.drawable.avatar_15,
+ R.drawable.avatar_16,
+)
+
+@DrawableRes
+internal fun randomAvatarId(): Int = avatarsIdList.random()
+
+internal fun Context.drawableUriOf(@DrawableRes resourceId: Int): Uri =
+ with(resources) {
+ Uri.Builder()
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .authority(getResourcePackageName(resourceId))
+ .appendPath(getResourceTypeName(resourceId))
+ .appendPath(getResourceEntryName(resourceId))
+ .build()
+ }
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/sampledata/Strings.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/sampledata/Strings.kt
new file mode 100644
index 0000000..c062956
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/sampledata/Strings.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.common.sampledata
+
+/**
+ * From [androidx.compose.ui.tooling.preview.datasource.LoremIpsum]
+ */
+private val LOREM_IPSUM = """
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer sodales
+laoreet commodo. Phasellus a purus eu risus elementum consequat. Aenean eu
+elit ut nunc convallis laoreet non ut libero. Suspendisse interdum placerat
+risus vel ornare. Donec vehicula, turpis sed consectetur ullamcorper, ante
+nunc egestas quam, ultricies adipiscing velit enim at nunc. Aenean id diam
+neque. Praesent ut lacus sed justo viverra fermentum et ut sem. Fusce
+convallis gravida lacinia. Integer semper dolor ut elit sagittis lacinia.
+Praesent sodales scelerisque eros at rhoncus. Duis posuere sapien vel ipsum
+ornare interdum at eu quam. Vestibulum vel massa erat. Aenean quis sagittis
+purus. Phasellus arcu purus, rutrum id consectetur non, bibendum at nibh.
+
+Duis nec erat dolor. Nulla vitae consectetur ligula. Quisque nec mi est. Ut
+quam ante, rutrum at pellentesque gravida, pretium in dui. Cras eget sapien
+velit. Suspendisse ut sem nec tellus vehicula eleifend sit amet quis velit.
+Phasellus quis suscipit nisi. Nam elementum malesuada tincidunt. Curabitur
+iaculis pretium eros, malesuada faucibus leo eleifend a. Curabitur congue
+orci in neque euismod a blandit libero vehicula.""".trim()
+
+private val LOREM_IPSUM_WORDS = LOREM_IPSUM.split(" ")
+
+private val names = listOf(
+ "Jacob",
+ "Sophia",
+ "Noah",
+ "Emma",
+ "Mason",
+ "Isabella",
+ "William",
+ "Olivia",
+ "Ethan",
+ "Ava",
+ "Liam",
+ "Emily",
+ "Michael",
+ "Abigail",
+ "Alexander",
+ "Mia",
+ "Jayden",
+ "Madison",
+ "Daniel",
+ "Elizabeth",
+ "Aiden",
+ "Chloe",
+ "James",
+ "Ella",
+ "Elijah",
+ "Avery",
+ "Matthew",
+ "Charlotte",
+ "Benjamin",
+ "Sofia"
+)
+
+private val surnames = arrayOf(
+ "Smith",
+ "Johnson",
+ "Williams",
+ "Brown",
+ "Jones",
+ "Garcia",
+ "Miller",
+ "Davis",
+ "Rodriguez",
+ "Martinez"
+)
+
+private val cities = arrayOf(
+ "Shanghai",
+ "Karachi",
+ "Beijing",
+ "Delhi",
+ "Lagos",
+ "Tianjin",
+ "Istanbul",
+ "Tokyo",
+ "Guangzhou",
+ "Mumbai",
+ "Moscow",
+ "São Paulo",
+ "Shenzhen",
+ "Jakarta",
+ "Lahore",
+ "Seoul",
+ "Wuhan",
+ "Kinshasa",
+ "Cairo",
+ "Mexico City",
+ "Lima",
+ "London",
+ "New York City"
+)
+
+internal fun randomFirstName(): String = names.random()
+
+internal fun randomLastName(): String = surnames.random()
+
+internal fun randomFullName(): String = randomFirstName() + " " + randomLastName()
+
+internal fun randomCity(): String = cities.random()
+
+internal object LoremIpsum {
+ fun string(wordCount: Int, withLineBreaks: Boolean = false): String =
+ words(wordCount, withLineBreaks).joinToString(separator = " ")
+
+ fun words(wordCount: Int, withLineBreaks: Boolean = false): List<String> =
+ if (withLineBreaks) {
+ // Source includes line breaks
+ LOREM_IPSUM_WORDS.take(wordCount.coerceIn(1, LOREM_IPSUM_WORDS.size))
+ } else {
+ LOREM_IPSUM.filter { it != '\n' }.split(' ')
+ .take(wordCount.coerceIn(1, LOREM_IPSUM_WORDS.size))
+ }
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/sampledata/Time.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/sampledata/Time.kt
new file mode 100644
index 0000000..53944f9
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/common/sampledata/Time.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.integration.macrobenchmark.target.common.sampledata
+
+import java.text.SimpleDateFormat
+import java.time.Instant
+import java.util.Date
+import java.util.Locale
+
+internal fun newHourMinuteTimeStamp(): String {
+ return SimpleDateFormat("hh:mma", Locale.US).format(Date.from(Instant.now()))
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt
new file mode 100644
index 0000000..8f99fe2
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt
@@ -0,0 +1,758 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalMotionApi::class)
+
+package androidx.compose.integration.macrobenchmark.target.motionlayout.newmessage
+
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.common.components.TestableButton
+import androidx.compose.material.Button
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.Icon
+import androidx.compose.material.LocalTextStyle
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material.icons.filled.KeyboardArrowDown
+import androidx.compose.material.icons.filled.Send
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTagsAsResourceId
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.ConstraintSet
+import androidx.constraintlayout.compose.Dimension
+import androidx.constraintlayout.compose.ExperimentalMotionApi
+import androidx.constraintlayout.compose.MotionLayout
+import androidx.constraintlayout.compose.MotionLayoutScope
+import androidx.constraintlayout.compose.MotionScene
+import androidx.constraintlayout.compose.Visibility
+
+// Copied from ComposeMail project
+
+@Preview
+@Composable
+fun NewMotionMessagePreview() {
+ NewMotionMessageWithControls(useDsl = false)
+}
+
+@Preview
+@Composable
+fun NewMotionMessagePreviewWithDsl() {
+ NewMotionMessageWithControls(useDsl = true)
+}
+
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalMotionApi::class)
+@Composable
+fun NewMotionMessageWithControls(
+ useDsl: Boolean
+) {
+ val initialLayout = NewMessageLayout.Full
+ val newMessageState = rememberNewMessageState(initialLayoutState = initialLayout)
+ val motionScene = if (useDsl) {
+ messageMotionSceneDsl(initialState = initialLayout)
+ } else {
+ messageMotionScene(initialState = initialLayout)
+ }
+ Column(Modifier.semantics { testTagsAsResourceId = true }) {
+ Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
+ TestableButton(
+ >
+ text = "Fab"
+ )
+ TestableButton(
+ >
+ text = "Full"
+ )
+ TestableButton(
+ >
+ text = "Mini"
+ )
+ }
+ NewMessageButton(
+ modifier = Modifier.fillMaxSize(),
+ motionScene = motionScene,
+ state = newMessageState,
+ )
+ }
+}
+
+@OptIn(ExperimentalMotionApi::class)
+@Composable
+private fun messageMotionSceneDsl(initialState: NewMessageLayout): MotionScene {
+ val startState = remember { initialState }
+ val endState = when (startState) {
+ NewMessageLayout.Fab -> NewMessageLayout.Full
+ NewMessageLayout.Mini -> NewMessageLayout.Fab
+ NewMessageLayout.Full -> NewMessageLayout.Fab
+ }
+
+ val primary = MaterialTheme.colors.primary
+ val primaryVariant = MaterialTheme.colors.primaryVariant
+ val >
+ val surface = MaterialTheme.colors.surface
+ val >
+
+ return MotionScene {
+ val box = createRefFor("box")
+ val minIcon = createRefFor("minIcon")
+ val editClose = createRefFor("editClose")
+ val title = createRefFor("title")
+ val content = createRefFor("content")
+
+ val fab = constraintSet(NewMessageLayout.Fab.name) {
+ constrain(box) {
+ width = Dimension.value(50.dp)
+ height = Dimension.value(50.dp)
+ end.linkTo(parent.end, 12.dp)
+ bottom.linkTo(parent.bottom, 12.dp)
+ customColor("background", primary)
+ }
+ constrain(minIcon) {
+ width = Dimension.value(40.dp)
+ height = Dimension.value(40.dp)
+
+ end.linkTo(editClose.start, 8.dp)
+ top.linkTo(editClose.top)
+ customColor("content", onPrimary)
+ }
+ constrain(editClose) {
+ width = Dimension.value(40.dp)
+ height = Dimension.value(40.dp)
+
+ centerTo(box)
+
+ customColor("content", onPrimary)
+ }
+ constrain(title) {
+ width = Dimension.fillToConstraints
+ top.linkTo(box.top)
+ bottom.linkTo(editClose.bottom)
+ start.linkTo(box.start, 8.dp)
+ end.linkTo(minIcon.start, 8.dp)
+ customColor("content", onPrimary)
+
+ visibility = Visibility.Gone
+ }
+ constrain(content) {
+ width = Dimension.fillToConstraints
+ height = Dimension.fillToConstraints
+ start.linkTo(box.start, 8.dp)
+ end.linkTo(box.end, 8.dp)
+
+ top.linkTo(editClose.bottom, 8.dp)
+ bottom.linkTo(box.bottom, 8.dp)
+
+ visibility = Visibility.Gone
+ }
+ }
+ val full = constraintSet(NewMessageLayout.Full.name) {
+ constrain(box) {
+ width = Dimension.fillToConstraints
+ height = Dimension.fillToConstraints
+ start.linkTo(parent.start, 12.dp)
+ end.linkTo(parent.end, 12.dp)
+ bottom.linkTo(parent.bottom, 12.dp)
+ top.linkTo(parent.top, 40.dp)
+ customColor("background", surface)
+ }
+ constrain(minIcon) {
+ width = Dimension.value(40.dp)
+ height = Dimension.value(40.dp)
+
+ end.linkTo(editClose.start, 8.dp)
+ top.linkTo(editClose.top)
+ customColor("content", onSurface)
+ }
+ constrain(editClose) {
+ width = Dimension.value(40.dp)
+ height = Dimension.value(40.dp)
+
+ end.linkTo(box.end, 4.dp)
+ top.linkTo(box.top, 4.dp)
+ customColor("content", onSurface)
+ }
+ constrain(title) {
+ width = Dimension.fillToConstraints
+ top.linkTo(box.top)
+ bottom.linkTo(editClose.bottom)
+ start.linkTo(box.start, 8.dp)
+ end.linkTo(minIcon.start, 8.dp)
+ customColor("content", onSurface)
+ }
+ constrain(content) {
+ width = Dimension.fillToConstraints
+ height = Dimension.fillToConstraints
+ start.linkTo(box.start, 8.dp)
+ end.linkTo(box.end, 8.dp)
+ top.linkTo(editClose.bottom, 8.dp)
+ bottom.linkTo(box.bottom, 8.dp)
+ }
+ }
+ val mini = constraintSet(NewMessageLayout.Mini.name) {
+ constrain(box) {
+ width = Dimension.value(220.dp)
+ height = Dimension.value(50.dp)
+
+ end.linkTo(parent.end, 12.dp)
+ bottom.linkTo(parent.bottom, 12.dp)
+
+ customColor("background", primaryVariant)
+ }
+ constrain(minIcon) {
+ width = Dimension.value(40.dp)
+ height = Dimension.value(40.dp)
+
+ end.linkTo(editClose.start, 8.dp)
+ top.linkTo(editClose.top)
+
+ rotationZ = 180f
+
+ customColor("content", onPrimary)
+ }
+ constrain(editClose) {
+ width = Dimension.value(40.dp)
+ height = Dimension.value(40.dp)
+
+ end.linkTo(box.end, 4.dp)
+ top.linkTo(box.top, 4.dp)
+ customColor("content", onPrimary)
+ }
+ constrain(title) {
+ width = Dimension.fillToConstraints
+ top.linkTo(box.top)
+ bottom.linkTo(editClose.bottom)
+ start.linkTo(box.start, 8.dp)
+ end.linkTo(minIcon.start, 8.dp)
+ customColor("content", onPrimary)
+ }
+ constrain(content) {
+ width = Dimension.fillToConstraints
+ start.linkTo(box.start, 8.dp)
+ end.linkTo(box.end, 8.dp)
+
+ top.linkTo(editClose.bottom, 8.dp)
+ bottom.linkTo(box.bottom, 8.dp)
+
+ visibility = Visibility.Gone
+ }
+ }
+
+ fun constraintSetFor(layoutState: NewMessageLayout) =
+ when (layoutState) {
+ NewMessageLayout.Full -> full
+ NewMessageLayout.Mini -> mini
+ NewMessageLayout.Fab -> fab
+ }
+ defaultTransition(
+ from = constraintSetFor(startState),
+ to = constraintSetFor(endState)
+ )
+ }
+}
+
+@OptIn(ExperimentalMotionApi::class)
+@Composable
+private fun messageMotionScene(initialState: NewMessageLayout): MotionScene {
+ val startState = remember { initialState }
+ val endState = when (startState) {
+ NewMessageLayout.Fab -> NewMessageLayout.Full
+ NewMessageLayout.Mini -> NewMessageLayout.Fab
+ NewMessageLayout.Full -> NewMessageLayout.Fab
+ }
+
+ val startStateName = startState.name
+ val endStateName = endState.name
+ val primary = MaterialTheme.colors.primary.toHexString()
+ val primaryVariant = MaterialTheme.colors.primaryVariant.toHexString()
+ val >
+ val surface = MaterialTheme.colors.surface.toHexString()
+ val >
+
+ return MotionScene(
+ content =
+ """
+ {
+ ConstraintSets: {
+ ${NewMessageLayout.Fab.name}: {
+ box: {
+ width: 50, height: 50,
+ end: ['parent', 'end', 12],
+ bottom: ['parent', 'bottom', 12],
+ custom: {
+ background: '#$primary'
+ }
+ },
+ minIcon: {
+ width: 40, height: 40,
+ end: ['editClose', 'start', 8],
+ top: ['editClose', 'top', 0],
+ visibility: 'gone',
+ custom: {
+ content: '#$onPrimary'
+ }
+ },
+ editClose: {
+ width: 40, height: 40,
+ centerHorizontally: 'box',
+ centerVertically: 'box',
+ custom: {
+ content: '#$onPrimary'
+ }
+ },
+ title: {
+ width: 'spread',
+ top: ['box', 'top', 0],
+ bottom: ['editClose', 'bottom', 0],
+ start: ['box', 'start', 8],
+ end: ['minIcon', 'start', 8],
+ custom: {
+ content: '#$onPrimary'
+ }
+
+ visibility: 'gone'
+ },
+ content: {
+ width: 'spread', height: 'spread',
+ start: ['box', 'start', 8],
+ end: ['box', 'end', 8],
+
+ top: ['editClose', 'bottom', 8],
+ bottom: ['box', 'bottom', 8],
+
+ visibility: 'gone'
+ }
+ },
+ ${NewMessageLayout.Full.name}: {
+ box: {
+ width: 'spread', height: 'spread',
+ start: ['parent', 'start', 12],
+ end: ['parent', 'end', 12],
+ bottom: ['parent', 'bottom', 12],
+ top: ['parent', 'top', 40],
+ custom: {
+ background: '#$surface'
+ }
+ },
+ minIcon: {
+ width: 40, height: 40,
+ end: ['editClose', 'start', 8],
+ top: ['editClose', 'top', 0],
+ custom: {
+ content: '#$onSurface'
+ }
+ },
+ editClose: {
+ width: 40, height: 40,
+ end: ['box', 'end', 4],
+ top: ['box', 'top', 4],
+ custom: {
+ content: '#$onSurface'
+ }
+ },
+ title: {
+ width: 'spread',
+ top: ['box', 'top', 0],
+ bottom: ['editClose', 'bottom', 0],
+ start: ['box', 'start', 8],
+ end: ['minIcon', 'start', 8],
+ custom: {
+ content: '#$onSurface'
+ }
+ },
+ content: {
+ width: 'spread', height: 'spread',
+ start: ['box', 'start', 8],
+ end: ['box', 'end', 8],
+
+ top: ['editClose', 'bottom', 8],
+ bottom: ['box', 'bottom', 8]
+ }
+ },
+ ${NewMessageLayout.Mini.name}: {
+ box: {
+ width: 180, height: 50,
+ bottom: ['parent', 'bottom', 12],
+ end: ['parent', 'end', 12],
+ custom: {
+ background: '#$primaryVariant'
+ }
+ },
+ minIcon: {
+ width: 40, height: 40,
+ end: ['editClose', 'start', 8],
+ top: ['editClose', 'top', 0],
+ rotationZ: 180,
+ custom: {
+ content: '#$onPrimary'
+ }
+ },
+ editClose: {
+ width: 40, height: 40,
+ end: ['box', 'end', 4],
+ top: ['box', 'top', 4],
+ custom: {
+ content: '#$onPrimary'
+ }
+ },
+ title: {
+ width: 'spread',
+ top: ['box', 'top', 0],
+ bottom: ['editClose', 'bottom', 0],
+ start: ['box', 'start', 8],
+ end: ['minIcon', 'start', 8],
+ custom: {
+ content: '#$onPrimary'
+ }
+ },
+ content: {
+ width: 'spread', height: 'spread',
+ start: ['box', 'start', 8],
+ end: ['box', 'end', 8],
+
+ top: ['editClose', 'bottom', 8],
+ bottom: ['box', 'bottom', 8],
+
+ visibility: 'gone'
+ }
+ }
+ },
+ Transitions: {
+ default: {
+ from: '$startStateName',
+ to: '$endStateName'
+ }
+ }
+ }
+ """
+ )
+}
+
+@OptIn(ExperimentalMotionApi::class)
+@Composable
+internal fun MotionLayoutScope.MotionMessageContent(
+ state: NewMessageState
+) {
+ val currentState = state.currentState
+ val focusManager = LocalFocusManager.current
+ val dialogName = remember(currentState) {
+ when (currentState) {
+ NewMessageLayout.Mini -> "Draft"
+ else -> "Message"
+ }
+ }
+ Surface(
+ modifier = Modifier.layoutId("box"),
+ color = motionColor(id = "box", name = "background"),
+ elevation = 4.dp,
+ shape = RoundedCornerShape(8.dp)
+ ) {}
+ ColorableIconButton(
+ modifier = Modifier.layoutId("editClose"),
+ imageVector = when (currentState) {
+ NewMessageLayout.Fab -> Icons.Default.Edit
+ else -> Icons.Default.Close
+ },
+ color = motionColor("editClose", "content"),
+ enabled = true
+ ) {
+ when (currentState) {
+ NewMessageLayout.Fab -> state.setToFull()
+ else -> state.setToFab()
+ }
+ }
+ ColorableIconButton(
+ modifier = Modifier.layoutId("minIcon"),
+ imageVector = Icons.Default.KeyboardArrowDown,
+ color = motionColor("minIcon", "content"),
+ enabled = true
+ ) {
+ when (currentState) {
+ NewMessageLayout.Full -> state.setToMini()
+ else -> state.setToFull()
+ }
+ }
+ CheapText(
+ text = dialogName,
+ modifier = Modifier.layoutId("title"),
+ color = motionColor("title", "content"),
+ style = MaterialTheme.typography.h6
+ )
+ MessageWidget(modifier = Modifier.layoutId("content"), >
+ focusManager.clearFocus()
+ state.setToFab()
+ })
+// MessageWidgetCol(
+// modifier = Modifier
+// .layoutId("content")
+// .padding(start = 4.dp, end = 4.dp, bottom = 4.dp)
+// )
+}
+
+@Composable
+private fun NewMessageButton(
+ modifier: Modifier = Modifier,
+ motionScene: MotionScene,
+ state: NewMessageState
+) {
+ val currentStateName = state.currentState.name
+ MotionLayout(
+ motionScene = motionScene,
+ animationSpec = tween(700),
+ constraintSetName = currentStateName,
+ modifier = modifier
+ ) {
+ MotionMessageContent(state = state)
+ }
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+internal fun ColorableIconButton(
+ modifier: Modifier,
+ imageVector: ImageVector,
+ color: Color,
+ enabled: Boolean,
+ onClick: () -> Unit
+) {
+ Surface(
+ modifier = modifier,
+ color = Color.Transparent,
+ contentColor = color,
+ >
+ enabled = enabled
+ ) {
+ Icon(
+ imageVector = imageVector,
+ contentDescription = null,
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+}
+
+// With column
+@Composable
+internal fun MessageWidgetCol(modifier: Modifier) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ TextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = "",
+ >
+ placeholder = {
+ Text("Recipients")
+ }
+ )
+ TextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = "",
+ >
+ placeholder = {
+ Text("Subject")
+ }
+ )
+ TextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(weight = 2.0f, fill = true),
+ value = "",
+ >
+ placeholder = {
+ Text("Message")
+ }
+ )
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ ) {
+ Button( /*TODO*/ }) {
+ Row {
+ Text(text = "Send")
+ Spacer(modifier = Modifier.width(8.dp))
+ Icon(
+ imageVector = Icons.Default.Send,
+ contentDescription = "Send Mail",
+ )
+ }
+ }
+ Button( /*TODO*/ }) {
+ Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = "Delete Draft",
+ )
+ }
+ }
+ }
+}
+
+// With ConstraintLayout
+@Preview
+@Composable
+private fun MessageWidgetPreview() {
+ MessageWidget(modifier = Modifier.fillMaxSize())
+}
+
+@Composable
+internal fun MessageWidget(
+ modifier: Modifier,
+ onDelete: () -> Unit = {}
+) {
+ val constraintSet = remember {
+ ConstraintSet(
+ """
+ {
+ gl1: { type: 'hGuideline', end: 50 },
+ recipient: {
+ top: ['parent', 'top', 2],
+ width: 'spread',
+ centerHorizontally: 'parent',
+ },
+ subject: {
+ top: ['recipient', 'bottom', 8],
+ width: 'spread',
+ centerHorizontally: 'parent',
+ },
+ message: {
+ height: 'spread',
+ width: 'spread',
+ centerHorizontally: 'parent',
+ top: ['subject', 'bottom', 8],
+ bottom: ['gl1', 'bottom', 4],
+ },
+ delete: {
+ height: 'spread',
+ top: ['gl1', 'bottom', 0],
+ bottom: ['parent', 'bottom', 4],
+ start: ['parent', 'start', 0]
+ },
+ send: {
+ height: 'spread',
+ top: ['gl1', 'bottom', 0],
+ bottom: ['parent', 'bottom', 4],
+ end: ['parent', 'end', 0]
+ }
+ }
+ """.trimIndent()
+ )
+ }
+ ConstraintLayout(
+ modifier = modifier.padding(top = 4.dp, start = 8.dp, end = 8.dp, bottom = 0.dp),
+ constraintSet = constraintSet
+ ) {
+ OutlinedTextField(
+ modifier = Modifier.layoutId("recipient"),
+ value = "",
+ >
+ label = {
+ CheapText("To")
+ }
+ )
+ OutlinedTextField(
+ modifier = Modifier.layoutId("subject"),
+ value = "",
+ >
+ label = {
+ CheapText("Subject")
+ }
+ )
+ OutlinedTextField(
+ modifier = Modifier
+ .layoutId("message")
+ .fillMaxHeight(),
+ value = "",
+ >
+ label = {
+ CheapText("Message")
+ }
+ )
+ Button(
+ modifier = Modifier.layoutId("send"),
+ // TODO: Do something different for Send onClick
+ ) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ CheapText(text = "Send")
+ Icon(
+ imageVector = Icons.Default.Send,
+ contentDescription = "Send Mail",
+ )
+ }
+ }
+ Button(
+ modifier = Modifier.layoutId("delete"),
+ >
+ ) {
+ Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = "Delete Draft",
+ )
+ }
+ }
+}
+
+/**
+ * [Text] Composable constrained to one line for better animation performance.
+ */
+@Composable
+private fun CheapText(
+ text: String,
+ modifier: Modifier = Modifier,
+ color: Color = Color.Unspecified,
+ style: TextStyle = LocalTextStyle.current,
+ overflow: TextOverflow = TextOverflow.Clip
+) {
+ Text(
+ text = text,
+ modifier = modifier,
+ color = color,
+ style = style,
+ maxLines = 1,
+ overflow = overflow,
+ )
+}
+
+private fun Color.toHexString() = toArgb().toUInt().toString(16)
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessageState.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessageState.kt
new file mode 100644
index 0000000..677182e
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessageState.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark.target.motionlayout.newmessage
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+
+@Composable
+internal fun rememberNewMessageState(
+ key: Any = Unit,
+ initialLayoutState: NewMessageLayout
+): NewMessageState {
+ return remember(key) { NewMessageState(initialLayoutState) }
+}
+
+internal class NewMessageState(initialLayoutState: NewMessageLayout) {
+ private var _currentState = mutableStateOf(initialLayoutState)
+
+ val currentState: NewMessageLayout
+ get() = _currentState.value
+
+ fun setToFull() {
+ _currentState.value = NewMessageLayout.Full
+ }
+
+ fun setToMini() {
+ _currentState.value = NewMessageLayout.Mini
+ }
+
+ fun setToFab() {
+ _currentState.value = NewMessageLayout.Fab
+ }
+}
+
+internal enum class NewMessageLayout {
+ Full,
+ Mini,
+ Fab
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/toolbar/CollapsibleToolbar.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/toolbar/CollapsibleToolbar.kt
new file mode 100644
index 0000000..50ab5bc
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/toolbar/CollapsibleToolbar.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark.target.motionlayout.toolbar
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.common.components.CardSample
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.common.components.OutlinedSearchBar
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.shadow
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.constraintlayout.compose.ConstraintSet
+import androidx.constraintlayout.compose.Dimension
+import androidx.constraintlayout.compose.ExperimentalMotionApi
+import androidx.constraintlayout.compose.MotionLayout
+import kotlin.math.absoluteValue
+
+@Preview
+@Composable
+fun MotionCollapseToolbarPreview() {
+ MotionCollapseToolbar(
+ Modifier
+ .fillMaxSize()
+ )
+}
+
+@Composable
+fun MotionCollapseToolbar(
+ modifier: Modifier = Modifier,
+) {
+ val collapsibleToolbarState = rememberCollapsibleToolbarState()
+ Box(modifier.nestedScroll(collapsibleToolbarState)) {
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxWidth()
+ .testTag("LazyColumn"),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ contentPadding = PaddingValues(top = collapsibleToolbarState.currentHeight)
+ ) {
+ items(count = 20) {
+ CardSample(
+ Modifier
+ .width(250.dp)
+ .height(80.dp)
+ .shadow(4.dp, RoundedCornerShape(10.dp))
+ .background(Color.White, RoundedCornerShape(10.dp))
+ .padding(4.dp)
+ )
+ }
+ }
+ CollapsibleToolbar(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(210.dp),
+ toolbarState = collapsibleToolbarState
+ )
+ }
+}
+
+private val expandedCSet = ConstraintSet {
+ val title = createRefFor("title")
+ val toolbar = createRefFor("toolbar")
+ val container = createRefFor("container")
+
+ constrain(container) {
+ width = Dimension.fillToConstraints
+ height = Dimension.value(200.dp)
+
+ start.linkTo(parent.start)
+ end.linkTo(parent.end)
+ top.linkTo(parent.top)
+ }
+
+ constrain(title) {
+ centerHorizontallyTo(container)
+ top.linkTo(container.top)
+
+ scaleX = 1.5f
+ scaleY = 1.5f
+ }
+ constrain(toolbar) {
+ width = Dimension.fillToConstraints
+ centerHorizontallyTo(container)
+ bottom.linkTo(container.bottom)
+ }
+}
+
+private val collapsedCSet = ConstraintSet(expandedCSet) {
+ val title = createRefFor("title")
+ val toolbar = createRefFor("toolbar")
+ val container = createRefFor("container")
+
+ constrain(container) {
+ height = Dimension.value(70.dp)
+ }
+
+ constrain(title) {
+ clearConstraints()
+ resetTransforms()
+
+ top.linkTo(toolbar.top)
+ start.linkTo(container.start)
+ bottom.linkTo(toolbar.bottom)
+ }
+
+ constrain(toolbar) {
+ clearHorizontal()
+
+ top.linkTo(container.top)
+ end.linkTo(container.end)
+ start.linkTo(title.end, 12.dp)
+ }
+}
+
+@Composable
+fun rememberCollapsibleToolbarState(): CollapsibleToolbarState {
+ val density = LocalDensity.current
+ return remember { CollapsibleToolbarState(density) }
+}
+
+class CollapsibleToolbarState internal constructor(
+ private val density: Density
+) : NestedScrollConnection {
+ private val maximumHeight = 200.dp
+ private var minimumHeight = 70.dp
+
+ private val consumableHeight = maximumHeight - minimumHeight
+
+ internal val currentHeight: Dp
+ get() = maximumHeight - consumedHeight
+
+ internal val progress: Float
+ get() = (consumedHeight.value / consumableHeight.value)
+
+ private var consumedHeight by mutableStateOf(0.dp)
+
+ private fun consumeDeltaScroll(scrollDelta: Float): Float {
+ val remainingHeight = consumableHeight - consumedHeight
+ val remainingPx = with(density) { remainingHeight.toPx() }
+ val maxHeightPx = with(density) { consumableHeight.toPx() }
+
+ // TODO: Simplify, no need to differentiate positive/negative scroll
+ if (scrollDelta < 0) {
+ // Going down
+ val diff = remainingPx + scrollDelta
+ if (diff > 0) {
+ consumedHeight += with(density) { scrollDelta.absoluteValue.toDp() }
+ return scrollDelta
+ } else {
+ consumedHeight = consumableHeight
+ return remainingPx * -1f
+ }
+ } else if (scrollDelta > 0) {
+ // Going up
+ val diff = remainingPx + scrollDelta
+ if (diff < maxHeightPx) {
+ consumedHeight -= with(density) { scrollDelta.absoluteValue.toDp() }
+ return scrollDelta
+ } else {
+ val toConsume = with(density) { consumedHeight.toPx() }
+ consumedHeight = 0.dp
+ return toConsume
+ }
+ } else {
+ return 0f
+ }
+ }
+
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ consumeDeltaScroll(available.y)
+ return Offset.Zero
+ }
+}
+
+@OptIn(ExperimentalMotionApi::class)
+@Composable
+fun CollapsibleToolbar(
+ modifier: Modifier = Modifier,
+ toolbarState: CollapsibleToolbarState
+) {
+ MotionLayout(
+ modifier = modifier,
+ start = expandedCSet,
+ end = collapsedCSet,
+ progress = toolbarState.progress
+ ) {
+ Box(
+ Modifier
+ .layoutId("container")
+ .background(Color.White)
+ )
+ Text(
+ modifier = Modifier.layoutId("title"),
+ text = "MotionLayout",
+ maxLines = 1,
+ fontSize = 18.sp
+ )
+ OutlinedSearchBar(modifier = Modifier.layoutId("toolbar"))
+ }
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_1.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_1.png
new file mode 100644
index 0000000..22ff80a
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_1.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_10.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_10.png
new file mode 100644
index 0000000..f65c0b2
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_10.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_11.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_11.png
new file mode 100644
index 0000000..18b842b
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_11.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_12.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_12.png
new file mode 100644
index 0000000..fd71f8c
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_12.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_13.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_13.png
new file mode 100644
index 0000000..b97517f
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_13.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_14.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_14.png
new file mode 100644
index 0000000..fb7a871
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_14.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_15.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_15.png
new file mode 100644
index 0000000..2860447
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_15.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_16.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_16.png
new file mode 100644
index 0000000..52ee27a
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_16.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_2.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_2.png
new file mode 100644
index 0000000..0126588d
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_2.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_3.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_3.png
new file mode 100644
index 0000000..8ecc7fb
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_3.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_4.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_4.png
new file mode 100644
index 0000000..a04de0a
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_4.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_5.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_5.png
new file mode 100644
index 0000000..b0ebf8c
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_5.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_6.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_6.png
new file mode 100644
index 0000000..410f7ee
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_6.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_7.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_7.png
new file mode 100644
index 0000000..42df954
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_7.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_8.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_8.png
new file mode 100644
index 0000000..ea62706d
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_8.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_9.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_9.png
new file mode 100644
index 0000000..e612b7a
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/avatar_9.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/ic_launcher_background.xml b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..2aea1e0
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<vector
+ android:height="108dp"
+ android:width="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#008577"
+ android:pathData="M0,0h108v108h-108z"/>
+ <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+</vector>
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/ic_launcher_foreground.xml b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..f5d459c
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,50 @@
+<!--
+ Copyright 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108">
+ <path
+ android:fillType="evenOdd"
+ android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="78.5885"
+ android:endY="90.9159"
+ android:startX="48.7653"
+ android:startY="61.0927"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1" />
+</vector>
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-anydpi/ic_launcher.xml b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-anydpi/ic_launcher.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-anydpi/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@drawable/ic_launcher_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-hdpi/ic_launcher.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..898f3ed
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-mdpi/ic_launcher.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..64ba76f
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-xhdpi/ic_launcher.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..e5ed4659
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-xxhdpi/ic_launcher.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b0907ca
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2c18de9
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/build.gradle b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/build.gradle
new file mode 100644
index 0000000..00320cd
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/build.gradle
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.test")
+ id("kotlin-android")
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 26
+ }
+ namespace "androidx.constraintlayout.compose.integration.macrobenchmark"
+
+ // We need animations to work for MotionLayout
+ testOptions.animationsDisabled false
+
+ targetProjectPath = ":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark-target"
+ // Enable the benchmark to run separately from the app process
+ experimentalProperties["android.experimental.self-instrumenting"] = true
+
+ buildTypes {
+ // declare a build type to match the target app's build type
+ benchmark {
+ debuggable = true
+ signingConfig = debug.signingConfig
+ // Selects release buildType if the benchmark buildType not available in other modules.
+ matchingFallbacks = ['release']
+ }
+ }
+}
+
+androidComponents {
+ beforeVariants(selector().all()) {
+ // enable only the benchmark buildType, since we only want to measure close to release performance
+ enabled = buildType == 'benchmark'
+ }
+}
+
+dependencies {
+ implementation(project(":benchmark:benchmark-junit4"))
+ implementation(project(":benchmark:benchmark-macro-junit4"))
+ implementation(project(":internal-testutils-macrobenchmark"))
+ implementation(project(":internal-testutils-runtime"))
+ implementation(libs.testRules)
+ implementation(libs.testExtJunit)
+ implementation(libs.testCore)
+ implementation(libs.testRunner)
+ implementation(libs.testUiautomator)
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8e90956
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest />
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
new file mode 100644
index 0000000..d15fad9
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.integration.macrobenchmark
+
+import android.content.Intent
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.MacrobenchmarkScope
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.uiautomator.By
+import kotlin.math.roundToInt
+import org.junit.Rule
+import org.junit.Test
+
+private const val PACKAGE_NAME =
+ "androidx.constraintlayout.compose.integration.macrobenchmark.target"
+private const val ACTION =
+ "androidx.constraintlayout.compose.integration.macrobenchmark.target.MOTION_LAYOUT_ACTIVITY"
+
+/**
+ * Run locally using `./gradlew :constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark:connectedCheck`
+ */
+@LargeTest
+class MotionLayoutBenchmark {
+
+ @get:Rule
+ val benchmarkRule = MacrobenchmarkRule()
+
+ /**
+ * Transitions the Layout through its three different ConstraintSets using the MotionScene DSL.
+ */
+ @Test
+ fun messageDsl() = benchmarkRule.testNewMessage(NewMessageMode.Dsl)
+
+ /**
+ * Transitions the Layout through its three different ConstraintSets using the MotionScene JSON.
+ */
+ @Test
+ fun messageJson() = benchmarkRule.testNewMessage(NewMessageMode.Json)
+
+ @Test
+ fun collapsibleToolbar() = benchmarkRule.testCollapsibleToolbar()
+
+ /**
+ * The base method to benchmark FrameTimings of a Composable from the macrobenchmark-app module.
+ *
+ * [composableName] should be a registered Composable in **MotionLayoutBenchmarkActivity**
+ *
+ * The [setupBlock] is run after the activity starts with the given [composableName]. You may use
+ * this as a chance to set the UI in the way you wish it to be measured.
+ *
+ * The [measureBlock] is called after the setup. [FrameTimingMetric] measures UI performance during
+ * this block.
+ */
+ private fun MacrobenchmarkRule.motionBenchmark(
+ composableName: String,
+ setupBlock: MacrobenchmarkScope.() -> Unit = {},
+ measureBlock: MacrobenchmarkScope.() -> Unit
+ ) {
+ measureRepeated(
+ packageName = PACKAGE_NAME,
+ metrics = listOf(FrameTimingMetric()),
+ compilationMode = CompilationMode.DEFAULT,
+ iterations = 10,
+ // HOT causes issues with the measure block logic where multiple click actions are
+ // triggered at once
+ startupMode = StartupMode.WARM,
+ setupBlock = {
+ val intent = Intent()
+ intent.action = ACTION
+ intent.putExtra("ComposableName", composableName)
+ startActivityAndWait(intent)
+ device.waitForIdle()
+ setupBlock()
+ },
+ measureBlock = measureBlock
+ )
+ }
+
+ private fun MacrobenchmarkRule.testCollapsibleToolbar() =
+ motionBenchmark("CollapsibleToolbar") {
+ val column = device.findObject(By.res("LazyColumn"))
+ val bounds = column.visibleBounds
+
+ // Margin to reduce the amount of pixels scrolled
+ val vMargin = (bounds.height() * 0.2f).roundToInt()
+ val x = (bounds.width() * 0.5f).roundToInt()
+ val y1 = bounds.bottom - vMargin
+ val y2 = bounds.top + vMargin
+
+ // Swipe down
+ device.swipe(x, y1, x, y2, 50)
+ device.waitForIdle()
+
+ // Swipe up
+ device.swipe(x, y2, x, y1, 50)
+ device.waitForIdle()
+ }
+
+ private fun MacrobenchmarkRule.testNewMessage(
+ mode: NewMessageMode
+ ) {
+ motionBenchmark(
+ mode.composableName
+ ) {
+ val toFab = device.findObject(By.res("Fab"))
+ val toFull = device.findObject(By.res("Full"))
+ val toMini = device.findObject(By.res("Mini"))
+
+ toMini.click()
+ device.waitForIdle()
+
+ toFab.click()
+ device.waitForIdle()
+
+ toFull.click()
+ device.waitForIdle()
+ }
+ }
+
+ internal enum class NewMessageMode(val composableName: String) {
+ Json("NewMessageJson"),
+ Dsl("NewMessageDsl")
+ }
+}
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ChainsTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ChainsTest.kt
index bbfe49b..5fb7268 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ChainsTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ChainsTest.kt
@@ -21,7 +21,9 @@
import androidx.compose.foundation.layout.size
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.test.assertHeightIsEqualTo
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.dp
@@ -114,7 +116,11 @@
val boxSizes = arrayOf(10.dp, 20.dp, 30.dp)
val margin = 10.dp
rule.setContent {
- ConstraintLayout(Modifier.size(rootSize)) {
+ ConstraintLayout(
+ Modifier
+ .background(Color.LightGray)
+ .size(rootSize)
+ ) {
val (box0, box1, box2) = createRefs()
val chain0 = createHorizontalChain(box0, box1, chainStyle = ChainStyle.Packed)
constrain(chain0) {
@@ -170,4 +176,288 @@
rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(box1Left, box1Top)
rule.onNodeWithTag("box2").assertPositionInRootIsEqualTo(margin, margin)
}
+
+ @Test
+ fun testHorizontalPacked_withMargins() {
+ val rootSize = 100.dp
+ val boxSizes = arrayOf(10.dp, 20.dp, 30.dp)
+ val boxMargin = 5.dp
+ val boxGoneMargin = 7.dp
+ val constraintSet = ConstraintSet {
+ val box0 = createRefFor("box0")
+ val boxGone = createRefFor("boxGone")
+ val box1 = createRefFor("box1")
+ val box2 = createRefFor("box2")
+
+ createHorizontalChain(
+ box0.withChainParams(
+ startMargin = 0.dp,
+ endMargin = boxMargin, // Not applied since the next box is Gone
+ endGoneMargin = boxGoneMargin
+ ),
+ boxGone.withChainParams(
+ // None of these margins should apply since it's Gone
+ startMargin = 100.dp,
+ endMargin = 100.dp,
+ startGoneMargin = 100.dp,
+ endGoneMargin = 100.dp
+ ),
+ box1,
+ box2.withHorizontalChainParams(startMargin = boxMargin, endMargin = 0.dp),
+ chainStyle = ChainStyle.Packed
+ )
+
+ constrain(box0) {
+ width = Dimension.value(boxSizes[0])
+ height = Dimension.value(boxSizes[0])
+ centerVerticallyTo(parent)
+ }
+ constrain(boxGone) {
+ width = Dimension.value(boxSizes[1])
+ height = Dimension.value(boxSizes[1])
+ centerVerticallyTo(box0)
+
+ visibility = Visibility.Gone
+ }
+ constrain(box1) {
+ width = Dimension.value(boxSizes[1])
+ height = Dimension.value(boxSizes[1])
+ centerVerticallyTo(box0)
+ }
+ constrain(box2) {
+ width = Dimension.value(boxSizes[2])
+ height = Dimension.value(boxSizes[2])
+ centerVerticallyTo(box0)
+ }
+ }
+ rule.setContent {
+ ConstraintLayout(
+ modifier = Modifier
+ .background(Color.LightGray)
+ .size(rootSize),
+ constraintSet = constraintSet
+ ) {
+ Box(
+ modifier = Modifier
+ .background(Color.Red)
+ .layoutTestId("box0")
+ )
+ Box(
+ modifier = Modifier
+ .background(Color.Blue)
+ .layoutTestId("box1")
+ )
+ Box(
+ modifier = Modifier
+ .background(Color.Green)
+ .layoutTestId("box2")
+ )
+ }
+ }
+ rule.waitForIdle()
+
+ val totalMargins = boxMargin + boxGoneMargin
+ val totalChainSpace = boxSizes[0] + boxSizes[1] + boxSizes[2] + totalMargins
+
+ val box0Left = (rootSize - totalChainSpace) * 0.5f
+ val box0Top = (rootSize - boxSizes[0]) * 0.5f
+
+ val box1Left = box0Left + boxSizes[0] + boxGoneMargin
+ val box1Top = (rootSize - boxSizes[1]) * 0.5f
+
+ val box2Left = box1Left + boxSizes[1] + boxMargin
+ val box2Top = (rootSize - boxSizes[2]) * 0.5f
+
+ rule.onNodeWithTag("box0").assertPositionInRootIsEqualTo(box0Left, box0Top)
+ rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(box1Left, box1Top)
+ rule.onNodeWithTag("box2").assertPositionInRootIsEqualTo(box2Left, box2Top)
+ }
+
+ @Test
+ fun testVerticalPacked_withMargins() {
+ val rootSize = 100.dp
+ val boxSizes = arrayOf(10.dp, 20.dp, 30.dp)
+ val boxMargin = 5.dp
+ val boxGoneMargin = 7.dp
+ val constraintSet = ConstraintSet {
+ val box0 = createRefFor("box0")
+ val boxGone = createRefFor("boxGone")
+ val box1 = createRefFor("box1")
+ val box2 = createRefFor("box2")
+
+ createVerticalChain(
+ box0.withChainParams(
+ topMargin = 0.dp,
+ bottomMargin = boxMargin, // Not applied since the next box is Gone
+ bottomGoneMargin = boxGoneMargin
+ ),
+ boxGone.withChainParams(
+ // None of these margins should apply since it's Gone
+ topMargin = 100.dp,
+ bottomMargin = 100.dp,
+ topGoneMargin = 100.dp,
+ bottomGoneMargin = 100.dp
+ ),
+ box1,
+ box2.withVerticalChainParams(topMargin = boxMargin, bottomMargin = 0.dp),
+ chainStyle = ChainStyle.Packed
+ )
+
+ constrain(box0) {
+ width = Dimension.value(boxSizes[0])
+ height = Dimension.value(boxSizes[0])
+ centerHorizontallyTo(parent)
+ }
+ constrain(boxGone) {
+ width = Dimension.value(100.dp) // Dimensions won't matter since it's Gone
+ height = Dimension.value(100.dp) // Dimensions won't matter since it's Gone
+ centerHorizontallyTo(box0)
+
+ visibility = Visibility.Gone
+ }
+ constrain(box1) {
+ width = Dimension.value(boxSizes[1])
+ height = Dimension.value(boxSizes[1])
+ centerHorizontallyTo(box0)
+ }
+ constrain(box2) {
+ width = Dimension.value(boxSizes[2])
+ height = Dimension.value(boxSizes[2])
+ centerHorizontallyTo(box0)
+ }
+ }
+ rule.setContent {
+ ConstraintLayout(
+ modifier = Modifier
+ .background(Color.LightGray)
+ .size(rootSize),
+ constraintSet = constraintSet
+ ) {
+ Box(
+ modifier = Modifier
+ .background(Color.Red)
+ .layoutTestId("box0")
+ )
+ Box(
+ modifier = Modifier
+ .background(Color.Blue)
+ .layoutTestId("box1")
+ )
+ Box(
+ modifier = Modifier
+ .background(Color.Green)
+ .layoutTestId("box2")
+ )
+ }
+ }
+ rule.waitForIdle()
+
+ val totalMargins = boxMargin + boxGoneMargin
+ val totalChainSpace = boxSizes[0] + boxSizes[1] + boxSizes[2] + totalMargins
+
+ val box0Left = (rootSize - boxSizes[0]) * 0.5f
+ val box0Top = (rootSize - totalChainSpace) * 0.5f
+
+ val box1Left = (rootSize - boxSizes[1]) * 0.5f
+ val box1Top = box0Top + boxSizes[0] + boxGoneMargin
+
+ val box2Left = (rootSize - boxSizes[2]) * 0.5f
+ val box2Top = box1Top + boxSizes[1] + boxMargin
+
+ rule.onNodeWithTag("box0").assertPositionInRootIsEqualTo(box0Left, box0Top)
+ rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(box1Left, box1Top)
+ rule.onNodeWithTag("box2").assertPositionInRootIsEqualTo(box2Left, box2Top)
+ }
+
+ @Test
+ fun testHorizontalWeight_withConstraintSet() {
+ val rootSize = 100.dp
+ val boxSize = 10.dp
+
+ rule.setContent {
+ ConstraintLayout(
+ modifier = Modifier
+ .background(Color.LightGray)
+ .size(rootSize),
+ constraintSet = ConstraintSet {
+ val box0 = createRefFor("box0")
+ val box1 = createRefFor("box1")
+
+ constrain(box0) {
+ width = Dimension.fillToConstraints
+ height = Dimension.value(boxSize)
+
+ horizontalChainWeight = 1.0f
+ verticalChainWeight = 2.0f // Ignored in horizontal chain
+ }
+ constrain(box1) {
+ width = Dimension.fillToConstraints
+ height = Dimension.value(boxSize)
+ }
+ createHorizontalChain(box0, box1.withChainParams(weight = 0.5f))
+ }
+ ) {
+ Box(
+ modifier = Modifier
+ .background(Color.Red)
+ .layoutTestId("box0")
+ )
+ Box(
+ modifier = Modifier
+ .background(Color.Blue)
+ .layoutTestId("box1")
+ )
+ }
+ }
+ rule.waitForIdle()
+ rule.onNodeWithTag("box0").assertWidthIsEqualTo(rootSize * 0.667f)
+ rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(rootSize * 0.667f, 0.dp)
+ rule.onNodeWithTag("box1").assertWidthIsEqualTo(rootSize * 0.334f)
+ }
+
+ @Test
+ fun testVerticalWeight_withConstraintSet() {
+ val rootSize = 100.dp
+ val boxSize = 10.dp
+
+ rule.setContent {
+ ConstraintLayout(
+ modifier = Modifier
+ .background(Color.LightGray)
+ .size(rootSize),
+ constraintSet = ConstraintSet {
+ val box0 = createRefFor("box0")
+ val box1 = createRefFor("box1")
+
+ constrain(box0) {
+ width = Dimension.value(boxSize)
+ height = Dimension.fillToConstraints
+
+ horizontalChainWeight = 2.0f // Ignored in vertical chain
+ verticalChainWeight = 1.0f
+ }
+ constrain(box1) {
+ width = Dimension.value(boxSize)
+ height = Dimension.fillToConstraints
+ }
+ createVerticalChain(box0, box1.withChainParams(weight = 0.5f))
+ }
+ ) {
+ Box(
+ modifier = Modifier
+ .background(Color.Red)
+ .layoutTestId("box0")
+ )
+ Box(
+ modifier = Modifier
+ .background(Color.Blue)
+ .layoutTestId("box1")
+ )
+ }
+ }
+ rule.waitForIdle()
+ rule.onNodeWithTag("box0").assertHeightIsEqualTo(rootSize * 0.667f)
+ rule.onNodeWithTag("box1").assertPositionInRootIsEqualTo(0.dp, rootSize * 0.667f)
+ rule.onNodeWithTag("box1").assertHeightIsEqualTo(rootSize * 0.334f)
+ }
}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
index 5c2b507..c93b13f 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
@@ -1663,4 +1663,68 @@
rule.onNodeWithTag(boxTag1).assertPositionInRootIsEqualTo(29.5.dp, 0.dp)
rule.onNodeWithTag(boxTag2).assertPositionInRootIsEqualTo(60.dp, 0.dp)
}
+
+ @Test
+ fun testBias_withConstraintSet() {
+ val rootSize = 100.dp
+ val boxSize = 10.dp
+ val horBias = 0.2f
+ val verBias = 1f - horBias
+ rule.setContent {
+ ConstraintLayout(
+ modifier = Modifier.size(rootSize),
+ constraintSet = ConstraintSet {
+ constrain(createRefFor("box")) {
+ width = Dimension.value(boxSize)
+ height = Dimension.value(boxSize)
+
+ centerTo(parent)
+ horizontalBias = horBias
+ verticalBias = verBias
+ }
+ }) {
+ Box(
+ modifier = Modifier
+ .background(Color.Red)
+ .layoutTestId("box")
+ )
+ }
+ }
+ rule.waitForIdle()
+ rule.onNodeWithTag("box").assertPositionInRootIsEqualTo(
+ (rootSize - boxSize) * 0.2f,
+ (rootSize - boxSize) * 0.8f
+ )
+ }
+
+ @Test
+ fun testBias_withInlineDsl() {
+ val rootSize = 100.dp
+ val boxSize = 10.dp
+ val horBias = 0.2f
+ val verBias = 1f - horBias
+ rule.setContent {
+ ConstraintLayout(Modifier.size(rootSize)) {
+ val box = createRef()
+ Box(
+ modifier = Modifier
+ .background(Color.Red)
+ .constrainAs(box) {
+ width = Dimension.value(boxSize)
+ height = Dimension.value(boxSize)
+
+ centerTo(parent)
+ horizontalBias = horBias
+ verticalBias = verBias
+ }
+ .layoutTestId("box")
+ )
+ }
+ }
+ rule.waitForIdle()
+ rule.onNodeWithTag("box").assertPositionInRootIsEqualTo(
+ (rootSize - boxSize) * 0.2f,
+ (rootSize - boxSize) * 0.8f
+ )
+ }
}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstrainScope.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstrainScope.kt
index 4e171e2..fb1c981 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstrainScope.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstrainScope.kt
@@ -134,7 +134,7 @@
set(value) {
field = value
addTransform {
- if (visibility != Visibility.Invisible) {
+ if (this@ConstrainScope.visibility != Visibility.Invisible) {
// A bit of a hack, this behavior is not defined in :core
// Invisible should override alpha
alpha(value)
@@ -260,6 +260,44 @@
}
}
+ /**
+ * Applied when the widget has constraints on the [start] and [end] anchors. It defines the
+ * position of the widget relative to the space within the constraints, where `0f` is the
+ * left-most position and `1f` is the right-most position.
+ *
+ *
+ *
+ * When layout direction is RTL, the value of the bias is effectively inverted.
+ *
+ * E.g.: For `horizontalBias = 0.3f`, `0.7f` is used for RTL.
+ *
+ *
+ *
+ * Note that the bias may also be applied with calls such as [linkTo].
+ */
+ @FloatRange(0.0, 1.0)
+ var horizontalBias: Float = 0.5f
+ set(value) {
+ field = value
+ tasks.add { state ->
+ state.constraints(id).horizontalBias(value)
+ }
+ }
+
+ /**
+ * Applied when the widget has constraints on the [top] and [bottom] anchors. It defines the
+ * position of the widget relative to the space within the constraints, where `0f` is the
+ * top-most position and `1f` is the bottom-most position.
+ */
+ @FloatRange(0.0, 1.0)
+ var verticalBias: Float = 0.5f
+ set(value) {
+ field = value
+ tasks.add { state ->
+ state.constraints(id).verticalBias(value)
+ }
+ }
+
private fun addTransform(change: ConstraintReference.() -> Unit) =
tasks.add { state -> change(state.constraints(id)) }
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
index 3193e55..d086df2 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
@@ -583,7 +583,7 @@
updateHelpersHashCode(16)
elements.forEach { updateHelpersHashCode(it.hashCode()) }
- return ConstrainedLayoutReference(id)
+ return ConstrainedLayoutReference(id)
}
/**
@@ -592,7 +592,6 @@
* Use [constrain] with the resulting [HorizontalChainReference] to modify the start/left and
* end/right constraints of this chain.
*/
- // TODO(popam, b/157783937): this API should be improved
fun createHorizontalChain(
vararg elements: LayoutReference,
chainStyle: ChainStyle = ChainStyle.Spread
@@ -603,7 +602,17 @@
id,
androidx.constraintlayout.core.state.State.Helper.HORIZONTAL_CHAIN
) as androidx.constraintlayout.core.state.helpers.HorizontalChainReference
- helper.add(*(elements.map { it.id }.toTypedArray()))
+ elements.forEach { chainElement ->
+ val elementParams = chainElement.getHelperParams() ?: ChainParams.Default
+ helper.addChainElement(
+ chainElement.id,
+ elementParams.weight,
+ state.convertDimension(elementParams.startMargin).toFloat(),
+ state.convertDimension(elementParams.endMargin).toFloat(),
+ state.convertDimension(elementParams.startGoneMargin).toFloat(),
+ state.convertDimension(elementParams.endGoneMargin).toFloat()
+ )
+ }
helper.style(chainStyle.style)
helper.apply()
if (chainStyle.bias != null) {
@@ -622,7 +631,6 @@
* Use [constrain] with the resulting [VerticalChainReference] to modify the top and
* bottom constraints of this chain.
*/
- // TODO(popam, b/157783937): this API should be improved
fun createVerticalChain(
vararg elements: LayoutReference,
chainStyle: ChainStyle = ChainStyle.Spread
@@ -633,7 +641,17 @@
id,
androidx.constraintlayout.core.state.State.Helper.VERTICAL_CHAIN
) as androidx.constraintlayout.core.state.helpers.VerticalChainReference
- helper.add(*(elements.map { it.id }.toTypedArray()))
+ elements.forEach { chainElement ->
+ val elementParams = chainElement.getHelperParams() ?: ChainParams.Default
+ helper.addChainElement(
+ chainElement.id,
+ elementParams.weight,
+ state.convertDimension(elementParams.topMargin).toFloat(),
+ state.convertDimension(elementParams.bottomMargin).toFloat(),
+ state.convertDimension(elementParams.topGoneMargin).toFloat(),
+ state.convertDimension(elementParams.bottomGoneMargin).toFloat()
+ )
+ }
helper.style(chainStyle.style)
helper.apply()
if (chainStyle.bias != null) {
@@ -645,6 +663,147 @@
updateHelpersHashCode(chainStyle.hashCode())
return VerticalChainReference(id)
}
+
+ /**
+ * Sets the parameters that are used by chains to customize the resulting layout.
+ *
+ * Use margins to customize the space between widgets in the chain.
+ *
+ * Use weight to distribute available space to each widget when their dimensions are not
+ * fixed.
+ *
+ *
+ *
+ * Similarly named parameters available from [ConstrainScope.linkTo] are ignored in
+ * Chains.
+ *
+ * Since margins are only for widgets within the chain: Top, Start and End, Bottom margins are
+ * ignored when the widget is the first or the last element in the chain, respectively.
+ *
+ * @param startMargin Added space from the start of this widget to the previous widget
+ * @param topMargin Added space from the top of this widget to the previous widget
+ * @param endMargin Added space from the end of this widget to the next widget
+ * @param bottomMargin Added space from the bottom of this widget to the next widget
+ * @param startGoneMargin Added space from the start of this widget when the previous widget has [Visibility.Gone]
+ * @param topGoneMargin Added space from the top of this widget when the previous widget has [Visibility.Gone]
+ * @param endGoneMargin Added space from the end of this widget when the next widget has [Visibility.Gone]
+ * @param bottomGoneMargin Added space from the bottom of this widget when the next widget has [Visibility.Gone]
+ * @param weight Defines the proportion of space (relative to the total weight) occupied by this
+ * layout when the corresponding dimension is not a fixed value.
+ * @return The same [LayoutReference] instance with the applied values
+ */
+ fun LayoutReference.withChainParams(
+ startMargin: Dp = 0.dp,
+ topMargin: Dp = 0.dp,
+ endMargin: Dp = 0.dp,
+ bottomMargin: Dp = 0.dp,
+ startGoneMargin: Dp = 0.dp,
+ topGoneMargin: Dp = 0.dp,
+ endGoneMargin: Dp = 0.dp,
+ bottomGoneMargin: Dp = 0.dp,
+ weight: Float = Float.NaN,
+ ): LayoutReference =
+ this.apply {
+ setHelperParams(
+ ChainParams(
+ startMargin = startMargin,
+ topMargin = topMargin,
+ endMargin = endMargin,
+ bottomMargin = bottomMargin,
+ startGoneMargin = startGoneMargin,
+ topGoneMargin = topGoneMargin,
+ endGoneMargin = endGoneMargin,
+ bottomGoneMargin = bottomGoneMargin,
+ weight = weight
+ )
+ )
+ }
+
+ /**
+ * Sets the parameters that are used by horizontal chains to customize the resulting layout.
+ *
+ * Use margins to customize the space between widgets in the chain.
+ *
+ * Use weight to distribute available space to each widget when their horizontal dimension is
+ * not fixed.
+ *
+ *
+ *
+ * Similarly named parameters available from [ConstrainScope.linkTo] are ignored in
+ * Chains.
+ *
+ * Since margins are only for widgets within the chain: Start and End margins are
+ * ignored when the widget is the first or the last element in the chain, respectively.
+ *
+ * @param startMargin Added space from the start of this widget to the previous widget
+ * @param endMargin Added space from the end of this widget to the next widget
+ * @param startGoneMargin Added space from the start of this widget when the previous widget has [Visibility.Gone]
+ * @param endGoneMargin Added space from the end of this widget when the next widget has [Visibility.Gone]
+ * @param weight Defines the proportion of space (relative to the total weight) occupied by this
+ * layout when the width is not a fixed dimension.
+ * @return The same [LayoutReference] instance with the applied values
+ */
+ fun LayoutReference.withHorizontalChainParams(
+ startMargin: Dp = 0.dp,
+ endMargin: Dp = 0.dp,
+ startGoneMargin: Dp = 0.dp,
+ endGoneMargin: Dp = 0.dp,
+ weight: Float = Float.NaN
+ ): LayoutReference =
+ withChainParams(
+ startMargin = startMargin,
+ topMargin = 0.dp,
+ endMargin = endMargin,
+ bottomMargin = 0.dp,
+ startGoneMargin = startGoneMargin,
+ topGoneMargin = 0.dp,
+ endGoneMargin = endGoneMargin,
+ bottomGoneMargin = 0.dp,
+ weight = weight
+ )
+
+ /**
+ * Sets the parameters that are used by vertical chains to customize the resulting layout.
+ *
+ * Use margins to customize the space between widgets in the chain.
+ *
+ * Use weight to distribute available space to each widget when their vertical dimension is not
+ * fixed.
+ *
+ *
+ *
+ * Similarly named parameters available from [ConstrainScope.linkTo] are ignored in
+ * Chains.
+ *
+ * Since margins are only for widgets within the chain: Top and Bottom margins are
+ * ignored when the widget is the first or the last element in the chain, respectively.
+ *
+ * @param topMargin Added space from the top of this widget to the previous widget
+ * @param bottomMargin Added space from the bottom of this widget to the next widget
+ * @param topGoneMargin Added space from the top of this widget when the previous widget has [Visibility.Gone]
+ * @param bottomGoneMargin Added space from the bottom of this widget when the next widget has [Visibility.Gone]
+ * @param weight Defines the proportion of space (relative to the total weight) occupied by this
+ * layout when the height is not a fixed dimension.
+ * @return The same [LayoutReference] instance with the applied values
+ */
+ fun LayoutReference.withVerticalChainParams(
+ topMargin: Dp = 0.dp,
+ bottomMargin: Dp = 0.dp,
+ topGoneMargin: Dp = 0.dp,
+ bottomGoneMargin: Dp = 0.dp,
+ weight: Float = Float.NaN
+ ): LayoutReference =
+ withChainParams(
+ startMargin = 0.dp,
+ topMargin = topMargin,
+ endMargin = 0.dp,
+ bottomMargin = bottomMargin,
+ startGoneMargin = 0.dp,
+ topGoneMargin = topGoneMargin,
+ endGoneMargin = 0.dp,
+ bottomGoneMargin = bottomGoneMargin,
+ weight = weight
+ )
}
/**
@@ -653,6 +812,11 @@
*/
@Stable
abstract class LayoutReference internal constructor(internal open val id: Any) {
+ /**
+ * This map should be used to store one instance of different implementations of [HelperParams].
+ */
+ private val helperParamsMap: MutableMap<String, HelperParams> = mutableMapOf()
+
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@@ -667,6 +831,60 @@
override fun hashCode(): Int {
return id.hashCode()
}
+
+ internal fun setHelperParams(helperParams: HelperParams) {
+ // Use the class name to force one instance per implementation
+ helperParamsMap[helperParams.javaClass.simpleName] = helperParams
+ }
+
+ /**
+ * Returns the [HelperParams] that corresponds to the class type [T]. Null if no instance of
+ * type [T] has been set.
+ */
+ internal inline fun <reified T> getHelperParams(): T? where T : HelperParams {
+ return helperParamsMap[T::class.java.simpleName] as? T
+ }
+}
+
+/**
+ * Helpers that need parameters on a per-widget basis may implement this interface to store custom
+ * parameters within [LayoutReference].
+ *
+ * @see [LayoutReference.getHelperParams]
+ * @see [LayoutReference.setHelperParams]
+ */
+internal interface HelperParams
+
+/**
+ * Parameters that may be defined for each widget within a chain.
+ *
+ * These will always be used instead of similarly named parameters defined with other calls such as
+ * [ConstrainScope.linkTo].
+ */
+internal class ChainParams(
+ val startMargin: Dp,
+ val topMargin: Dp,
+ val endMargin: Dp,
+ val bottomMargin: Dp,
+ val startGoneMargin: Dp,
+ val topGoneMargin: Dp,
+ val endGoneMargin: Dp,
+ val bottomGoneMargin: Dp,
+ val weight: Float,
+) : HelperParams {
+ companion object {
+ internal val Default = ChainParams(
+ startMargin = 0.dp,
+ topMargin = 0.dp,
+ endMargin = 0.dp,
+ bottomMargin = 0.dp,
+ startGoneMargin = 0.dp,
+ topGoneMargin = 0.dp,
+ endGoneMargin = 0.dp,
+ bottomGoneMargin = 0.dp,
+ weight = Float.NaN
+ )
+ }
}
/**
@@ -859,7 +1077,7 @@
@Immutable
class Wrap internal constructor(
internal val mode: Int
- ) {
+) {
companion object {
val None =
Wrap(androidx.constraintlayout.core.widgets.Flow.WRAP_NONE)
@@ -876,7 +1094,7 @@
@Immutable
class VerticalAlign internal constructor(
internal val mode: Int
- ) {
+) {
companion object {
val Top = VerticalAlign(androidx.constraintlayout.core.widgets.Flow.VERTICAL_ALIGN_TOP)
val Bottom =
@@ -894,7 +1112,7 @@
@Immutable
class HorizontalAlign internal constructor(
internal val mode: Int
- ) {
+) {
companion object {
val Start =
HorizontalAlign(androidx.constraintlayout.core.widgets.Flow.HORIZONTAL_ALIGN_START)
@@ -910,7 +1128,7 @@
@Immutable
class FlowStyle internal constructor(
internal val style: Int
- ) {
+) {
companion object {
val Spread = FlowStyle(0)
val SpreadInside = FlowStyle(1)
diff --git a/constraintlayout/constraintlayout-core/api/current.txt b/constraintlayout/constraintlayout-core/api/current.txt
index ff1bb30..30a9d6c 100644
--- a/constraintlayout/constraintlayout-core/api/current.txt
+++ b/constraintlayout/constraintlayout-core/api/current.txt
@@ -2477,20 +2477,22 @@
}
public class ChainReference extends androidx.constraintlayout.core.state.HelperReference {
- ctor public ChainReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
- method public void addChainElement(String!, float, float, float);
- method public androidx.constraintlayout.core.state.helpers.ChainReference! bias(float);
+ ctor public ChainReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
+ method public void addChainElement(String, float, float, float);
+ method public androidx.constraintlayout.core.state.helpers.ChainReference bias(float);
method public float getBias();
- method protected float getPostMargin(String!);
- method protected float getPreMargin(String!);
- method public androidx.constraintlayout.core.state.State.Chain! getStyle();
- method protected float getWeight(String!);
- method public androidx.constraintlayout.core.state.helpers.ChainReference! style(androidx.constraintlayout.core.state.State.Chain!);
+ method protected float getPostGoneMargin(String);
+ method protected float getPostMargin(String);
+ method protected float getPreGoneMargin(String);
+ method protected float getPreMargin(String);
+ method public androidx.constraintlayout.core.state.State.Chain getStyle();
+ method protected float getWeight(String);
+ method public androidx.constraintlayout.core.state.helpers.ChainReference style(androidx.constraintlayout.core.state.State.Chain);
field protected float mBias;
- field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPostMargin;
- field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPreMargin;
- field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapWeights;
- field protected androidx.constraintlayout.core.state.State.Chain! mStyle;
+ field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPostMargin;
+ field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPreMargin;
+ field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapWeights;
+ field protected androidx.constraintlayout.core.state.State.Chain mStyle;
}
public interface Facade {
diff --git a/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt b/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt
index ff1bb30..30a9d6c 100644
--- a/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt
+++ b/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt
@@ -2477,20 +2477,22 @@
}
public class ChainReference extends androidx.constraintlayout.core.state.HelperReference {
- ctor public ChainReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
- method public void addChainElement(String!, float, float, float);
- method public androidx.constraintlayout.core.state.helpers.ChainReference! bias(float);
+ ctor public ChainReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
+ method public void addChainElement(String, float, float, float);
+ method public androidx.constraintlayout.core.state.helpers.ChainReference bias(float);
method public float getBias();
- method protected float getPostMargin(String!);
- method protected float getPreMargin(String!);
- method public androidx.constraintlayout.core.state.State.Chain! getStyle();
- method protected float getWeight(String!);
- method public androidx.constraintlayout.core.state.helpers.ChainReference! style(androidx.constraintlayout.core.state.State.Chain!);
+ method protected float getPostGoneMargin(String);
+ method protected float getPostMargin(String);
+ method protected float getPreGoneMargin(String);
+ method protected float getPreMargin(String);
+ method public androidx.constraintlayout.core.state.State.Chain getStyle();
+ method protected float getWeight(String);
+ method public androidx.constraintlayout.core.state.helpers.ChainReference style(androidx.constraintlayout.core.state.State.Chain);
field protected float mBias;
- field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPostMargin;
- field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPreMargin;
- field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapWeights;
- field protected androidx.constraintlayout.core.state.State.Chain! mStyle;
+ field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPostMargin;
+ field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPreMargin;
+ field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapWeights;
+ field protected androidx.constraintlayout.core.state.State.Chain mStyle;
}
public interface Facade {
diff --git a/constraintlayout/constraintlayout-core/api/restricted_current.txt b/constraintlayout/constraintlayout-core/api/restricted_current.txt
index ff1bb30..161a4ec 100644
--- a/constraintlayout/constraintlayout-core/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-core/api/restricted_current.txt
@@ -2477,20 +2477,23 @@
}
public class ChainReference extends androidx.constraintlayout.core.state.HelperReference {
- ctor public ChainReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
- method public void addChainElement(String!, float, float, float);
- method public androidx.constraintlayout.core.state.helpers.ChainReference! bias(float);
+ ctor public ChainReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
+ method public void addChainElement(String, float, float, float);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void addChainElement(Object, float, float, float, float, float);
+ method public androidx.constraintlayout.core.state.helpers.ChainReference bias(float);
method public float getBias();
- method protected float getPostMargin(String!);
- method protected float getPreMargin(String!);
- method public androidx.constraintlayout.core.state.State.Chain! getStyle();
- method protected float getWeight(String!);
- method public androidx.constraintlayout.core.state.helpers.ChainReference! style(androidx.constraintlayout.core.state.State.Chain!);
+ method protected float getPostGoneMargin(String);
+ method protected float getPostMargin(String);
+ method protected float getPreGoneMargin(String);
+ method protected float getPreMargin(String);
+ method public androidx.constraintlayout.core.state.State.Chain getStyle();
+ method protected float getWeight(String);
+ method public androidx.constraintlayout.core.state.helpers.ChainReference style(androidx.constraintlayout.core.state.State.Chain);
field protected float mBias;
- field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPostMargin;
- field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPreMargin;
- field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapWeights;
- field protected androidx.constraintlayout.core.state.State.Chain! mStyle;
+ field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPostMargin;
+ field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPreMargin;
+ field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapWeights;
+ field protected androidx.constraintlayout.core.state.State.Chain mStyle;
}
public interface Facade {
diff --git a/constraintlayout/constraintlayout-core/build.gradle b/constraintlayout/constraintlayout-core/build.gradle
index 40b4820..329d954 100644
--- a/constraintlayout/constraintlayout-core/build.gradle
+++ b/constraintlayout/constraintlayout-core/build.gradle
@@ -23,6 +23,7 @@
}
dependencies {
+ api("androidx.annotation:annotation:1.5.0")
testImplementation(libs.junit)
}
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
index edf122b..a6af993 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
@@ -811,6 +811,7 @@
postMargin = toPix(state, array.getFloat(3));
break;
}
+ // TODO: Define how to set gone margin in JSON syntax
chain.addChainElement(id, weight, preMargin, postMargin);
}
} else {
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java
index 4229892..5a22613 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java
@@ -18,85 +18,181 @@
import static androidx.constraintlayout.core.widgets.ConstraintWidget.UNKNOWN;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
import androidx.constraintlayout.core.state.HelperReference;
import androidx.constraintlayout.core.state.State;
import java.util.HashMap;
+/**
+ * {@link HelperReference} for Chains.
+ *
+ * Elements should be added with {@link ChainReference#addChainElement}
+ */
public class ChainReference extends HelperReference {
protected float mBias = 0.5f;
- protected HashMap<String ,Float> mMapWeights;
- protected HashMap<String,Float> mMapPreMargin;
- protected HashMap<String,Float> mMapPostMargin;
- protected State.Chain mStyle = State.Chain.SPREAD;
+ /**
+ * @deprecated Unintended visibility, use {@link #getWeight(String)} instead
+ */
+ @Deprecated // TODO(b/253515185): Change to private visibility once we change major version
+ protected @NonNull HashMap<String, Float> mMapWeights = new HashMap<>();
- public ChainReference(State state, State.Helper type) {
+ /**
+ * @deprecated Unintended visibility, use {@link #getPreMargin(String)} instead
+ */
+ @Deprecated // TODO(b/253515185): Change to private visibility once we change major version
+ protected @NonNull HashMap<String, Float> mMapPreMargin = new HashMap<>();
+
+ /**
+ * @deprecated Unintended visibility, use {@link #getPostMargin(String)} instead
+ */
+ @Deprecated // TODO(b/253515185): Change to private visibility once we change major version
+ protected @NonNull HashMap<String, Float> mMapPostMargin = new HashMap<>();
+
+ private HashMap<String, Float> mMapPreGoneMargin;
+ private HashMap<String, Float> mMapPostGoneMargin;
+
+ protected @NonNull State.Chain mStyle = State.Chain.SPREAD;
+
+ public ChainReference(@NonNull State state, @NonNull State.Helper type) {
super(state, type);
}
- public State.Chain getStyle() {
+ public @NonNull State.Chain getStyle() {
return State.Chain.SPREAD;
}
- // @TODO: add description
- public ChainReference style(State.Chain style) {
+ /**
+ * Sets the {@link State.Chain style}.
+ *
+ * @param style Defines the way the chain will lay out its elements
+ * @return This same instance
+ */
+ @NonNull
+ public ChainReference style(@NonNull State.Chain style) {
mStyle = style;
return this;
}
- public void addChainElement(String id, float weight, float preMargin, float postMargin ) {
- super.add(id);
+ /**
+ * Adds the element by the given id to the Chain.
+ *
+ * The order in which the elements are added is important. It will represent the element's
+ * position in the Chain.
+ *
+ * @param id Id of the element to add
+ * @param weight Weight used to distribute remaining space to each element
+ * @param preMargin Additional space in pixels between the added element and the previous one
+ * (if any)
+ * @param postMargin Additional space in pixels between the added element and the next one (if
+ * any)
+ */
+ public void addChainElement(@NonNull String id,
+ float weight,
+ float preMargin,
+ float postMargin) {
+ addChainElement(id, weight, preMargin, postMargin, 0, 0);
+ }
+
+ /**
+ * Adds the element by the given id to the Chain.
+ *
+ * The object's {@link Object#toString()} result will be used to map the given margins and
+ * weight to it, so it must stable and comparable.
+ *
+ * The order in which the elements are added is important. It will represent the element's
+ * position in the Chain.
+ *
+ * @param id Id of the element to add
+ * @param weight Weight used to distribute remaining space to each element
+ * @param preMargin Additional space in pixels between the added element and the
+ * previous one
+ * (if any)
+ * @param postMargin Additional space in pixels between the added element and the next
+ * one (if
+ * any)
+ * @param preGoneMargin Additional space in pixels between the added element and the previous
+ * one (if any) when the previous element has Gone visibility
+ * @param postGoneMargin Additional space in pixels between the added element and the next
+ * one (if any) when the next element has Gone visibility
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public void addChainElement(@NonNull Object id,
+ float weight,
+ float preMargin,
+ float postMargin,
+ float preGoneMargin,
+ float postGoneMargin) {
+ super.add(id); // Add element id as is, it's expected to return the same given instance
+ String idString = id.toString();
if (!Float.isNaN(weight)) {
- if (mMapWeights == null) {
- mMapWeights = new HashMap<>();
- }
- mMapWeights.put(id, weight);
+ mMapWeights.put(idString, weight);
}
if (!Float.isNaN(preMargin)) {
- if (mMapPreMargin == null) {
- mMapPreMargin = new HashMap<>();
- }
- mMapPreMargin.put(id, preMargin);
+ mMapPreMargin.put(idString, preMargin);
}
if (!Float.isNaN(postMargin)) {
- if (mMapPostMargin == null) {
- mMapPostMargin = new HashMap<>();
+ mMapPostMargin.put(idString, postMargin);
+ }
+ if (!Float.isNaN(preGoneMargin)) {
+ if (mMapPreGoneMargin == null) {
+ mMapPreGoneMargin = new HashMap<>();
}
- mMapPostMargin.put(id, postMargin);
+ mMapPreGoneMargin.put(idString, preGoneMargin);
+ }
+ if (!Float.isNaN(postGoneMargin)) {
+ if (mMapPostGoneMargin == null) {
+ mMapPostGoneMargin = new HashMap<>();
+ }
+ mMapPostGoneMargin.put(idString, postGoneMargin);
}
}
- protected float getWeight(String id) {
- if (mMapWeights == null) {
- return UNKNOWN;
- }
- if (mMapWeights.containsKey(id)) {
- return mMapWeights.get(id);
- }
- return UNKNOWN;
+ protected float getWeight(@NonNull String id) {
+ if (mMapWeights.containsKey(id)) {
+ return mMapWeights.get(id);
+ }
+ return UNKNOWN;
}
- protected float getPostMargin(String id) {
- if (mMapPostMargin != null && mMapPostMargin.containsKey(id)) {
+ protected float getPostMargin(@NonNull String id) {
+ if (mMapPostMargin.containsKey(id)) {
return mMapPostMargin.get(id);
}
return 0;
}
- protected float getPreMargin(String id) {
- if (mMapPreMargin != null && mMapPreMargin.containsKey(id)) {
+ protected float getPreMargin(@NonNull String id) {
+ if (mMapPreMargin.containsKey(id)) {
return mMapPreMargin.get(id);
}
return 0;
}
+ protected float getPostGoneMargin(@NonNull String id) {
+ if (mMapPostGoneMargin != null && mMapPostGoneMargin.containsKey(id)) {
+ return mMapPostGoneMargin.get(id);
+ }
+ return 0;
+ }
+
+ protected float getPreGoneMargin(@NonNull String id) {
+ if (mMapPreGoneMargin != null && mMapPreGoneMargin.containsKey(id)) {
+ return mMapPreGoneMargin.get(id);
+ }
+ return 0;
+ }
+
public float getBias() {
return mBias;
}
// @TODO: add description
+ @NonNull
@Override
public ChainReference bias(float bias) {
mBias = bias;
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/HorizontalChainReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/HorizontalChainReference.java
index be09236..32539da 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/HorizontalChainReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/HorizontalChainReference.java
@@ -58,14 +58,17 @@
} else {
// No constraint declared, default to Parent.
String refKey = reference.getKey().toString();
- first.startToStart(State.PARENT).margin(getPreMargin(refKey));
+ first.startToStart(State.PARENT).margin(getPreMargin(refKey)).marginGone(
+ getPreGoneMargin(refKey));
}
}
if (previous != null) {
String preKey = previous.getKey().toString();
String refKey = reference.getKey().toString();
- previous.endToStart(reference.getKey()).margin(getPostMargin(preKey));
- reference.startToEnd(previous.getKey()).margin(getPreMargin(refKey));
+ previous.endToStart(reference.getKey()).margin(getPostMargin(preKey)).marginGone(
+ getPostGoneMargin(preKey));
+ reference.startToEnd(previous.getKey()).margin(getPreMargin(refKey)).marginGone(
+ getPreGoneMargin(refKey));
}
float weight = getWeight(key.toString());
if (weight != UNKNOWN) {
@@ -88,7 +91,8 @@
} else {
// No constraint declared, default to Parent.
String preKey = previous.getKey().toString();
- previous.endToEnd(State.PARENT).margin(getPostMargin(preKey));
+ previous.endToEnd(State.PARENT).margin(getPostMargin(preKey)).marginGone(
+ getPostGoneMargin(preKey));
}
}
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/VerticalChainReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/VerticalChainReference.java
index d4fdd23..8d880b1 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/VerticalChainReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/VerticalChainReference.java
@@ -49,14 +49,17 @@
} else {
// No constraint declared, default to Parent.
String refKey = reference.getKey().toString();
- first.topToTop(State.PARENT).margin(getPreMargin(refKey));
+ first.topToTop(State.PARENT).margin(getPreMargin(refKey)).marginGone(
+ getPreGoneMargin(refKey));
}
}
if (previous != null) {
String preKey = previous.getKey().toString();
String refKey = reference.getKey().toString();
- previous.bottomToTop(reference.getKey()).margin(getPostMargin(preKey));
- reference.topToBottom(previous.getKey()).margin(getPreMargin(refKey));
+ previous.bottomToTop(reference.getKey()).margin(getPostMargin(preKey)).marginGone(
+ getPostGoneMargin(preKey));
+ reference.topToBottom(previous.getKey()).margin(getPreMargin(refKey)).marginGone(
+ getPreGoneMargin(refKey));
}
float weight = getWeight(key.toString());
if (weight != UNKNOWN) {
@@ -77,7 +80,8 @@
} else {
// No constraint declared, default to Parent.
String preKey = previous.getKey().toString();
- previous.bottomToBottom(State.PARENT).margin(getPostMargin(preKey));
+ previous.bottomToBottom(State.PARENT).margin(getPostMargin(preKey)).marginGone(
+ getPostGoneMargin(preKey));
}
}
diff --git a/credentials/credentials/api/current.txt b/credentials/credentials/api/current.txt
index 3544ddf9..b14a46d 100644
--- a/credentials/credentials/api/current.txt
+++ b/credentials/credentials/api/current.txt
@@ -1,12 +1,22 @@
// Signature format: 4.0
package androidx.credentials {
- public abstract class CreateCredentialRequest {
- ctor public CreateCredentialRequest();
+ public class CreateCredentialRequest {
+ ctor public CreateCredentialRequest(String type, android.os.Bundle data, boolean requireSystemProvider);
+ method public final android.os.Bundle getData();
+ method public final boolean getRequireSystemProvider();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final boolean requireSystemProvider;
+ property public final String type;
}
- public abstract class CreateCredentialResponse {
- ctor public CreateCredentialResponse();
+ public class CreateCredentialResponse {
+ ctor public CreateCredentialResponse(String type, android.os.Bundle data);
+ method public final android.os.Bundle getData();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final String type;
}
public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
@@ -21,8 +31,12 @@
ctor public CreatePasswordResponse();
}
- public abstract class Credential {
- ctor public Credential();
+ public class Credential {
+ ctor public Credential(String type, android.os.Bundle data);
+ method public final android.os.Bundle getData();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final String type;
}
public final class CredentialManager {
@@ -51,8 +65,14 @@
property public final CharSequence? errorMessage;
}
- public abstract class GetCredentialOption {
- ctor public GetCredentialOption();
+ public class GetCredentialOption {
+ ctor public GetCredentialOption(String type, android.os.Bundle data, boolean requireSystemProvider);
+ method public final android.os.Bundle getData();
+ method public final boolean getRequireSystemProvider();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final boolean requireSystemProvider;
+ property public final String type;
}
public final class GetCredentialRequest {
diff --git a/credentials/credentials/api/public_plus_experimental_current.txt b/credentials/credentials/api/public_plus_experimental_current.txt
index 3544ddf9..b14a46d 100644
--- a/credentials/credentials/api/public_plus_experimental_current.txt
+++ b/credentials/credentials/api/public_plus_experimental_current.txt
@@ -1,12 +1,22 @@
// Signature format: 4.0
package androidx.credentials {
- public abstract class CreateCredentialRequest {
- ctor public CreateCredentialRequest();
+ public class CreateCredentialRequest {
+ ctor public CreateCredentialRequest(String type, android.os.Bundle data, boolean requireSystemProvider);
+ method public final android.os.Bundle getData();
+ method public final boolean getRequireSystemProvider();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final boolean requireSystemProvider;
+ property public final String type;
}
- public abstract class CreateCredentialResponse {
- ctor public CreateCredentialResponse();
+ public class CreateCredentialResponse {
+ ctor public CreateCredentialResponse(String type, android.os.Bundle data);
+ method public final android.os.Bundle getData();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final String type;
}
public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
@@ -21,8 +31,12 @@
ctor public CreatePasswordResponse();
}
- public abstract class Credential {
- ctor public Credential();
+ public class Credential {
+ ctor public Credential(String type, android.os.Bundle data);
+ method public final android.os.Bundle getData();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final String type;
}
public final class CredentialManager {
@@ -51,8 +65,14 @@
property public final CharSequence? errorMessage;
}
- public abstract class GetCredentialOption {
- ctor public GetCredentialOption();
+ public class GetCredentialOption {
+ ctor public GetCredentialOption(String type, android.os.Bundle data, boolean requireSystemProvider);
+ method public final android.os.Bundle getData();
+ method public final boolean getRequireSystemProvider();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final boolean requireSystemProvider;
+ property public final String type;
}
public final class GetCredentialRequest {
diff --git a/credentials/credentials/api/restricted_current.txt b/credentials/credentials/api/restricted_current.txt
index 3544ddf9..b14a46d 100644
--- a/credentials/credentials/api/restricted_current.txt
+++ b/credentials/credentials/api/restricted_current.txt
@@ -1,12 +1,22 @@
// Signature format: 4.0
package androidx.credentials {
- public abstract class CreateCredentialRequest {
- ctor public CreateCredentialRequest();
+ public class CreateCredentialRequest {
+ ctor public CreateCredentialRequest(String type, android.os.Bundle data, boolean requireSystemProvider);
+ method public final android.os.Bundle getData();
+ method public final boolean getRequireSystemProvider();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final boolean requireSystemProvider;
+ property public final String type;
}
- public abstract class CreateCredentialResponse {
- ctor public CreateCredentialResponse();
+ public class CreateCredentialResponse {
+ ctor public CreateCredentialResponse(String type, android.os.Bundle data);
+ method public final android.os.Bundle getData();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final String type;
}
public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
@@ -21,8 +31,12 @@
ctor public CreatePasswordResponse();
}
- public abstract class Credential {
- ctor public Credential();
+ public class Credential {
+ ctor public Credential(String type, android.os.Bundle data);
+ method public final android.os.Bundle getData();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final String type;
}
public final class CredentialManager {
@@ -51,8 +65,14 @@
property public final CharSequence? errorMessage;
}
- public abstract class GetCredentialOption {
- ctor public GetCredentialOption();
+ public class GetCredentialOption {
+ ctor public GetCredentialOption(String type, android.os.Bundle data, boolean requireSystemProvider);
+ method public final android.os.Bundle getData();
+ method public final boolean getRequireSystemProvider();
+ method public final String getType();
+ property public final android.os.Bundle data;
+ property public final boolean requireSystemProvider;
+ property public final String type;
}
public final class GetCredentialRequest {
diff --git a/credentials/credentials/build.gradle b/credentials/credentials/build.gradle
index 29d733e..6bb5a52 100644
--- a/credentials/credentials/build.gradle
+++ b/credentials/credentials/build.gradle
@@ -24,6 +24,7 @@
}
dependencies {
+ api("androidx.annotation:annotation:1.5.0")
api(libs.kotlinStdlib)
implementation(libs.kotlinCoroutinesCore)
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
new file mode 100644
index 0000000..953bd5a
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CreatePasswordRequestJavaTest {
+ @Test
+ public void constructor_nullId_throws() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new CreatePasswordRequest(null, "pwd")
+ );
+ }
+
+ @Test
+ public void constructor_nullPassword_throws() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new CreatePasswordRequest("id", null)
+ );
+ }
+
+ @Test
+ public void constructor_emptyPassword_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new CreatePasswordRequest("id", "")
+ );
+ }
+
+ @Test
+ public void getter_id() {
+ String idExpected = "id";
+ CreatePasswordRequest request = new CreatePasswordRequest(idExpected, "password");
+ assertThat(request.getId()).isEqualTo(idExpected);
+ }
+
+ @Test
+ public void getter_password() {
+ String passwordExpected = "pwd";
+ CreatePasswordRequest request = new CreatePasswordRequest("id", passwordExpected);
+ assertThat(request.getPassword()).isEqualTo(passwordExpected);
+ }
+
+ @Test
+ public void getter_frameworkProperties() {
+ String idExpected = "id";
+ String passwordExpected = "pwd";
+ Bundle expectedData = new Bundle();
+ expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_ID, idExpected);
+ expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_PASSWORD, passwordExpected);
+
+ CreatePasswordRequest request = new CreatePasswordRequest(idExpected, passwordExpected);
+
+ assertThat(request.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(request.getData(), expectedData)).isTrue();
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
new file mode 100644
index 0000000..9943eed
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CreatePasswordRequestTest {
+ @Test
+ fun constructor_emptyPassword_throws() {
+ assertThrows<IllegalArgumentException> {
+ CreatePasswordRequest("id", "")
+ }
+ }
+
+ @Test
+ fun getter_id() {
+ val idExpected = "id"
+ val request = CreatePasswordRequest(idExpected, "password")
+ assertThat(request.id).isEqualTo(idExpected)
+ }
+
+ @Test
+ fun getter_password() {
+ val passwordExpected = "pwd"
+ val request = CreatePasswordRequest("id", passwordExpected)
+ assertThat(request.password).isEqualTo(passwordExpected)
+ }
+
+ @Test
+ fun getter_frameworkProperties() {
+ val idExpected = "id"
+ val passwordExpected = "pwd"
+ val expectedData = Bundle()
+ expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_ID, idExpected)
+ expectedData.putString(CreatePasswordRequest.BUNDLE_KEY_PASSWORD, passwordExpected)
+
+ val request = CreatePasswordRequest(idExpected, passwordExpected)
+
+ assertThat(request.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+ assertThat(equals(request.data, expectedData)).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseJavaTest.java
new file mode 100644
index 0000000..5f75653
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseJavaTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CreatePasswordResponseJavaTest {
+ @Test
+ public void getter_frameworkProperties() {
+ CreatePasswordResponse response = new CreatePasswordResponse();
+
+ assertThat(response.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(response.getData(), Bundle.EMPTY)).isTrue();
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseTest.kt
new file mode 100644
index 0000000..84e4102
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordResponseTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CreatePasswordResponseTest {
+ @Test
+ fun getter_frameworkProperties() {
+ val response = CreatePasswordResponse()
+ Truth.assertThat(response.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+ Truth.assertThat(equals(response.data, Bundle.EMPTY)).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
index db1a2a9..2604d86 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
@@ -16,10 +16,15 @@
package androidx.credentials;
+import static androidx.credentials.CreatePublicKeyCredentialBaseRequest.BUNDLE_KEY_REQUEST_JSON;
+import static androidx.credentials.CreatePublicKeyCredentialRequest.BUNDLE_KEY_ALLOW_HYBRID;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import android.os.Bundle;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -80,4 +85,22 @@
String testJsonActual = createPublicKeyCredentialReq.getRequestJson();
assertThat(testJsonActual).isEqualTo(testJsonExpected);
}
+
+ @Test
+ public void getter_frameworkProperties_success() {
+ String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+ boolean allowHybridExpected = false;
+ Bundle expectedData = new Bundle();
+ expectedData.putString(
+ BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
+ expectedData.putBoolean(
+ BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected);
+
+ CreatePublicKeyCredentialRequest request =
+ new CreatePublicKeyCredentialRequest(requestJsonExpected, allowHybridExpected);
+
+ assertThat(request.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(request.getData(), expectedData)).isTrue();
+ assertThat(request.getRequireSystemProvider()).isFalse();
+ }
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java
index 82adc07..8558cbb 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java
@@ -16,8 +16,15 @@
package androidx.credentials;
+import static androidx.credentials.CreatePublicKeyCredentialBaseRequest.BUNDLE_KEY_REQUEST_JSON;
+import static androidx.credentials.CreatePublicKeyCredentialRequest.BUNDLE_KEY_ALLOW_HYBRID;
+import static androidx.credentials.CreatePublicKeyCredentialRequestPrivileged.BUNDLE_KEY_CLIENT_DATA_HASH;
+import static androidx.credentials.CreatePublicKeyCredentialRequestPrivileged.BUNDLE_KEY_RP;
+
import static com.google.common.truth.Truth.assertThat;
+import android.os.Bundle;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -107,4 +114,26 @@
createPublicKeyCredentialRequestPrivileged.getClientDataHash();
assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected);
}
+
+ @Test
+ public void getter_frameworkProperties_success() {
+ String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+ String rpExpected = "RP";
+ String clientDataHashExpected = "X342%4dfd7&";
+ boolean allowHybridExpected = false;
+ Bundle expectedData = new Bundle();
+ expectedData.putString(BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
+ expectedData.putString(BUNDLE_KEY_RP, rpExpected);
+ expectedData.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHashExpected);
+ expectedData.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected);
+
+ CreatePublicKeyCredentialRequestPrivileged request =
+ new CreatePublicKeyCredentialRequestPrivileged(
+ requestJsonExpected, rpExpected, clientDataHashExpected,
+ allowHybridExpected);
+
+ assertThat(request.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(request.getData(), expectedData)).isTrue();
+ assertThat(request.getRequireSystemProvider()).isFalse();
+ }
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt
index faffbcb..e485003 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt
@@ -16,6 +16,7 @@
package androidx.credentials
+import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -110,4 +111,37 @@
val clientDataHashActual = createPublicKeyCredentialRequestPrivileged.clientDataHash
assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected)
}
+
+ @Test
+ fun getter_frameworkProperties_success() {
+ val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+ val rpExpected = "RP"
+ val clientDataHashExpected = "X342%4dfd7&"
+ val allowHybridExpected = false
+ val expectedData = Bundle()
+ expectedData.putString(
+ CreatePublicKeyCredentialBaseRequest.BUNDLE_KEY_REQUEST_JSON,
+ requestJsonExpected
+ )
+ expectedData.putString(CreatePublicKeyCredentialRequestPrivileged.BUNDLE_KEY_RP, rpExpected)
+ expectedData.putString(
+ CreatePublicKeyCredentialRequestPrivileged.BUNDLE_KEY_CLIENT_DATA_HASH,
+ clientDataHashExpected
+ )
+ expectedData.putBoolean(
+ CreatePublicKeyCredentialRequest.BUNDLE_KEY_ALLOW_HYBRID,
+ allowHybridExpected
+ )
+
+ val request = CreatePublicKeyCredentialRequestPrivileged(
+ requestJsonExpected,
+ rpExpected,
+ clientDataHashExpected,
+ allowHybridExpected
+ )
+
+ assertThat(request.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ assertThat(equals(request.data, expectedData)).isTrue()
+ assertThat(request.requireSystemProvider).isFalse()
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
index 4058526..32d4365 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.credentials.CreatePublicKeyCredentialBaseRequest.Companion.BUNDLE_KEY_REQUEST_JSON
+import androidx.credentials.CreatePublicKeyCredentialRequest.Companion.BUNDLE_KEY_ALLOW_HYBRID
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -69,4 +72,26 @@
val testJsonActual = createPublicKeyCredentialReq.requestJson
assertThat(testJsonActual).isEqualTo(testJsonExpected)
}
+
+ @Test
+ fun getter_frameworkProperties_success() {
+ val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+ val allowHybridExpected = false
+ val expectedData = Bundle()
+ expectedData.putString(
+ BUNDLE_KEY_REQUEST_JSON, requestJsonExpected
+ )
+ expectedData.putBoolean(
+ BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected
+ )
+
+ val request = CreatePublicKeyCredentialRequest(
+ requestJsonExpected,
+ allowHybridExpected
+ )
+
+ assertThat(request.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ assertThat(equals(request.data, expectedData)).isTrue()
+ assertThat(request.requireSystemProvider).isFalse()
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java
index dc7bdb26..099f367 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java
@@ -20,6 +20,8 @@
import static org.junit.Assert.assertThrows;
+import android.os.Bundle;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -59,4 +61,19 @@
String testJsonActual = createPublicKeyCredentialResponse.getRegistrationResponseJson();
assertThat(testJsonActual).isEqualTo(testJsonExpected);
}
+
+ @Test
+ public void getter_frameworkProperties_success() {
+ String registrationResponseJsonExpected = "{\"input\":5}";
+ Bundle expectedData = new Bundle();
+ expectedData.putString(
+ CreatePublicKeyCredentialResponse.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON,
+ registrationResponseJsonExpected);
+
+ CreatePublicKeyCredentialResponse response =
+ new CreatePublicKeyCredentialResponse(registrationResponseJsonExpected);
+
+ assertThat(response.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(response.getData(), expectedData)).isTrue();
+ }
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt
index 9311d59..e2179b9 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt
@@ -16,6 +16,7 @@
package androidx.credentials
+import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -47,4 +48,19 @@
val testJsonActual = createPublicKeyCredentialResponse.registrationResponseJson
assertThat(testJsonActual).isEqualTo(testJsonExpected)
}
+
+ @Test
+ fun getter_frameworkProperties_success() {
+ val registrationResponseJsonExpected = "{\"input\":5}"
+ val expectedData = Bundle()
+ expectedData.putString(
+ CreatePublicKeyCredentialResponse.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON,
+ registrationResponseJsonExpected
+ )
+
+ val response = CreatePublicKeyCredentialResponse(registrationResponseJsonExpected)
+
+ assertThat(response.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ assertThat(equals(response.data, expectedData)).isTrue()
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java
new file mode 100644
index 0000000..d8050da
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionJavaTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class GetPasswordOptionJavaTest {
+ @Test
+ public void getter_frameworkProperties() {
+ GetPasswordOption option = new GetPasswordOption();
+
+ assertThat(option.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(option.getData(), Bundle.EMPTY)).isTrue();
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt
new file mode 100644
index 0000000..2c155af
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPasswordOptionTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GetPasswordOptionTest {
+ @Test
+ fun getter_frameworkProperties() {
+ val option = GetPasswordOption()
+ Truth.assertThat(option.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+ Truth.assertThat(equals(option.data, Bundle.EMPTY)).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
index ec3083c..7575d2a 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
@@ -16,10 +16,15 @@
package androidx.credentials;
+import static androidx.credentials.GetPublicKeyCredentialBaseOption.BUNDLE_KEY_REQUEST_JSON;
+import static androidx.credentials.GetPublicKeyCredentialOption.BUNDLE_KEY_ALLOW_HYBRID;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import android.os.Bundle;
+
import org.junit.Test;
public class GetPublicKeyCredentialOptionJavaTest {
@@ -73,4 +78,20 @@
String testJsonActual = getPublicKeyCredentialOpt.getRequestJson();
assertThat(testJsonActual).isEqualTo(testJsonExpected);
}
+
+ @Test
+ public void getter_frameworkProperties_success() {
+ String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+ boolean allowHybridExpected = false;
+ Bundle expectedData = new Bundle();
+ expectedData.putString(BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
+ expectedData.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected);
+
+ GetPublicKeyCredentialOption option =
+ new GetPublicKeyCredentialOption(requestJsonExpected, allowHybridExpected);
+
+ assertThat(option.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(option.getData(), expectedData)).isTrue();
+ assertThat(option.getRequireSystemProvider()).isFalse();
+ }
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java
index 2b18b0d..54efd71 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java
@@ -16,8 +16,15 @@
package androidx.credentials;
+import static androidx.credentials.GetPublicKeyCredentialBaseOption.BUNDLE_KEY_REQUEST_JSON;
+import static androidx.credentials.GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_ALLOW_HYBRID;
+import static androidx.credentials.GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_CLIENT_DATA_HASH;
+import static androidx.credentials.GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_RP;
+
import static com.google.common.truth.Truth.assertThat;
+import android.os.Bundle;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -106,4 +113,26 @@
String clientDataHashActual = getPublicKeyCredentialOptionPrivileged.getClientDataHash();
assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected);
}
+
+ @Test
+ public void getter_frameworkProperties_success() {
+ String requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+ String rpExpected = "RP";
+ String clientDataHashExpected = "X342%4dfd7&";
+ boolean allowHybridExpected = false;
+ Bundle expectedData = new Bundle();
+ expectedData.putString(BUNDLE_KEY_REQUEST_JSON, requestJsonExpected);
+ expectedData.putString(BUNDLE_KEY_RP, rpExpected);
+ expectedData.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHashExpected);
+ expectedData.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybridExpected);
+
+ GetPublicKeyCredentialOptionPrivileged option =
+ new GetPublicKeyCredentialOptionPrivileged(
+ requestJsonExpected, rpExpected, clientDataHashExpected,
+ allowHybridExpected);
+
+ assertThat(option.getType()).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(option.getData(), expectedData)).isTrue();
+ assertThat(option.getRequireSystemProvider()).isFalse();
+ }
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt
index 024070d..e1c1079 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt
@@ -16,6 +16,7 @@
package androidx.credentials
+import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -107,4 +108,35 @@
val clientDataHashActual = getPublicKeyCredentialOptionPrivileged.clientDataHash
assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected)
}
+
+ @Test
+ fun getter_frameworkProperties_success() {
+ val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+ val rpExpected = "RP"
+ val clientDataHashExpected = "X342%4dfd7&"
+ val allowHybridExpected = false
+ val expectedData = Bundle()
+ expectedData.putString(
+ GetPublicKeyCredentialBaseOption.BUNDLE_KEY_REQUEST_JSON,
+ requestJsonExpected
+ )
+ expectedData.putString(GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_RP, rpExpected)
+ expectedData.putString(
+ GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_CLIENT_DATA_HASH,
+ clientDataHashExpected
+ )
+ expectedData.putBoolean(
+ GetPublicKeyCredentialOptionPrivileged.BUNDLE_KEY_ALLOW_HYBRID,
+ allowHybridExpected
+ )
+
+ val option = GetPublicKeyCredentialOptionPrivileged(
+ requestJsonExpected, rpExpected, clientDataHashExpected,
+ allowHybridExpected
+ )
+
+ assertThat(option.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ assertThat(equals(option.data, expectedData)).isTrue()
+ assertThat(option.requireSystemProvider).isFalse()
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
index 7cc8392..821b580 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
@@ -16,6 +16,7 @@
package androidx.credentials
+import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -68,4 +69,25 @@
val testJsonActual = createPublicKeyCredentialReq.requestJson
assertThat(testJsonActual).isEqualTo(testJsonExpected)
}
+
+ @Test
+ fun getter_frameworkProperties_success() {
+ val requestJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+ val allowHybridExpected = false
+ val expectedData = Bundle()
+ expectedData.putString(
+ GetPublicKeyCredentialBaseOption.BUNDLE_KEY_REQUEST_JSON,
+ requestJsonExpected
+ )
+ expectedData.putBoolean(
+ GetPublicKeyCredentialOption.BUNDLE_KEY_ALLOW_HYBRID,
+ allowHybridExpected
+ )
+
+ val option = GetPublicKeyCredentialOption(requestJsonExpected, allowHybridExpected)
+
+ assertThat(option.type).isEqualTo(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+ assertThat(equals(option.data, expectedData)).isTrue()
+ assertThat(option.requireSystemProvider).isFalse()
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java
new file mode 100644
index 0000000..c41d985
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialJavaTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PasswordCredentialJavaTest {
+ @Test
+ public void constructor_nullId_throws() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new PasswordCredential(null, "pwd")
+ );
+ }
+
+ @Test
+ public void constructor_nullPassword_throws() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new PasswordCredential("id", null)
+ );
+ }
+
+ @Test
+ public void constructor_emptyPassword_throws() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new PasswordCredential("id", "")
+ );
+ }
+
+ @Test
+ public void getter_id() {
+ String idExpected = "id";
+ PasswordCredential credential = new PasswordCredential(idExpected, "password");
+ assertThat(credential.getId()).isEqualTo(idExpected);
+ }
+
+ @Test
+ public void getter_password() {
+ String passwordExpected = "pwd";
+ PasswordCredential credential = new PasswordCredential("id", passwordExpected);
+ assertThat(credential.getPassword()).isEqualTo(passwordExpected);
+ }
+
+ @Test
+ public void getter_frameworkProperties() {
+ String idExpected = "id";
+ String passwordExpected = "pwd";
+ Bundle expectedData = new Bundle();
+ expectedData.putString(PasswordCredential.BUNDLE_KEY_ID, idExpected);
+ expectedData.putString(PasswordCredential.BUNDLE_KEY_PASSWORD, passwordExpected);
+
+ CreatePasswordRequest credential = new CreatePasswordRequest(idExpected, passwordExpected);
+
+ assertThat(credential.getType()).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(credential.getData(), expectedData)).isTrue();
+ }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt
new file mode 100644
index 0000000..c8f425c
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PasswordCredentialTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PasswordCredentialTest {
+ @Test
+ fun constructor_emptyPassword_throws() {
+ assertThrows<IllegalArgumentException> {
+ PasswordCredential("id", "")
+ }
+ }
+
+ @Test
+ fun getter_id() {
+ val idExpected = "id"
+ val credential = PasswordCredential(idExpected, "password")
+ assertThat(credential.id).isEqualTo(idExpected)
+ }
+
+ @Test
+ fun getter_password() {
+ val passwordExpected = "pwd"
+ val credential = PasswordCredential("id", passwordExpected)
+ assertThat(credential.password).isEqualTo(passwordExpected)
+ }
+
+ @Test
+ fun getter_frameworkProperties() {
+ val idExpected = "id"
+ val passwordExpected = "pwd"
+ val expectedData = Bundle()
+ expectedData.putString(PasswordCredential.BUNDLE_KEY_ID, idExpected)
+ expectedData.putString(PasswordCredential.BUNDLE_KEY_PASSWORD, passwordExpected)
+
+ val credential = PasswordCredential(idExpected, passwordExpected)
+
+ assertThat(credential.type).isEqualTo(PasswordCredential.TYPE_PASSWORD_CREDENTIAL)
+ assertThat(equals(credential.data, expectedData)).isTrue()
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
index 86847fa..04223aa 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
@@ -20,6 +20,8 @@
import static org.junit.Assert.assertThrows;
+import android.os.Bundle;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -64,6 +66,20 @@
}
@Test
+ public void getter_frameworkProperties() {
+ String jsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+ Bundle expectedData = new Bundle();
+ expectedData.putString(
+ PublicKeyCredential.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON, jsonExpected);
+
+ PublicKeyCredential publicKeyCredential = new PublicKeyCredential(jsonExpected);
+
+ assertThat(publicKeyCredential.getType()).isEqualTo(
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL);
+ assertThat(TestUtilsKt.equals(publicKeyCredential.getData(), expectedData)).isTrue();
+ }
+
+ @Test
public void staticProperty_hasCorrectTypeConstantValue() {
String typeExpected = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL";
String typeActual = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL;
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
index 47aecb4..0c67994 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
@@ -16,6 +16,7 @@
package androidx.credentials
+import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
@@ -54,6 +55,22 @@
}
@Test
+ fun getter_frameworkProperties() {
+ val jsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+ val expectedData = Bundle()
+ expectedData.putString(
+ PublicKeyCredential.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON, jsonExpected
+ )
+
+ val publicKeyCredential = PublicKeyCredential(jsonExpected)
+
+ assertThat(publicKeyCredential.type).isEqualTo(
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL
+ )
+ assertThat(equals(publicKeyCredential.data, expectedData)).isTrue()
+ }
+
+ @Test
fun staticProperty_hasCorrectTypeConstantValue() {
val typeExpected = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
val typeActual = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
new file mode 100644
index 0000000..4567380
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials
+
+import android.os.Bundle
+
+/** True if the two Bundles contain the same elements, and false otherwise. */
+@Suppress("DEPRECATION")
+fun equals(a: Bundle, b: Bundle): Boolean {
+ if (a.keySet().size != b.keySet().size) {
+ return false
+ }
+ for (key in a.keySet()) {
+ if (!b.keySet().contains(key)) {
+ return false
+ }
+
+ val valA = a.get(key)
+ val valB = b.get(key)
+ if (valA is Bundle && valB is Bundle && !equals(valA, valB)) {
+ return false
+ } else {
+ val isEqual = (valA?.equals(valB) ?: (valB == null))
+ if (!isEqual) {
+ return false
+ }
+ }
+ }
+ return true
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
index d630fa7..7c373fe 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
@@ -16,11 +16,22 @@
package androidx.credentials
+import android.os.Bundle
+
/**
* Base request class for registering a credential.
*
* An application can construct a subtype request and call [CredentialManager.executeCreateCredential] to
* launch framework UI flows to collect consent and any other metadata needed from the user to
* register a new user credential.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass
+ * @property data the request data in the [Bundle] format
+ * @property requireSystemProvider true if must only be fulfilled by a system provider and false
+ * otherwise
*/
-abstract class CreateCredentialRequest
\ No newline at end of file
+open class CreateCredentialRequest(
+ val type: String,
+ val data: Bundle,
+ val requireSystemProvider: Boolean,
+)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt
index 192d472..5267997 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialResponse.kt
@@ -16,5 +16,13 @@
package androidx.credentials
-/** Base response class for registering a credential. */
-abstract class CreateCredentialResponse
\ No newline at end of file
+import android.os.Bundle
+
+/**
+ * Base response class for the credential creation operation made with the
+ * [CreateCredentialRequest].
+ *
+ * @property type the credential type determined by the credential-type-specific subclass
+ * @property data the response data in the [Bundle] format
+ */
+open class CreateCredentialResponse(val type: String, val data: Bundle)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt
index 2fc0116..f03c633 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordRequest.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* A request to save the user password credential with their password provider.
*
@@ -28,9 +31,29 @@
class CreatePasswordRequest constructor(
val id: String,
val password: String,
-) : CreateCredentialRequest() {
+) : CreateCredentialRequest(
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+ toBundle(id, password),
+ false,
+) {
init {
require(password.isNotEmpty()) { "password should not be empty" }
}
+
+ /** @hide */
+ companion object {
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
+
+ @JvmStatic
+ internal fun toBundle(id: String, password: String): Bundle {
+ val bundle = Bundle()
+ bundle.putString(BUNDLE_KEY_ID, id)
+ bundle.putString(BUNDLE_KEY_PASSWORD, password)
+ return bundle
+ }
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordResponse.kt
index d70468c..436db69 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePasswordResponse.kt
@@ -16,5 +16,10 @@
package androidx.credentials
+import android.os.Bundle
+
/** A response of a password saving flow. */
-class CreatePasswordResponse : CreateCredentialResponse()
\ No newline at end of file
+class CreatePasswordResponse : CreateCredentialResponse(
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+ Bundle(),
+)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt
index e78b5ac..7fbe48a 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* Base request class for registering a public key credential.
*
@@ -30,10 +33,19 @@
* @hide
*/
abstract class CreatePublicKeyCredentialBaseRequest constructor(
- val requestJson: String
-) : CreateCredentialRequest() {
+ val requestJson: String,
+ type: String,
+ data: Bundle,
+ requireSystemProvider: Boolean,
+) : CreateCredentialRequest(type, data, requireSystemProvider) {
init {
require(requestJson.isNotEmpty()) { "request json must not be empty" }
}
+
+ /** @hide */
+ companion object {
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
index d0b2d24..fe79aa99 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* A request to register a passkey from the user's public key credential provider.
*
@@ -32,4 +35,23 @@
requestJson: String,
@get:JvmName("allowHybrid")
val allowHybrid: Boolean = true
-) : CreatePublicKeyCredentialBaseRequest(requestJson)
\ No newline at end of file
+) : CreatePublicKeyCredentialBaseRequest(
+ requestJson,
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+ toBundle(requestJson, allowHybrid),
+ false,
+) {
+ /** @hide */
+ companion object {
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+
+ @JvmStatic
+ internal fun toBundle(requestJson: String, allowHybrid: Boolean): Bundle {
+ val bundle = Bundle()
+ bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+ bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+ return bundle
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt
index 23abbfb..1641f43 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* A privileged request to register a passkey from the user’s public key credential provider, where
* the caller can modify the rp. Only callers with privileged permission, e.g. user’s default
@@ -38,7 +41,12 @@
val clientDataHash: String,
@get:JvmName("allowHybrid")
val allowHybrid: Boolean = true
-) : CreatePublicKeyCredentialBaseRequest(requestJson) {
+) : CreatePublicKeyCredentialBaseRequest(
+ requestJson,
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+ toBundle(requestJson, rp, clientDataHash, allowHybrid),
+ false,
+) {
init {
require(rp.isNotEmpty()) { "rp must not be empty" }
@@ -88,4 +96,30 @@
this.rp, this.clientDataHash, this.allowHybrid)
}
}
+
+ /** @hide */
+ companion object {
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_RP = "androidx.credentials.BUNDLE_KEY_RP"
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_CLIENT_DATA_HASH =
+ "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+
+ @JvmStatic
+ internal fun toBundle(
+ requestJson: String,
+ rp: String,
+ clientDataHash: String,
+ allowHybrid: Boolean
+ ): Bundle {
+ val bundle = Bundle()
+ bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+ bundle.putString(BUNDLE_KEY_RP, rp)
+ bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
+ bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+ return bundle
+ }
+ }
}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt
index f4cf4b9..7b5cd1d 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* A response of a public key credential (passkey) flow.
*
@@ -28,10 +31,27 @@
*/
class CreatePublicKeyCredentialResponse(
val registrationResponseJson: String
-) : CreateCredentialResponse() {
+) : CreateCredentialResponse(
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+ toBundle(registrationResponseJson)
+) {
init {
require(registrationResponseJson.isNotEmpty()) { "registrationResponseJson must not be " +
"empty" }
}
+
+ /** @hide */
+ companion object {
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_REGISTRATION_RESPONSE_JSON =
+ "androidx.credentials.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON"
+
+ @JvmStatic
+ internal fun toBundle(registrationResponseJson: String): Bundle {
+ val bundle = Bundle()
+ bundle.putString(BUNDLE_KEY_REGISTRATION_RESPONSE_JSON, registrationResponseJson)
+ return bundle
+ }
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/Credential.kt b/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
index eb19ff8..d1d9629 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
@@ -16,5 +16,12 @@
package androidx.credentials
-/** Base class for a credential with which the user consented to authenticate to the app. */
-abstract class Credential
\ No newline at end of file
+import android.os.Bundle
+
+/**
+ * Base class for a credential with which the user consented to authenticate to the app.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass
+ * @property data the credential data in the [Bundle] format.
+ */
+open class Credential(val type: String, val data: Bundle)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/FederatedCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/FederatedCredential.kt
index dfc262c..e5c2bbf 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/FederatedCredential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/FederatedCredential.kt
@@ -16,6 +16,8 @@
package androidx.credentials
+import android.os.Bundle
+
/**
* A federated credential fetched from a federated identity provider (FedCM).
*
@@ -24,7 +26,10 @@
*
* @hide
*/
-class FederatedCredential private constructor() {
+class FederatedCredential private constructor() : Credential(
+ TYPE_FEDERATED_CREDENTIAL,
+ Bundle(),
+) {
companion object {
/** The type value for federated credential related operations. */
const val TYPE_FEDERATED_CREDENTIAL: String = "type.federated_credential"
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetCredentialOption.kt
index 2fcd72a..c75a157 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetCredentialOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetCredentialOption.kt
@@ -16,5 +16,21 @@
package androidx.credentials
-/** Base request class for getting a registered credential. */
-abstract class GetCredentialOption
\ No newline at end of file
+import android.os.Bundle
+
+/**
+ * Base class for getting a specific type of credentials.
+ *
+ * [GetCredentialRequest] will be composed of a list of [GetCredentialOption] subclasses to indicate
+ * the specific credential types and configurations that your app accepts.
+ *
+ * @property type the credential type determined by the credential-type-specific subclass
+ * @property data the request data in the [Bundle] format
+ * @property requireSystemProvider true if must only be fulfilled by a system provider and false
+ * otherwise
+ */
+open class GetCredentialOption(
+ val type: String,
+ val data: Bundle,
+ val requireSystemProvider: Boolean,
+)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt
index 920cbf2..a43927e 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPasswordOption.kt
@@ -16,5 +16,11 @@
package androidx.credentials
+import android.os.Bundle
+
/** A request to retrieve the user's saved application password from their password provider. */
-class GetPasswordOption : GetCredentialOption()
\ No newline at end of file
+class GetPasswordOption : GetCredentialOption(
+ PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+ Bundle(),
+ false,
+)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt
index d42c089..23cd3a3 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* Base request class for getting a registered public key credential.
*
@@ -27,10 +30,19 @@
* @hide
*/
abstract class GetPublicKeyCredentialBaseOption constructor(
- val requestJson: String
-) : GetCredentialOption() {
+ val requestJson: String,
+ type: String,
+ data: Bundle,
+ requireSystemProvider: Boolean,
+) : GetCredentialOption(type, data, requireSystemProvider) {
init {
require(requestJson.isNotEmpty()) { "request json must not be empty" }
}
+
+ /** @hide */
+ companion object {
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ const val BUNDLE_KEY_REQUEST_JSON = "androidx.credentials.BUNDLE_KEY_REQUEST_JSON"
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
index d372ffa..7f449d4 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
@@ -1,4 +1,4 @@
-package androidx.credentials/*
+/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+package androidx.credentials
+
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* A request to get passkeys from the user's public key credential provider.
*
@@ -30,4 +35,23 @@
requestJson: String,
@get:JvmName("allowHybrid")
val allowHybrid: Boolean = true,
-) : GetPublicKeyCredentialBaseOption(requestJson)
\ No newline at end of file
+) : GetPublicKeyCredentialBaseOption(
+ requestJson,
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+ toBundle(requestJson, allowHybrid),
+ false
+) {
+ /** @hide */
+ companion object {
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+
+ @JvmStatic
+ internal fun toBundle(requestJson: String, allowHybrid: Boolean): Bundle {
+ val bundle = Bundle()
+ bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+ bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+ return bundle
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt
index a8fc6a2..2228e7b 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* A privileged request to get passkeys from the user's public key credential provider. The caller
* can modify the RP. Only callers with privileged permission (e.g. user's public browser or caBLE)
@@ -38,7 +41,12 @@
val clientDataHash: String,
@get:JvmName("allowHybrid")
val allowHybrid: Boolean = true
-) : GetPublicKeyCredentialBaseOption(requestJson) {
+) : GetPublicKeyCredentialBaseOption(
+ requestJson,
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+ toBundle(requestJson, rp, clientDataHash, allowHybrid),
+ false,
+) {
init {
require(rp.isNotEmpty()) { "rp must not be empty" }
@@ -88,4 +96,30 @@
this.rp, this.clientDataHash, this.allowHybrid)
}
}
+
+ /** @hide */
+ companion object {
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_RP = "androidx.credentials.BUNDLE_KEY_RP"
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_CLIENT_DATA_HASH =
+ "androidx.credentials.BUNDLE_KEY_CLIENT_DATA_HASH"
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_ALLOW_HYBRID = "androidx.credentials.BUNDLE_KEY_ALLOW_HYBRID"
+
+ @JvmStatic
+ internal fun toBundle(
+ requestJson: String,
+ rp: String,
+ clientDataHash: String,
+ allowHybrid: Boolean
+ ): Bundle {
+ val bundle = Bundle()
+ bundle.putString(BUNDLE_KEY_REQUEST_JSON, requestJson)
+ bundle.putString(BUNDLE_KEY_RP, rp)
+ bundle.putString(BUNDLE_KEY_CLIENT_DATA_HASH, clientDataHash)
+ bundle.putBoolean(BUNDLE_KEY_ALLOW_HYBRID, allowHybrid)
+ return bundle
+ }
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt
index 252f4a1..3c42d37 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/PasswordCredential.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* Represents the user's password credential granted by the user for app sign-in.
*
@@ -28,9 +31,30 @@
class PasswordCredential constructor(
val id: String,
val password: String,
-) : Credential() {
+) : Credential(TYPE_PASSWORD_CREDENTIAL, toBundle(id, password)) {
init {
require(password.isNotEmpty()) { "password should not be empty" }
}
+
+ /** @hide */
+ companion object {
+ // TODO: this type is officially defined in the framework. This definition should be
+ // removed when the framework type is available in jetpack.
+ /** @hide */
+ const val TYPE_PASSWORD_CREDENTIAL: String = "android.credentials.TYPE_PASSWORD_CREDENTIAL"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
+
+ @JvmStatic
+ internal fun toBundle(id: String, password: String): Bundle {
+ val bundle = Bundle()
+ bundle.putString(BUNDLE_KEY_ID, id)
+ bundle.putString(BUNDLE_KEY_PASSWORD, password)
+ return bundle
+ }
+ }
}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
index ccfc29c..1a87b3c 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
@@ -16,6 +16,9 @@
package androidx.credentials
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+
/**
* Represents the user's passkey credential granted by the user for app sign-in.
*
@@ -30,7 +33,10 @@
*/
class PublicKeyCredential constructor(
val authenticationResponseJson: String
-) : Credential() {
+) : Credential(
+ TYPE_PUBLIC_KEY_CREDENTIAL,
+ toBundle(authenticationResponseJson)
+) {
init {
require(authenticationResponseJson.isNotEmpty()) {
@@ -40,5 +46,16 @@
/** The type value for public key credential related operations. */
const val TYPE_PUBLIC_KEY_CREDENTIAL: String =
"androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ const val BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON =
+ "androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON"
+
+ @JvmStatic
+ internal fun toBundle(authenticationResponseJson: String): Bundle {
+ val bundle = Bundle()
+ bundle.putString(BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON, authenticationResponseJson)
+ return bundle
+ }
}
}
\ No newline at end of file
diff --git a/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioSerializer.kt b/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioSerializer.kt
index 6b969da..cbd6d75 100644
--- a/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioSerializer.kt
+++ b/datastore/datastore-core-okio/src/commonMain/kotlin/androidx/datastore/core/okio/OkioSerializer.kt
@@ -43,7 +43,7 @@
* Marshal object to a Sink.
*
* @param t the data to write to output
- * @output the BufferedSink to serialize data to
+ * @param sink the BufferedSink to serialize data to
*/
public suspend fun writeTo(t: T, sink: BufferedSink)
}
\ No newline at end of file
diff --git a/development/diff_published_artifacts/.gitignore b/development/diff_published_artifacts/.gitignore
new file mode 100644
index 0000000..16e340a
--- /dev/null
+++ b/development/diff_published_artifacts/.gitignore
@@ -0,0 +1 @@
+download_staging/*
\ No newline at end of file
diff --git a/development/diff_published_artifacts/diff_published_artifacts.py b/development/diff_published_artifacts/diff_published_artifacts.py
new file mode 100644
index 0000000..036b255
--- /dev/null
+++ b/development/diff_published_artifacts/diff_published_artifacts.py
@@ -0,0 +1,248 @@
+# Copyright 2022, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import os
+import subprocess
+import shutil
+import re
+import requests
+import zipfile
+import sys
+
+FETCH_ARTIFACT = "/google/data/ro/projects/android/fetch_artifact"
+BASS = "/google/data/ro/projects/android/bass"
+DEFAULT_CLONE_DEPTH = 100 # chosen arbitrarily, may need to be adjusted
+DEFAULT_BUILD_ARTIFACT_SEARCH_TIME_SPAN_IN_DAYS = 7 # chosen arbitrarily, may need to be adjusted
+GIT_LOG_URL = "https://android.googlesource.com/platform/frameworks/support/+log"
+IGNORE_PATHS = [
+ # includes timestamps
+ "*.xml",
+ # are different because the xml files include timestamps
+ "*.xml.*",
+ "*.sha*",
+ "*.md5",
+ # is different because it references the .sha* files.
+ "*.module"
+]
+
+
+def main(build_id):
+ """
+ This is a script to take a given build_id, and search for a build of the previous commit on ab/
+ If a commit is found, we download the top-of-tree-m2repository-all-{build_id}.zip file from both
+ builds and diff the contents. If an .aar / .jar is different, we will unzip those and diff the
+ contents as well.
+ """
+ staging_dir = prep_staging_dir()
+ build_info_file_path = fetch_build_info(build_id, staging_dir)
+
+ # presubmit BUILD_INFO files include the commit being built as well as the and parent commit
+ if is_presubmit_build(build_id):
+ previous_revision = get_previous_revision_from_build_info(build_info_file_path)
+ # other builds only include the commit being built (as far as I can tell), we need to
+ # get the previous commit from git log
+ else:
+ current_revision = get_current_revision(build_info_file_path)
+ previous_revision = get_previous_revision_from_git_history(current_revision, staging_dir)
+ previous_build_id = get_previous_build_id(previous_revision)
+ (before, after) = download_and_unzip_repos(staging_dir, build_id, previous_build_id)
+ diff_repos(before, after, staging_dir)
+
+def prep_staging_dir():
+ """
+ remove and recreate the ./download_staging which is located as a sibling of this script.
+ """
+ current_dir = os.path.dirname(os.path.realpath(__file__))
+ staging_dir = current_dir + "/download_staging"
+ if os.path.isdir(staging_dir):
+ shutil.rmtree(staging_dir)
+ os.makedirs(staging_dir, exist_ok=True)
+ return staging_dir
+
+
+def fetch_m2repo(build_id, staging_dir):
+ file_path = f"top-of-tree-m2repository-all-{build_id}.zip"
+ if is_presubmit_build(build_id):
+ file_path = f"incremental/{file_path}"
+ return fetch_artifact(build_id, staging_dir, file_path)
+
+
+def fetch_build_info(build_id, staging_dir):
+ return fetch_artifact(build_id, staging_dir, "BUILD_INFO")
+
+
+def fetch_artifact(build_id, output_dir, file_path):
+ file_name = file_path.split("/")[-1]
+ print(f"fetching {file_name}")
+
+ if is_presubmit_build(build_id):
+ target = "androidx_incremental"
+ else:
+ target = "androidx"
+ return FetchArtifactService().fetch_artifact(build_id, "aosp-androidx-main", target, output_dir, file_path)
+
+
+def get_current_revision(build_info_file_path):
+ print("Getting current revision from BUILD_INFO")
+ with open(build_info_file_path) as f:
+ build_info = json.load(f)
+ support_project = next(
+ project for project in build_info["parsed_manifest"]["projects"] if
+ project["name"] == "platform/frameworks/support")
+ current_revision = support_project["revision"]
+ print(f"Found revision: {current_revision}")
+ return current_revision
+
+
+def get_previous_revision_from_build_info(build_info_file_path):
+ print("Getting previous revision from BUILD_INFO")
+ with open(build_info_file_path) as f:
+ build_info = json.load(f)
+ revision = build_info["git-pull"][0]["revisions"][0]["commit"]["parents"][0]["commitId"]
+ print(f"Found previous revision: {revision}")
+ return revision
+
+def get_previous_revision_from_git_history(current_revision, staging_dir):
+ """
+ Gets previous revision from git log endpoint for androidx-main.
+ """
+ response = requests.get(git_log_url(current_revision))
+ # endpoint returns some junk in the first line making it invalid json
+ text_with_first_line_removed = "\n".join(response.text.split("\n")[1:])
+ response_json = json.loads(text_with_first_line_removed)
+ previous_revision = response_json["log"][0]["parents"][0]
+ print(f"Found previous revision: {previous_revision}")
+ return previous_revision
+
+
+def get_previous_build_id(previous_revision):
+ print("Searching Android Build server for build matching previous revision")
+ output = BassService().search_builds(
+ DEFAULT_BUILD_ARTIFACT_SEARCH_TIME_SPAN_IN_DAYS,
+ "aosp-androidx-main",
+ "androidx",
+ "BUILD_INFO",
+ previous_revision
+ )
+ match = re.search("BuildID\: (\d+)", output.stdout)
+ if match is None:
+ raise Exception(f"Couldn't find previous build ID for revision {previous_revision}")
+
+ previous_build_id = match.group(1)
+ print(f"Found build matching previous revision: {previous_build_id}")
+ return previous_build_id
+
+def download_and_unzip_repos(staging_dir, build_id, previous_build_id):
+ before_dir = staging_dir + "/before"
+ after_dir = staging_dir + "/after"
+ os.makedirs(before_dir)
+ os.makedirs(after_dir)
+ after_zip = fetch_m2repo(build_id, staging_dir)
+ before_zip = fetch_m2repo(previous_build_id, staging_dir)
+ return (unzip(before_zip, before_dir), unzip(after_zip, after_dir))
+
+def diff_repos(before, after, staging_dir):
+ output = DiffService().diff(before, after, IGNORE_PATHS)
+ for line in output.stdout.splitlines():
+ if line.startswith("Binary files "):
+ for (before_file, after_file) in re.findall("Binary files (.+) and (.+) differ", line):
+ diff_binary(before_file, after_file, staging_dir)
+ else:
+ print(line)
+
+
+def diff_binary(before, after, staging_dir):
+ file_name = before.split("/")[-1]
+ if is_unzippable(before) and is_unzippable(after):
+ before_contents = unzip(before, staging_dir + "/" + file_name + "-before")
+ after_contents = unzip(after, staging_dir + "/" + file_name + "-after")
+ output = DiffService().diff(before_contents, after_contents)
+ # sometimes the binary is "different" but the contents are identical.
+ # It might be interesting to add diff the metadata, but for now just ignore it.
+ if output.stdout.strip() != "":
+ print(output.stdout)
+ else:
+ print(f"Binary files {before} and {after} differ")
+
+def is_unzippable(filename):
+ return filename.endswith(".zip") or filename.endswith(".aar") or filename.endswith(".jar")
+
+def unzip(file, destination):
+ with zipfile.ZipFile(file, 'r') as zip:
+ zip.extractall(destination)
+ return destination
+
+def is_presubmit_build(build_id):
+ return build_id.startswith("P")
+
+def git_log_url(revision):
+ return f"{GIT_LOG_URL}/{revision}?format=JSON"
+
+class DiffService():
+ @staticmethod
+ def diff(before_dir, after_dir, exclude=[]):
+ args = ["diff", "-r"]
+ for pattern in exclude:
+ args.extend(["-x", pattern])
+ args.extend([before_dir, after_dir])
+ return subprocess.run(args, text=True, capture_output=True)
+
+
+class FetchArtifactService():
+ @staticmethod
+ def fetch_artifact(build_id, branch, target, output_dir, file_path):
+ file_name = file_path.split("/")[-1]
+ subprocess.run(
+ [
+ FETCH_ARTIFACT,
+ "--bid",
+ build_id,
+ "--branch",
+ branch,
+ "--target",
+ target,
+ file_path,
+ ],
+ cwd=output_dir,
+ capture_output=True,
+ check=True
+ )
+ return f"{output_dir}/{file_name}"
+
+class BassService():
+ @staticmethod
+ def search_builds(days, branch, target, file_name, query):
+ return subprocess.run([
+ BASS,
+ "--days",
+ str(days),
+ "--successful",
+ "true",
+ "--branch",
+ branch,
+ "--target",
+ target,
+ "--filename",
+ file_name,
+ "--query",
+ query
+ ],
+ capture_output=True,
+ text=True,
+ check=True
+ )
+
+if __name__ == "__main__":
+ main(sys.argv[1])
\ No newline at end of file
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 5dcc6c6..0ad099d 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -37,16 +37,16 @@
docs("androidx.biometric:biometric-ktx:1.2.0-alpha05")
samples("androidx.biometric:biometric-ktx-samples:1.2.0-alpha05")
docs("androidx.browser:browser:1.5.0-alpha01")
- docs("androidx.camera:camera-camera2:1.2.0-rc01")
- docs("androidx.camera:camera-core:1.2.0-rc01")
- docs("androidx.camera:camera-extensions:1.2.0-rc01")
+ docs("androidx.camera:camera-camera2:1.3.0-alpha01")
+ docs("androidx.camera:camera-core:1.3.0-alpha01")
+ docs("androidx.camera:camera-extensions:1.3.0-alpha01")
stubs(fileTree(dir: "../camera/camera-extensions-stub", include: ["camera-extensions-stub.jar"]))
- docs("androidx.camera:camera-lifecycle:1.2.0-rc01")
- docs("androidx.camera:camera-mlkit-vision:1.2.0-rc01")
+ docs("androidx.camera:camera-lifecycle:1.3.0-alpha01")
+ docs("androidx.camera:camera-mlkit-vision:1.3.0-alpha01")
docs("androidx.camera:camera-previewview:1.1.0-beta02")
- docs("androidx.camera:camera-video:1.2.0-rc01")
- docs("androidx.camera:camera-view:1.2.0-rc01")
- docs("androidx.camera:camera-viewfinder:1.2.0-rc01")
+ docs("androidx.camera:camera-video:1.3.0-alpha01")
+ docs("androidx.camera:camera-view:1.3.0-alpha01")
+ docs("androidx.camera:camera-viewfinder:1.3.0-alpha01")
docs("androidx.car.app:app:1.3.0-beta01")
docs("androidx.car.app:app-automotive:1.3.0-beta01")
docs("androidx.car.app:app-projected:1.3.0-beta01")
@@ -54,55 +54,55 @@
docs("androidx.cardview:cardview:1.0.0")
docs("androidx.collection:collection:1.3.0-alpha02")
docs("androidx.collection:collection-ktx:1.3.0-alpha02")
- docs("androidx.compose.animation:animation:1.4.0-alpha01")
- docs("androidx.compose.animation:animation-core:1.4.0-alpha01")
- docs("androidx.compose.animation:animation-graphics:1.4.0-alpha01")
- samples("androidx.compose.animation:animation-samples:1.4.0-alpha01")
- samples("androidx.compose.animation:animation-core-samples:1.4.0-alpha01")
- samples("androidx.compose.animation:animation-graphics-samples:1.4.0-alpha01")
- docs("androidx.compose.foundation:foundation:1.4.0-alpha01")
- docs("androidx.compose.foundation:foundation-layout:1.4.0-alpha01")
- samples("androidx.compose.foundation:foundation-layout-samples:1.4.0-alpha01")
- samples("androidx.compose.foundation:foundation-samples:1.4.0-alpha01")
- docs("androidx.compose.material3:material3:1.1.0-alpha01")
- samples("androidx.compose.material3:material3-samples:1.1.0-alpha01")
- docs("androidx.compose.material3:material3-window-size-class:1.1.0-alpha01")
- samples("androidx.compose.material3:material3-window-size-class-samples:1.1.0-alpha01")
- docs("androidx.compose.material:material:1.4.0-alpha01")
- docs("androidx.compose.material:material-icons-core:1.4.0-alpha01")
- samples("androidx.compose.material:material-icons-core-samples:1.4.0-alpha01")
- docs("androidx.compose.material:material-ripple:1.4.0-alpha01")
- samples("androidx.compose.material:material-samples:1.4.0-alpha01")
- docs("androidx.compose.runtime:runtime:1.4.0-alpha01")
- docs("androidx.compose.runtime:runtime-livedata:1.4.0-alpha01")
- samples("androidx.compose.runtime:runtime-livedata-samples:1.4.0-alpha01")
- docs("androidx.compose.runtime:runtime-rxjava2:1.4.0-alpha01")
- samples("androidx.compose.runtime:runtime-rxjava2-samples:1.4.0-alpha01")
- docs("androidx.compose.runtime:runtime-rxjava3:1.4.0-alpha01")
- samples("androidx.compose.runtime:runtime-rxjava3-samples:1.4.0-alpha01")
- docs("androidx.compose.runtime:runtime-saveable:1.4.0-alpha01")
- samples("androidx.compose.runtime:runtime-saveable-samples:1.4.0-alpha01")
- samples("androidx.compose.runtime:runtime-samples:1.4.0-alpha01")
- docs("androidx.compose.runtime:runtime-tracing:1.0.0-alpha01")
- docs("androidx.compose.ui:ui:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-geometry:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-graphics:1.4.0-alpha01")
- samples("androidx.compose.ui:ui-graphics-samples:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-test:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-test-junit4:1.4.0-alpha01")
- samples("androidx.compose.ui:ui-test-samples:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-text:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-text-google-fonts:1.4.0-alpha01")
- samples("androidx.compose.ui:ui-text-samples:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-tooling:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-tooling-data:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-tooling-preview:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-unit:1.4.0-alpha01")
- samples("androidx.compose.ui:ui-unit-samples:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-util:1.4.0-alpha01")
- docs("androidx.compose.ui:ui-viewbinding:1.4.0-alpha01")
- samples("androidx.compose.ui:ui-viewbinding-samples:1.4.0-alpha01")
- samples("androidx.compose.ui:ui-samples:1.4.0-alpha01")
+ docs("androidx.compose.animation:animation:1.4.0-alpha02")
+ docs("androidx.compose.animation:animation-core:1.4.0-alpha02")
+ docs("androidx.compose.animation:animation-graphics:1.4.0-alpha02")
+ samples("androidx.compose.animation:animation-samples:1.4.0-alpha02")
+ samples("androidx.compose.animation:animation-core-samples:1.4.0-alpha02")
+ samples("androidx.compose.animation:animation-graphics-samples:1.4.0-alpha02")
+ docs("androidx.compose.foundation:foundation:1.4.0-alpha02")
+ docs("androidx.compose.foundation:foundation-layout:1.4.0-alpha02")
+ samples("androidx.compose.foundation:foundation-layout-samples:1.4.0-alpha02")
+ samples("androidx.compose.foundation:foundation-samples:1.4.0-alpha02")
+ docs("androidx.compose.material3:material3:1.1.0-alpha02")
+ samples("androidx.compose.material3:material3-samples:1.1.0-alpha02")
+ docs("androidx.compose.material3:material3-window-size-class:1.1.0-alpha02")
+ samples("androidx.compose.material3:material3-window-size-class-samples:1.1.0-alpha02")
+ docs("androidx.compose.material:material:1.4.0-alpha02")
+ docs("androidx.compose.material:material-icons-core:1.4.0-alpha02")
+ samples("androidx.compose.material:material-icons-core-samples:1.4.0-alpha02")
+ docs("androidx.compose.material:material-ripple:1.4.0-alpha02")
+ samples("androidx.compose.material:material-samples:1.4.0-alpha02")
+ docs("androidx.compose.runtime:runtime:1.4.0-alpha02")
+ docs("androidx.compose.runtime:runtime-livedata:1.4.0-alpha02")
+ samples("androidx.compose.runtime:runtime-livedata-samples:1.4.0-alpha02")
+ docs("androidx.compose.runtime:runtime-rxjava2:1.4.0-alpha02")
+ samples("androidx.compose.runtime:runtime-rxjava2-samples:1.4.0-alpha02")
+ docs("androidx.compose.runtime:runtime-rxjava3:1.4.0-alpha02")
+ samples("androidx.compose.runtime:runtime-rxjava3-samples:1.4.0-alpha02")
+ docs("androidx.compose.runtime:runtime-saveable:1.4.0-alpha02")
+ samples("androidx.compose.runtime:runtime-saveable-samples:1.4.0-alpha02")
+ samples("androidx.compose.runtime:runtime-samples:1.4.0-alpha02")
+ docs("androidx.compose.runtime:runtime-tracing:1.0.0-alpha02")
+ docs("androidx.compose.ui:ui:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-geometry:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-graphics:1.4.0-alpha02")
+ samples("androidx.compose.ui:ui-graphics-samples:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-test:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-test-junit4:1.4.0-alpha02")
+ samples("androidx.compose.ui:ui-test-samples:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-text:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-text-google-fonts:1.4.0-alpha02")
+ samples("androidx.compose.ui:ui-text-samples:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-tooling:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-tooling-data:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-tooling-preview:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-unit:1.4.0-alpha02")
+ samples("androidx.compose.ui:ui-unit-samples:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-util:1.4.0-alpha02")
+ docs("androidx.compose.ui:ui-viewbinding:1.4.0-alpha02")
+ samples("androidx.compose.ui:ui-viewbinding-samples:1.4.0-alpha02")
+ samples("androidx.compose.ui:ui-samples:1.4.0-alpha02")
docs("androidx.concurrent:concurrent-futures:1.1.0")
docs("androidx.concurrent:concurrent-futures-ktx:1.1.0")
docs("androidx.contentpager:contentpager:1.0.0")
@@ -122,14 +122,15 @@
docs("androidx.cursoradapter:cursoradapter:1.0.0")
docs("androidx.customview:customview:1.2.0-alpha02")
docs("androidx.customview:customview-poolingcontainer:1.0.0-rc01")
- docs("androidx.datastore:datastore:1.0.0")
- docs("androidx.datastore:datastore-core:1.0.0")
- docs("androidx.datastore:datastore-preferences:1.0.0")
- docs("androidx.datastore:datastore-preferences-core:1.0.0")
- docs("androidx.datastore:datastore-preferences-rxjava2:1.0.0")
- docs("androidx.datastore:datastore-preferences-rxjava3:1.0.0")
- docs("androidx.datastore:datastore-rxjava2:1.0.0")
- docs("androidx.datastore:datastore-rxjava3:1.0.0")
+ docs("androidx.datastore:datastore:1.1.0-alpha01")
+ docs("androidx.datastore:datastore-core:1.1.0-alpha01")
+ docs("androidx.datastore:datastore-core-okio:1.1.0-alpha01")
+ docs("androidx.datastore:datastore-preferences:1.1.0-alpha01")
+ docs("androidx.datastore:datastore-preferences-core:1.1.0-alpha01")
+ docs("androidx.datastore:datastore-preferences-rxjava2:1.1.0-alpha01")
+ docs("androidx.datastore:datastore-preferences-rxjava3:1.1.0-alpha01")
+ docs("androidx.datastore:datastore-rxjava2:1.1.0-alpha01")
+ docs("androidx.datastore:datastore-rxjava3:1.1.0-alpha01")
docs("androidx.documentfile:documentfile:1.1.0-alpha01")
docs("androidx.draganddrop:draganddrop:1.0.0")
docs("androidx.drawerlayout:drawerlayout:1.2.0-alpha01")
@@ -155,7 +156,7 @@
docs("androidx.glance:glance-preview:1.0.0-alpha05")
docs("androidx.glance:glance-wear-tiles:1.0.0-alpha05")
docs("androidx.glance:glance-wear-tiles-preview:1.0.0-alpha05")
- docs("androidx.graphics:graphics-core:1.0.0-alpha01")
+ docs("androidx.graphics:graphics-core:1.0.0-alpha02")
docs("androidx.gridlayout:gridlayout:1.0.0")
docs("androidx.health.connect:connect-client:1.0.0-alpha07")
samples("androidx.health.connect:connect-client-samples:1.0.0-alpha07")
@@ -203,22 +204,22 @@
docs("androidx.media2:media2-session:1.2.1")
docs("androidx.media2:media2-widget:1.2.1")
docs("androidx.media:media:1.6.0")
- docs("androidx.mediarouter:mediarouter:1.3.1")
- docs("androidx.mediarouter:mediarouter-testing:1.3.1")
+ docs("androidx.mediarouter:mediarouter:1.4.0-alpha01")
+ docs("androidx.mediarouter:mediarouter-testing:1.4.0-alpha01")
docs("androidx.metrics:metrics-performance:1.0.0-alpha03")
- docs("androidx.navigation:navigation-common:2.6.0-alpha03")
- docs("androidx.navigation:navigation-common-ktx:2.6.0-alpha03")
- docs("androidx.navigation:navigation-compose:2.6.0-alpha03")
- samples("androidx.navigation:navigation-compose-samples:2.6.0-alpha03")
- docs("androidx.navigation:navigation-dynamic-features-fragment:2.6.0-alpha03")
- docs("androidx.navigation:navigation-dynamic-features-runtime:2.6.0-alpha03")
- docs("androidx.navigation:navigation-fragment:2.6.0-alpha03")
- docs("androidx.navigation:navigation-fragment-ktx:2.6.0-alpha03")
- docs("androidx.navigation:navigation-runtime:2.6.0-alpha03")
- docs("androidx.navigation:navigation-runtime-ktx:2.6.0-alpha03")
- docs("androidx.navigation:navigation-testing:2.6.0-alpha03")
- docs("androidx.navigation:navigation-ui:2.6.0-alpha03")
- docs("androidx.navigation:navigation-ui-ktx:2.6.0-alpha03")
+ docs("androidx.navigation:navigation-common:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-common-ktx:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-compose:2.6.0-alpha04")
+ samples("androidx.navigation:navigation-compose-samples:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-dynamic-features-fragment:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-dynamic-features-runtime:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-fragment:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-fragment-ktx:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-runtime:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-runtime-ktx:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-testing:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-ui:2.6.0-alpha04")
+ docs("androidx.navigation:navigation-ui-ktx:2.6.0-alpha04")
docs("androidx.paging:paging-common:3.2.0-alpha03")
docs("androidx.paging:paging-common-ktx:3.2.0-alpha03")
docs("androidx.paging:paging-compose:1.0.0-alpha17")
@@ -237,31 +238,35 @@
docs("androidx.preference:preference:1.2.0")
docs("androidx.preference:preference-ktx:1.2.0")
docs("androidx.print:print:1.1.0-beta01")
- docs("androidx.profileinstaller:profileinstaller:1.3.0-alpha01")
+ docs("androidx.privacysandbox.tools:tools:1.0.0-alpha01")
+ docs("androidx.privacysandbox.tools:tools-apigenerator:1.0.0-alpha01")
+ docs("androidx.privacysandbox.tools:tools-apipackager:1.0.0-alpha01")
+ docs("androidx.privacysandbox.tools:tools-core:1.0.0-alpha01")
+ docs("androidx.profileinstaller:profileinstaller:1.3.0-alpha02")
docs("androidx.recommendation:recommendation:1.0.0")
docs("androidx.recyclerview:recyclerview:1.3.0-rc01")
docs("androidx.recyclerview:recyclerview-selection:2.0.0-alpha01")
docs("androidx.remotecallback:remotecallback:1.0.0-alpha02")
docs("androidx.resourceinspection:resourceinspection-annotation:1.0.1")
docs("androidx.resourceinspection:resourceinspection-processor:1.0.1")
- docs("androidx.room:room-common:2.5.0-beta01")
- docs("androidx.room:room-guava:2.5.0-beta01")
- docs("androidx.room:room-ktx:2.5.0-beta01")
- docs("androidx.room:room-migration:2.5.0-beta01")
- docs("androidx.room:room-paging:2.5.0-beta01")
- docs("androidx.room:room-paging-guava:2.5.0-beta01")
- docs("androidx.room:room-paging-rxjava2:2.5.0-beta01")
- docs("androidx.room:room-paging-rxjava3:2.5.0-beta01")
- docs("androidx.room:room-runtime:2.5.0-beta01")
- docs("androidx.room:room-rxjava2:2.5.0-beta01")
- docs("androidx.room:room-rxjava3:2.5.0-beta01")
- docs("androidx.room:room-testing:2.5.0-beta01")
+ docs("androidx.room:room-common:2.5.0-beta02")
+ docs("androidx.room:room-guava:2.5.0-beta02")
+ docs("androidx.room:room-ktx:2.5.0-beta02")
+ docs("androidx.room:room-migration:2.5.0-beta02")
+ docs("androidx.room:room-paging:2.5.0-beta02")
+ docs("androidx.room:room-paging-guava:2.5.0-beta02")
+ docs("androidx.room:room-paging-rxjava2:2.5.0-beta02")
+ docs("androidx.room:room-paging-rxjava3:2.5.0-beta02")
+ docs("androidx.room:room-runtime:2.5.0-beta02")
+ docs("androidx.room:room-rxjava2:2.5.0-beta02")
+ docs("androidx.room:room-rxjava3:2.5.0-beta02")
+ docs("androidx.room:room-testing:2.5.0-beta02")
docs("androidx.savedstate:savedstate:1.2.0")
docs("androidx.savedstate:savedstate-ktx:1.2.0")
docs("androidx.security:security-app-authenticator:1.0.0-alpha02")
docs("androidx.security:security-app-authenticator-testing:1.0.0-alpha01")
- docs("androidx.security:security-crypto:1.1.0-alpha03")
- docs("androidx.security:security-crypto-ktx:1.1.0-alpha02")
+ docs("androidx.security:security-crypto:1.1.0-alpha04")
+ docs("androidx.security:security-crypto-ktx:1.1.0-alpha04")
docs("androidx.security:security-identity-credential:1.0.0-alpha03")
docs("androidx.sharetarget:sharetarget:1.2.0")
docs("androidx.slice:slice-builders:1.1.0-alpha02")
@@ -269,21 +274,21 @@
docs("androidx.slice:slice-core:1.1.0-alpha02")
docs("androidx.slice:slice-view:1.1.0-alpha02")
docs("androidx.slidingpanelayout:slidingpanelayout:1.2.0")
- docs("androidx.sqlite:sqlite:2.3.0-beta01")
- docs("androidx.sqlite:sqlite-framework:2.3.0-beta01")
- docs("androidx.sqlite:sqlite-ktx:2.3.0-beta01")
+ docs("androidx.sqlite:sqlite:2.3.0-beta02")
+ docs("androidx.sqlite:sqlite-framework:2.3.0-beta02")
+ docs("androidx.sqlite:sqlite-ktx:2.3.0-beta02")
docs("androidx.startup:startup-runtime:1.2.0-alpha01")
docs("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
docs("androidx.test.uiautomator:uiautomator:2.3.0-alpha01")
docs("androidx.textclassifier:textclassifier:1.0.0-alpha04")
docs("androidx.tracing:tracing:1.2.0-alpha02")
docs("androidx.tracing:tracing-ktx:1.2.0-alpha02")
- docs("androidx.tracing:tracing-perfetto:1.0.0-alpha06")
- docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha06")
+ docs("androidx.tracing:tracing-perfetto:1.0.0-alpha07")
+ docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha07")
docs("androidx.transition:transition:1.4.1")
docs("androidx.transition:transition-ktx:1.4.1")
- docs("androidx.tv:tv-foundation:1.0.0-alpha01")
- docs("androidx.tv:tv-material:1.0.0-alpha01")
+ docs("androidx.tv:tv-foundation:1.0.0-alpha02")
+ docs("androidx.tv:tv-material:1.0.0-alpha02")
docs("androidx.tvprovider:tvprovider:1.1.0-alpha01")
docs("androidx.vectordrawable:vectordrawable:1.2.0-beta01")
docs("androidx.vectordrawable:vectordrawable-animated:1.2.0-alpha01")
@@ -291,33 +296,33 @@
docs("androidx.versionedparcelable:versionedparcelable:1.1.1")
docs("androidx.viewpager2:viewpager2:1.1.0-beta01")
docs("androidx.viewpager:viewpager:1.1.0-alpha01")
- docs("androidx.wear.compose:compose-foundation:1.1.0-beta01")
- samples("androidx.wear.compose:compose-foundation-samples:1.1.0-beta01")
- docs("androidx.wear.compose:compose-material:1.1.0-beta01")
- samples("androidx.wear.compose:compose-material-samples:1.1.0-beta01")
- docs("androidx.wear.compose:compose-navigation:1.1.0-beta01")
- samples("androidx.wear.compose:compose-navigation-samples:1.1.0-beta01")
+ docs("androidx.wear.compose:compose-foundation:1.1.0-rc01")
+ samples("androidx.wear.compose:compose-foundation-samples:1.1.0-rc01")
+ docs("androidx.wear.compose:compose-material:1.1.0-rc01")
+ samples("androidx.wear.compose:compose-material-samples:1.1.0-rc01")
+ docs("androidx.wear.compose:compose-navigation:1.1.0-rc01")
+ samples("androidx.wear.compose:compose-navigation-samples:1.1.0-rc01")
docs("androidx.wear.tiles:tiles:1.1.0")
docs("androidx.wear.tiles:tiles-material:1.1.0")
docs("androidx.wear.tiles:tiles-proto:1.1.0")
docs("androidx.wear.tiles:tiles-renderer:1.1.0")
docs("androidx.wear.tiles:tiles-testing:1.1.0")
- docs("androidx.wear.watchface:watchface:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-client:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-client-guava:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-complications:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-complications-data:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-complications-data-source:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-complications-data-source-ktx:1.2.0-alpha03")
- samples("androidx.wear.watchface:watchface-complications-permission-dialogs-sample:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-complications-rendering:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-data:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-editor:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-editor-guava:1.2.0-alpha03")
- samples("androidx.wear.watchface:watchface-editor-samples:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-guava:1.2.0-alpha03")
- samples("androidx.wear.watchface:watchface-samples:1.2.0-alpha03")
- docs("androidx.wear.watchface:watchface-style:1.2.0-alpha03")
+ docs("androidx.wear.watchface:watchface:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-client:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-client-guava:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-complications:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-complications-data:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-complications-data-source:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-complications-data-source-ktx:1.2.0-alpha04")
+ samples("androidx.wear.watchface:watchface-complications-permission-dialogs-sample:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-complications-rendering:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-data:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-editor:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-editor-guava:1.2.0-alpha04")
+ samples("androidx.wear.watchface:watchface-editor-samples:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-guava:1.2.0-alpha04")
+ samples("androidx.wear.watchface:watchface-samples:1.2.0-alpha04")
+ docs("androidx.wear.watchface:watchface-style:1.2.0-alpha04")
docs("androidx.wear:wear:1.3.0-alpha03")
stubs(fileTree(dir: "../wear/wear_stubs/", include: ["com.google.android.wearable-stubs.jar"]))
docs("androidx.wear:wear-ongoing:1.1.0-alpha01")
@@ -326,19 +331,20 @@
docs("androidx.wear:wear-input:1.2.0-alpha02")
samples("androidx.wear:wear-input-samples:1.2.0-alpha01")
docs("androidx.wear:wear-input-testing:1.2.0-alpha02")
- docs("androidx.webkit:webkit:1.6.0-alpha02")
- docs("androidx.window:window:1.1.0-alpha03")
+ docs("androidx.webkit:webkit:1.6.0-alpha03")
+ docs("androidx.window:window:1.1.0-alpha04")
stubs(fileTree(dir: "../window/stubs/", include: ["window-sidecar-release-0.1.0-alpha01.aar"]))
+ docs("androidx.window:window-core:1.1.0-alpha04")
stubs("androidx.window:window-extensions:1.0.0-alpha01")
- docs("androidx.window:window-java:1.1.0-alpha03")
- docs("androidx.window:window-rxjava2:1.1.0-alpha03")
- docs("androidx.window:window-rxjava3:1.1.0-alpha03")
- docs("androidx.window:window-testing:1.1.0-alpha03")
- docs("androidx.work:work-gcm:2.8.0-beta01")
- docs("androidx.work:work-multiprocess:2.8.0-beta01")
- docs("androidx.work:work-runtime:2.8.0-beta01")
- docs("androidx.work:work-runtime-ktx:2.8.0-beta01")
- docs("androidx.work:work-rxjava2:2.8.0-beta01")
- docs("androidx.work:work-rxjava3:2.8.0-beta01")
- docs("androidx.work:work-testing:2.8.0-beta01")
+ docs("androidx.window:window-java:1.1.0-alpha04")
+ docs("androidx.window:window-rxjava2:1.1.0-alpha04")
+ docs("androidx.window:window-rxjava3:1.1.0-alpha04")
+ docs("androidx.window:window-testing:1.1.0-alpha04")
+ docs("androidx.work:work-gcm:2.8.0-beta02")
+ docs("androidx.work:work-multiprocess:2.8.0-beta02")
+ docs("androidx.work:work-runtime:2.8.0-beta02")
+ docs("androidx.work:work-runtime-ktx:2.8.0-beta02")
+ docs("androidx.work:work-rxjava2:2.8.0-beta02")
+ docs("androidx.work:work-rxjava3:2.8.0-beta02")
+ docs("androidx.work:work-testing:2.8.0-beta02")
}
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiView.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiView.kt
index abd602c..bba8664 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiView.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiView.kt
@@ -26,6 +26,7 @@
import android.text.Spanned
import android.text.StaticLayout
import android.text.TextPaint
+import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import androidx.annotation.RequiresApi
@@ -34,7 +35,9 @@
/**
* A customized view to support drawing emojis asynchronously.
*/
-internal class EmojiView(context: Context) : View(context) {
+internal class EmojiView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+ View(context, attrs) {
+
companion object {
private const val EMOJI_DRAW_TEXT_SIZE_SP = 30
}
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-af/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-af/strings.xml
index 204ee00..ba26a9a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-af/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-af/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"VOORWERPE"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLE"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"VLAE"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Geen emosiekone beskikbaar nie"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-am/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-am/strings.xml
index f2d2255..8bced7b 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-am/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-am/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ነገሮች"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ምልክቶች"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ባንዲራዎች"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
index 06b974b..dbee125 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"الأشياء"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"الرموز"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"الأعلام"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-as/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-as/strings.xml
index 0a486a1..27f9a48 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-as/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-as/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"বস্তু"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"চিহ্ন"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"পতাকা"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-az/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-az/strings.xml
index d40c53c..494a404 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-az/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-az/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBYEKTLƏR"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SİMVOLLAR"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BAYRAQLAR"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-b+sr+Latn/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-b+sr+Latn/strings.xml
index 71b377f..082c070 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-b+sr+Latn/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-b+sr+Latn/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTI"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ZASTAVE"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-be/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-be/strings.xml
index 66979f9..b7f1775 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-be/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-be/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"АБ\'ЕКТЫ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"СІМВАЛЫ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"СЦЯГІ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-bg/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-bg/strings.xml
index b93d21c..5bc64c0 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-bg/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-bg/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ПРЕДМЕТИ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"СИМВОЛИ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ЗНАМЕНА"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-bn/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-bn/strings.xml
index 4e1034c..ce02e6c 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-bn/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-bn/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"অবজেক্ট"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"প্রতীক"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ফ্ল্যাগ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-bs/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-bs/strings.xml
index 7ed5285..f939f3a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-bs/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-bs/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"PREDMETI"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ZASTAVE"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Nije dostupan nijedan emoji"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ca/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ca/strings.xml
index edfa8ac..7b560c7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ca/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ca/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTES"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BANDERES"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-cs/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-cs/strings.xml
index 9aa5ac6..297d62d 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-cs/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-cs/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTY"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLY"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"VLAJKY"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-da/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-da/strings.xml
index 893f68e..4302216 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-da/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-da/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"TING"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLER"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAG"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-de/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-de/strings.xml
index 23bcc14..4765437 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-de/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-de/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTE"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLE"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAGGEN"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-el/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-el/strings.xml
index f5ebd3e..8b49435 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-el/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-el/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ΑΝΤΙΚΕΙΜΕΝΑ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ΣΥΜΒΟΛΑ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ΣΗΜΑΙΕΣ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rAU/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rAU/strings.xml
index 773953d..7b7b1f7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rAU/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rAU/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAGS"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No emojis available"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rCA/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rCA/strings.xml
index 773953d..7b7b1f7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rCA/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rCA/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAGS"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No emojis available"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rGB/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rGB/strings.xml
index 773953d..7b7b1f7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rGB/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rGB/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAGS"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No emojis available"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rIN/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rIN/strings.xml
index 773953d..7b7b1f7 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rIN/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rIN/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAGS"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No emojis available"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-en-rXC/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-en-rXC/strings.xml
index de4d6fb..ef401e8 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-en-rXC/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-en-rXC/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAGS"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No emojis available"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-es-rUS/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-es-rUS/strings.xml
index 3620abe..688cecb 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-es-rUS/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-es-rUS/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BANDERAS"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"No hay ningún emoji disponible"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-es/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-es/strings.xml
index 1f75326..3047306 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-es/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-es/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BANDERAS"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-et/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-et/strings.xml
index 3dca6c7..6d9cfc9 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-et/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-et/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTID"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SÜMBOLID"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"LIPUD"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-eu/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-eu/strings.xml
index ecd828a..86d8285 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-eu/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-eu/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTUAK"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"IKURRAK"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BANDERAK"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-fa/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-fa/strings.xml
index 5d05a16..cc8c523 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-fa/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-fa/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"اشیاء"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"نمادها"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"پرچمها"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-fi/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-fi/strings.xml
index 9a4c1c3..9d90888 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-fi/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-fi/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ESINEET"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLIT"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"LIPUT"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-fr-rCA/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-fr-rCA/strings.xml
index 82ce9a2..1d539cd 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-fr-rCA/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-fr-rCA/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJETS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLES"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"DRAPEAUX"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-fr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-fr/strings.xml
index 34fd6ba..1bc01ec 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-fr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-fr/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJETS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLES"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"DRAPEAUX"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-gl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-gl/strings.xml
index 7cb2985..9a13966 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-gl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-gl/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBXECTOS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BANDEIRAS"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-gu/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-gu/strings.xml
index 50975a9..d0b15fe 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-gu/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-gu/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ઑબ્જેક્ટ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"પ્રતીકો"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ઝંડા"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-hi/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-hi/strings.xml
index 9b732dd..e3a1b22 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-hi/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-hi/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ऑब्जेक्ट"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"सिंबल"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"झंडे"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-hr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-hr/strings.xml
index 0bc0396..a2dcb08 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-hr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-hr/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTI"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ZASTAVE"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Nije dostupan nijedan emoji"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-hu/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-hu/strings.xml
index 27f306f..57fbcd8d 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-hu/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-hu/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"TÁRGYAK"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SZIMBÓLUMOK"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ZÁSZLÓK"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-hy/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-hy/strings.xml
index f88c5f0..7842e75 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-hy/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-hy/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ԱՌԱՐԿԱՆԵՐ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ՆՇԱՆՆԵՐ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ԴՐՈՇՆԵՐ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-in/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-in/strings.xml
index 75fe397..fcb4268 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-in/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-in/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEK"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOL"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BENDERA"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-is/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-is/strings.xml
index 8449afb..86c6d24 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-is/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-is/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"HLUTIR"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"TÁKN"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FÁNAR"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-it/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-it/strings.xml
index 7ae073b..91b2d1e 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-it/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-it/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OGGETTI"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BANDIERE"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-iw/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-iw/strings.xml
index 0f72649..2114632 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-iw/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-iw/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"אובייקטים"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"סמלים"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"דגלים"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ja/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ja/strings.xml
index aa5af40..d4bad63 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ja/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ja/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"アイテム"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"記号"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"旗"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ka/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ka/strings.xml
index 550663f..fc2be1d 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ka/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ka/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ობიექტები"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"სიმბოლოები"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"დროშები"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-kk/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-kk/strings.xml
index c59ebb8..9ea1326 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-kk/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-kk/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"НЫСАНДАР"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ТАҢБАЛАР"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ЖАЛАУШАЛАР"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-km/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-km/strings.xml
index 88bb970..180d981 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-km/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-km/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"វត្ថុ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"និមិត្តសញ្ញា"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ទង់ជាតិ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-kn/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-kn/strings.xml
index c1bc2ae..8967259 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-kn/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-kn/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ವಸ್ತುಗಳು"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ಸಂಕೇತಗಳು"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ಫ್ಲ್ಯಾಗ್ಗಳು"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ko/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ko/strings.xml
index 92a44db..556d96e 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ko/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ko/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"사물"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"기호"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"깃발"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ky/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ky/strings.xml
index c6c3658..bf96ba5 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ky/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ky/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ОБЪЕКТТЕР"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"СИМВОЛДОР"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ЖЕЛЕКТЕР"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-lo/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-lo/strings.xml
index 93f8f6a..58728bf 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-lo/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-lo/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ວັດຖຸ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ສັນຍາລັກ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ທຸງ"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"ບໍ່ມີອີໂມຈິໃຫ້ນຳໃຊ້"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-lt/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-lt/strings.xml
index 69c0624..6e243bf 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-lt/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-lt/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTAI"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLIAI"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"VĖLIAVOS"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-lv/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-lv/strings.xml
index eef9dca..eaae840 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-lv/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-lv/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTI"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"KAROGI"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-mk/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-mk/strings.xml
index 3f844e5..194dbfe 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-mk/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-mk/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ОБЈЕКТИ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"СИМБОЛИ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ЗНАМИЊА"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Нема достапни емоџија"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ml/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ml/strings.xml
index 95f3bb7..6551915 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ml/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ml/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"വസ്തുക്കൾ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ചിഹ്നങ്ങൾ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"പതാകകൾ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-mn/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-mn/strings.xml
index dfbe911..0903d6d 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-mn/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-mn/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ОБЪЕКТ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ТЭМДЭГ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ТУГ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-mr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-mr/strings.xml
index f84380b..133595a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-mr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-mr/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ऑब्जेक्ट"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"चिन्हे"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ध्वज"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ms/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ms/strings.xml
index 4b21953..bbd44a1 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ms/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ms/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEK"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOL"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BENDERA"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Tiada emoji tersedia"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-my/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-my/strings.xml
index 371a93a..22a6327 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-my/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-my/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"အရာဝတ္ထုများ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"သင်္ကေတများ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"အလံများ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-nb/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-nb/strings.xml
index 88e1961..600a1de 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-nb/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-nb/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"GJENSTANDER"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLER"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAGG"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ne/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ne/strings.xml
index d60e4cb..0dfae15 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ne/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ne/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"वस्तुहरू"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"चिन्हहरू"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"झन्डाहरू"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-nl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-nl/strings.xml
index 3a4a9df..b662a97 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-nl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-nl/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJECTEN"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLEN"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"VLAGGEN"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-or/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-or/strings.xml
index c0a2ce0..00cdd48 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-or/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-or/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ଅବଜେକ୍ଟଗୁଡ଼ିକ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ଚିହ୍ନଗୁଡ଼ିକ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ଫ୍ଲାଗଗୁଡ଼ିକ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pa/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pa/strings.xml
index ba141b7..0ee52cb 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pa/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pa/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ਵਸਤੂਆਂ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ਚਿੰਨ੍ਹ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ਝੰਡੇ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pl/strings.xml
index 8720f89..427f6be 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pl/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"PRZEDMIOTY"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLE"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAGI"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pt-rBR/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pt-rBR/strings.xml
index 640b333..b8a0e5f 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pt-rBR/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pt-rBR/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BANDEIRAS"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Não há emojis disponíveis"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pt-rPT/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pt-rPT/strings.xml
index 59b7ead..c6a2bf9 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pt-rPT/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pt-rPT/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BANDEIRAS"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Nenhum emoji disponível"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-pt/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-pt/strings.xml
index 640b333..b8a0e5f 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-pt/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-pt/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJETOS"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SÍMBOLOS"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BANDEIRAS"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Não há emojis disponíveis"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ro/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ro/strings.xml
index 3bbfab6..44e6db0 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ro/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ro/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBIECTE"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLURI"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"STEAGURI"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ru/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ru/strings.xml
index 2dec46a..fec27c9 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ru/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ru/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ОБЪЕКТЫ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"СИМВОЛЫ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ФЛАГИ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-si/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-si/strings.xml
index 9716539..cbd141a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-si/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-si/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"වස්තු"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"සංකේත"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ධජ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml
index 745a0a0..93a8089 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sk/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"PREDMETY"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLY"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"VLAJKY"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Nie sú k dispozícii žiadne emodži"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sl/strings.xml
index 3e9a4bd..5f8cd6a 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sl/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"PREDMETI"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLI"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ZASTAVE"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sq/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sq/strings.xml
index 116b1e4..91bcd77 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sq/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sq/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBJEKTE"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SIMBOLE"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAMUJ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sr/strings.xml
index dac0c3e..f088321 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sr/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ОБЈЕКТИ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"СИМБОЛИ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ЗАСТАВЕ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sv/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sv/strings.xml
index 57e3a2a..69b54ae 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sv/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sv/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"FÖREMÅL"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SYMBOLER"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"FLAGGOR"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-sw/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-sw/strings.xml
index ae733c2..9897f9b 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-sw/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-sw/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"VITU"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"ISHARA"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BENDERA"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ta/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ta/strings.xml
index b250ef6..f1959f6 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ta/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ta/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"பொருட்கள்"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"சின்னங்கள்"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"கொடிகள்"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-te/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-te/strings.xml
index 7af3e30..194ae55 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-te/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-te/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ఆబ్జెక్ట్లు"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"గుర్తులు"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ఫ్లాగ్లు"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-th/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-th/strings.xml
index 4a5f1b7..f6f3782 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-th/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-th/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"วัตถุ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"สัญลักษณ์"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ธง"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"ไม่มีอีโมจิ"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-tl/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-tl/strings.xml
index 41f0d3b..b0b0961 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-tl/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-tl/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"MGA BAGAY"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"MGA SIMBOLO"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"MGA BANDILA"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-tr/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-tr/strings.xml
index 1ed37d1c..a6cddbf 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-tr/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-tr/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"NESNELER"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"SEMBOLLER"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BAYRAKLAR"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-uk/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-uk/strings.xml
index ec58aa6..bb41434 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-uk/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-uk/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ОБ’ЄКТИ"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"СИМВОЛИ"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"ПРАПОРИ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ur/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ur/strings.xml
index d11be3d..5ba39c4 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ur/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ur/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"آبجیکٹس"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"علامات"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"جھنڈے"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-uz/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-uz/strings.xml
index a51d22f..5107f5e 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-uz/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-uz/strings.xml
@@ -26,4 +26,5 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"OBYEKTLAR"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"BELGILAR"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"BAYROQCHALAR"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"Hech qanday emoji mavjud emas"</string>
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-vi/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-vi/strings.xml
index 4a3f34a..4583227 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-vi/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-vi/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"ĐỒ VẬT"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"BIỂU TƯỢNG"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"CỜ"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rCN/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rCN/strings.xml
index d1438f6..509d61e 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rCN/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rCN/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"物体"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"符号"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"旗帜"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
index 32b5850..ff47ce4 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rHK/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"物件"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"符號"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"旗幟"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rTW/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rTW/strings.xml
index afd04e4..135da04 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zh-rTW/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zh-rTW/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"物品"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"符號"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"旗幟"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-zu/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-zu/strings.xml
index fd576f9..332f6e4 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-zu/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-zu/strings.xml
@@ -26,4 +26,6 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"IZINTO"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"AMASIMBULI"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"AMAFULEGI"</string>
+ <!-- no translation found for emoji_empty_non_recent_category (288822832574892625) -->
+ <skip />
</resources>
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt
index b5ce3e7..10cd1d2 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt
@@ -31,7 +31,7 @@
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
-import androidx.fragment.app.test.FragmentTestActivity
+import androidx.fragment.app.test.EmptyFragmentTestActivity
import androidx.fragment.test.R
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -46,13 +46,19 @@
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
+import leakcanary.DetectLeaksAfterTestSuccess
+import org.junit.rules.RuleChain
@MediumTest
@RunWith(AndroidJUnit4::class)
class FragmentContainerViewTest {
@Suppress("DEPRECATION")
+ val activityRule = androidx.test.rule.ActivityTestRule(EmptyFragmentTestActivity::class.java)
+
@get:Rule
- var activityRule = androidx.test.rule.ActivityTestRule(FragmentTestActivity::class.java)
+ val ruleChain: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess())
+ .around(activityRule)
+
lateinit var context: Context
@Before
@@ -421,6 +427,8 @@
.that(drawnFirstCountDownLatch.await(1, TimeUnit.SECONDS))
.isTrue()
assertThat(drawnFirst!!).isEqualTo(frag1View)
+
+ drawnFirst = null
}
// Disappearing child views should be drawn last if transaction is a pop.
@@ -479,6 +487,8 @@
.isTrue()
// The popped Fragment will be drawn last and therefore will be on top
assertThat(drawnFirst!!).isNotEqualTo(frag2View)
+
+ drawnFirst = null
}
@Test
@@ -543,6 +553,8 @@
.that(drawnFirstCountDownLatch.await(1, TimeUnit.SECONDS))
.isTrue()
assertThat(drawnFirst!!).isNotEqualTo(frag2View)
+
+ drawnFirst = null
}
@Test
@@ -610,6 +622,8 @@
.that(drawnFirstCountDownLatch.await(1, TimeUnit.SECONDS))
.isTrue()
assertThat(drawnFirst!!).isNotEqualTo(frag2View)
+
+ drawnFirst = null
}
@Test
@@ -678,6 +692,8 @@
.isTrue()
// The view that was popped is drawn first which means it is on the bottom.
assertThat(drawnFirst!!).isEqualTo(frag2View)
+
+ drawnFirst = null
}
@Test
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReceiveResultTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReceiveResultTest.kt
index df4978c..b897a54 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReceiveResultTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReceiveResultTest.kt
@@ -30,12 +30,13 @@
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Assert.fail
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
+import leakcanary.DetectLeaksAfterTestSuccess
+import org.junit.rules.RuleChain
/**
* Tests for Fragment startActivityForResult and startIntentSenderForResult.
@@ -44,22 +45,18 @@
@MediumTest
class FragmentReceiveResultTest {
@Suppress("DEPRECATION")
- @get:Rule
val activityRule = androidx.test.rule.ActivityTestRule(FragmentTestActivity::class.java)
- private lateinit var activity: FragmentTestActivity
- private lateinit var fragment: TestFragment
-
- @Before
- fun setup() {
- activity = activityRule.activity
- fragment = attachTestFragment()
- }
+ // Detect leaks BEFORE and AFTER activity is destroyed
+ @get:Rule
+ val ruleChain: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess())
+ .around(activityRule)
@Suppress("DEPRECATION")
@Test
@UiThreadTest
fun testNoFragmentOnActivityResult() {
+ val activity = activityRule.activity
activity.supportFragmentManager.saveAllStateInternal()
// 0xffff is the request code for the startActivityResult launcher in FragmentManager
@@ -70,12 +67,17 @@
@Test
fun testNoFragmentOnRequestPermissionsResult() {
// 0xffff + 2 is the request code for the requestPermissions launcher in FragmentManager
- activity.onRequestPermissionsResult(0xffff + 2, arrayOf("permission"), intArrayOf(1))
+ activityRule.activity.onRequestPermissionsResult(
+ 0xffff + 2,
+ arrayOf("permission"),
+ intArrayOf(1)
+ )
}
@Test
fun testStartActivityForResultOk() {
- startActivityForResult(10, Activity.RESULT_OK, "content 10")
+ val fragment: TestFragment = attachTestFragment()
+ startActivityForResult(fragment, 10, Activity.RESULT_OK, "content 10")
assertWithMessage("Fragment should receive result").that(fragment.hasResult[0]).isTrue()
assertThat(fragment.requestCode[0]).isEqualTo(10)
@@ -85,8 +87,9 @@
@Test
fun testMultipleStartActivityForResultOk() {
- startActivityForResult(10, Activity.RESULT_OK, "content 10")
- startActivityForResult(20, Activity.RESULT_OK, "content 20")
+ val fragment: TestFragment = attachTestFragment()
+ startActivityForResult(fragment, 10, Activity.RESULT_OK, "content 10")
+ startActivityForResult(fragment, 20, Activity.RESULT_OK, "content 20")
assertWithMessage("Fragment should receive result").that(fragment.hasResult[0]).isTrue()
assertThat(fragment.requestCode[0]).isEqualTo(10)
@@ -101,7 +104,8 @@
@Test
fun testStartActivityForResultCanceled() {
- startActivityForResult(20, Activity.RESULT_CANCELED, "content 20")
+ val fragment: TestFragment = attachTestFragment()
+ startActivityForResult(fragment, 20, Activity.RESULT_CANCELED, "content 20")
assertWithMessage("Fragment should receive result").that(fragment.hasResult[0]).isTrue()
assertThat(fragment.requestCode[0]).isEqualTo(20)
@@ -111,7 +115,8 @@
@Test
fun testStartIntentSenderForResultOk() {
- startIntentSenderForResult(30, Activity.RESULT_OK, "content 30")
+ val fragment: TestFragment = attachTestFragment()
+ startIntentSenderForResult(fragment, 30, Activity.RESULT_OK, "content 30")
assertWithMessage("Fragment should receive result").that(fragment.hasResult[0]).isTrue()
assertThat(fragment.requestCode[0]).isEqualTo(30)
@@ -121,7 +126,8 @@
@Test
fun testStartIntentSenderForResultWithOptionsOk() {
- startIntentSenderForResult(30, Activity.RESULT_OK, "content 30", Bundle())
+ val fragment: TestFragment = attachTestFragment()
+ startIntentSenderForResult(fragment, 30, Activity.RESULT_OK, "content 30", Bundle())
assertWithMessage("Fragment should receive result").that(fragment.hasResult[0]).isTrue()
assertThat(fragment.requestCode[0]).isEqualTo(30)
@@ -131,7 +137,8 @@
@Test
fun testStartIntentSenderForResultCanceled() {
- startIntentSenderForResult(40, Activity.RESULT_CANCELED, "content 40")
+ val fragment: TestFragment = attachTestFragment()
+ startIntentSenderForResult(fragment, 40, Activity.RESULT_CANCELED, "content 40")
assertWithMessage("Fragment should receive result").that(fragment.hasResult[0]).isTrue()
assertThat(fragment.requestCode[0]).isEqualTo(40)
@@ -140,6 +147,7 @@
}
private fun attachTestFragment(): TestFragment {
+ val activity = activityRule.activity
val fragment = TestFragment()
activityRule.runOnUiThread {
activity.supportFragmentManager.beginTransaction()
@@ -154,10 +162,12 @@
@Suppress("DEPRECATION")
private fun startActivityForResult(
+ fragment: TestFragment,
requestCode: Int,
resultCode: Int,
content: String
) {
+ val activity = activityRule.activity
activityRule.runOnUiThread {
val intent = Intent(activity, FragmentResultActivity::class.java)
intent.putExtra(FragmentResultActivity.EXTRA_RESULT_CODE, resultCode)
@@ -174,11 +184,13 @@
@Suppress("DEPRECATION")
private fun startIntentSenderForResult(
+ fragment: TestFragment,
requestCode: Int,
resultCode: Int,
content: String,
options: Bundle? = null
) {
+ val activity = activityRule.activity
activityRule.runOnUiThread {
val intent = Intent(activity, FragmentResultActivity::class.java)
intent.putExtra(FragmentResultActivity.EXTRA_RESULT_CODE, resultCode)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReorderingTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReorderingTest.kt
index fde5c4d..57ffc27 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReorderingTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentReorderingTest.kt
@@ -15,7 +15,6 @@
*/
package androidx.fragment.app
-import android.app.Instrumentation
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
@@ -28,9 +27,11 @@
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import leakcanary.DetectLeaksAfterTestSuccess
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.junit.rules.RuleChain
import org.junit.runner.RunWith
@SmallTest
@@ -38,25 +39,24 @@
class FragmentReorderingTest() {
@Suppress("DEPRECATION")
- @get:Rule
var activityRule = androidx.test.rule.ActivityTestRule(FragmentTestActivity::class.java)
- private lateinit var container: ViewGroup
- private lateinit var fm: FragmentManager
- private lateinit var instrumentation: Instrumentation
+ // Detect leaks BEFORE and AFTER activity is destroyed
+ @get:Rule
+ val ruleChain: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess())
+ .around(activityRule)
@Before
fun setup() {
activityRule.setContentView(R.layout.simple_container)
- container = activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
- fm = activityRule.activity.supportFragmentManager
- instrumentation = InstrumentationRegistry.getInstrumentation()
}
// Ensure that a replaced fragment is stopped before its replacement is started
// and vice versa when popped
@Test
fun stopBeforeStart() {
+ val fm = activityRule.activity.supportFragmentManager
+
val fragment1 = StrictViewFragment()
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
@@ -67,7 +67,7 @@
val fragment2 = StrictViewFragment()
lateinit var replaceStateWhenStopped: Lifecycle.State
lateinit var replaceStateWhenPopStarted: Lifecycle.State
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fragment1.lifecycle.addObserver(
LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_STOP) {
@@ -104,6 +104,11 @@
// actually creates a View.
@Test
fun addReplace() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+
val fragment1 = CountCallsFragment()
val fragment2 = StrictViewFragment()
instrumentation.runOnMainSync {
@@ -134,6 +139,10 @@
// the same view back again.
@Test
fun startWithPop() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
+
// Start with a single fragment on the back stack
val fragment1 = CountCallsFragment()
fm.beginTransaction()
@@ -146,7 +155,7 @@
assertChildren(container, fragment1)
// Now pop and add
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.popBackStack()
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
@@ -166,9 +175,12 @@
// Popping the back stack in the middle of other operations doesn't fool it.
@Test
fun middlePop() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
val fragment2 = CountCallsFragment()
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
.addToBackStack(null)
@@ -195,9 +207,12 @@
// View being created. Hide still gets notified.
@Test
fun removeRedundantRemove() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
var id = -1
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
id = fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
.addToBackStack(null)
@@ -234,6 +249,9 @@
// Ensure that removing and adding the same view results in no operation
@Test
fun removeRedundantAdd() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
val id = fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
@@ -243,7 +261,7 @@
activityRule.executePendingTransactions()
assertThat(fragment1.onCreateViewCount).isEqualTo(1)
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.beginTransaction()
.remove(fragment1)
.addToBackStack(null)
@@ -271,6 +289,9 @@
// detaching, then attaching results in on change. Hide still functions
@Test
fun removeRedundantAttach() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
val id = fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
@@ -281,7 +302,7 @@
assertThat(fragment1.onAttachCount).isEqualTo(1)
assertChildren(container, fragment1)
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.beginTransaction()
.detach(fragment1)
.addToBackStack(null)
@@ -325,6 +346,9 @@
// attaching, then detaching shouldn't result in a View being created
@Test
fun removeRedundantDetach() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
val id = fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
@@ -341,7 +365,7 @@
assertThat(fragment1.onCreateViewCount).isEqualTo(0)
assertChildren(container)
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.beginTransaction()
.attach(fragment1)
.addToBackStack(null)
@@ -383,6 +407,10 @@
// show, then hide should optimize out
@Test
fun removeRedundantHide() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
val fragment1 = CountCallsFragment()
val id = fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
@@ -494,6 +522,9 @@
// hiding and showing the same view should optimize out
@Test
fun removeRedundantShow() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
val id = fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
@@ -505,7 +536,7 @@
assertThat(fragment1.onHideCount).isEqualTo(0)
assertChildren(container, fragment1)
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.beginTransaction()
.hide(fragment1)
.addToBackStack(null)
@@ -544,6 +575,9 @@
// the transaction completes.
@Test
fun viewOrder() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
val id = fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
@@ -555,7 +589,7 @@
val fragment2 = CountCallsFragment()
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.beginTransaction()
.replace(R.id.fragmentContainer, fragment2)
.addToBackStack(null)
@@ -578,8 +612,11 @@
// Popping an added transaction results in no operation
@Test
fun addPopBackStack() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
.addToBackStack(null)
@@ -598,9 +635,12 @@
// optimization.
@Test
fun popNonBackStack() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
val fragment2 = CountCallsFragment()
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
.addToBackStack(null)
@@ -623,9 +663,12 @@
// transaction should all be run prior to running the ordered transaction.
@Test
fun noReordering() {
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
val fragment1 = CountCallsFragment()
val fragment2 = CountCallsFragment()
- instrumentation.runOnMainSync {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment1)
.addToBackStack(null)
@@ -649,7 +692,9 @@
@Test
fun focusedView() {
activityRule.setContentView(R.layout.double_container)
- container = activityRule.activity.findViewById<View>(R.id.fragmentContainer1) as ViewGroup
+ val fm = activityRule.activity.supportFragmentManager
+ val container =
+ activityRule.activity.findViewById<View>(R.id.fragmentContainer1) as ViewGroup
lateinit var firstEditText: EditText
activityRule.runOnUiThread {
firstEditText = EditText(container.context)
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
index 5ab086a..6ed9681 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
@@ -53,11 +53,256 @@
import java.lang.annotation.RetentionPolicy;
/**
- * Static library support version of the framework's {@link android.app.DialogFragment}.
- * Used to write apps that run on platforms prior to Android 3.0. When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation. See the framework SDK
- * documentation for a class overview.
+ * A fragment that displays a dialog window, floating in the foreground of its
+ * activity's window. This fragment contains a Dialog object, which it
+ * displays as appropriate based on the fragment's state. Control of
+ * the dialog (deciding when to show, hide, dismiss it) should be done through
+ * the APIs here, not with direct calls on the dialog.
+ *
+ * <p>Implementations should override this class and implement
+ * {@link #onViewCreated(View, Bundle)} to supply the
+ * content of the dialog. Alternatively, they can override
+ * {@link #onCreateDialog(Bundle)} to create an entirely custom dialog, such
+ * as an AlertDialog, with its own content.
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#Lifecycle">Lifecycle</a>
+ * <li><a href="#BasicDialog">Basic Dialog</a>
+ * <li><a href="#AlertDialog">Alert Dialog</a>
+ * <li><a href="#DialogOrEmbed">Selecting Between Dialog or Embedding</a>
+ * </ol>
+ *
+ * <a name="Lifecycle"></a>
+ * <h3>Lifecycle</h3>
+ *
+ * <p>DialogFragment does various things to keep the fragment's lifecycle
+ * driving it, instead of the Dialog. Note that dialogs are generally
+ * autonomous entities -- they are their own window, receiving their own
+ * input events, and often deciding on their own when to disappear (by
+ * receiving a back key event or the user clicking on a button).
+ *
+ * <p>DialogFragment needs to ensure that what is happening with the Fragment
+ * and Dialog states remains consistent. To do this, it watches for dismiss
+ * events from the dialog and takes care of removing its own state when they
+ * happen. This means you should use {@link #show(FragmentManager, String)},
+ * {@link #show(FragmentTransaction, String)}, or {@link #showNow(FragmentManager, String)}
+ * to add an instance of DialogFragment to your UI, as these keep track of
+ * how DialogFragment should remove itself when the dialog is dismissed.
+ *
+ * <a name="BasicDialog"></a>
+ * <h3>Basic Dialog</h3>
+ *
+ * <p>The simplest use of DialogFragment is as a floating container for the
+ * fragment's view hierarchy. A simple implementation may look like this:
+ *
+ * <pre>{@code
+ * public class MyDialogFragment extends DialogFragment {
+ * int mNum;
+ *
+ * // Create a new instance of MyDialogFragment, providing "num" as an argument.
+ * static MyDialogFragment newInstance(int num) {
+ * MyDialogFragment f = new MyDialogFragment();
+ *
+ * // Supply num input as an argument.
+ * Bundle args = new Bundle();
+ * args.putInt("num", num);
+ * f.setArguments(args);
+ *
+ * return f;
+ * }
+ *
+ * {@literal @}Override
+ * public void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ * mNum = getArguments().getInt("num");
+ *
+ * // Pick a style based on the num.
+ * int style = DialogFragment.STYLE_NORMAL, theme = 0;
+ * switch ((mNum-1)%6) {
+ * case 1: style = DialogFragment.STYLE_NO_TITLE; break;
+ * case 2: style = DialogFragment.STYLE_NO_FRAME; break;
+ * case 3: style = DialogFragment.STYLE_NO_INPUT; break;
+ * case 4: style = DialogFragment.STYLE_NORMAL; break;
+ * case 5: style = DialogFragment.STYLE_NORMAL; break;
+ * case 6: style = DialogFragment.STYLE_NO_TITLE; break;
+ * case 7: style = DialogFragment.STYLE_NO_FRAME; break;
+ * case 8: style = DialogFragment.STYLE_NORMAL; break;
+ * }
+ * switch ((mNum-1)%6) {
+ * case 4: theme = android.R.style.Theme_Holo; break;
+ * case 5: theme = android.R.style.Theme_Holo_Light_Dialog; break;
+ * case 6: theme = android.R.style.Theme_Holo_Light; break;
+ * case 7: theme = android.R.style.Theme_Holo_Light_Panel; break;
+ * case 8: theme = android.R.style.Theme_Holo_Light; break;
+ * }
+ * setStyle(style, theme);
+ * }
+ *
+ * {@literal @}Override
+ * public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ * Bundle savedInstanceState) {
+ * return inflater.inflate(R.layout.fragment_dialog, container, false);
+ * }
+ *
+ * {@literal @}Override
+ * public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ * super.onViewCreated(view, savedInstanceState);
+ *
+ * // set DialogFragment title
+ * getDialog().setTitle("Dialog #" + mNum);
+ * }
+ * }
+ * }</pre>
+ *
+ * <p>An example showDialog() method on the Activity could be:
+ *
+ * <pre>{@code
+ * public void showDialog() {
+ * mStackLevel++;
+ *
+ * // DialogFragment.show() will take care of adding the fragment
+ * // in a transaction. We also want to remove any currently showing
+ * // dialog, so make our own transaction and take care of that here.
+ * FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ * Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
+ * if (prev != null) {
+ * ft.remove(prev);
+ * }
+ * ft.addToBackStack(null);
+ *
+ * // Create and show the dialog.
+ * DialogFragment newFragment = MyDialogFragment.newInstance(mStackLevel);
+ * newFragment.show(ft, "dialog");
+ * }
+ * }</pre>
+ *
+ * <p>This removes any currently shown dialog, creates a new DialogFragment
+ * with an argument, and shows it as a new state on the back stack. When the
+ * transaction is popped, the current DialogFragment and its Dialog will be
+ * destroyed, and the previous one (if any) re-shown. Note that in this case
+ * DialogFragment will take care of popping the transaction of the Dialog that
+ * is dismissed separately from it.
+ *
+ * <a name="AlertDialog"></a>
+ * <h3>Alert Dialog</h3>
+ *
+ * <p>Instead of (or in addition to) implementing {@link #onViewCreated(View, Bundle)} to
+ * generate the view hierarchy inside of a dialog, you may implement
+ * {@link #onCreateDialog(Bundle)} to create your own custom Dialog object.
+ *
+ * <p>This is most useful for creating an AlertDialog, allowing you
+ * to display standard alerts to the user that are managed by a fragment.
+ * A simple example implementation of this is:
+ *
+ * <pre>{@code
+ * public static class MyAlertDialogFragment extends DialogFragment {
+ *
+ * public static MyAlertDialogFragment newInstance(int title) {
+ * MyAlertDialogFragment frag = new MyAlertDialogFragment();
+ * Bundle args = new Bundle();
+ * args.putInt("title", title);
+ * frag.setArguments(args);
+ * return frag;
+ * }
+ *
+ * {@literal @}Override
+ * public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ *
+ * return new AlertDialog.Builder(getActivity())
+ * .setIcon(R.drawable.alert_dialog_icon)
+ * .setTitle(title)
+ * .setPositiveButton(R.string.alert_dialog_ok,
+ * (dialogInterface, i) -> ((MainActivity)getActivity()).doPositiveClick())
+ * .setNegativeButton(R.string.alert_dialog_cancel,
+ * (dialogInterface, i) -> ((MainActivity)getActivity()).doNegativeClick())
+ * .create();
+ * return super.onCreateDialog(savedInstanceState);
+ * }
+ * }
+ * }</pre>
+ *
+ * <p>The activity creating this fragment may have the following methods to
+ * show the dialog and receive results from it:
+ *
+ * <pre>{@code
+ * void showDialog() {
+ * DialogFragment newFragment = MyAlertDialogFragment.newInstance(
+ * R.string.alert_dialog_two_buttons_title);
+ * newFragment.show(getSupportFragmentManager(), "dialog");
+ * }
+ *
+ * public void doPositiveClick() {
+ * // Do stuff here.
+ * Log.i("MainActivity", "Positive click!");
+ * }
+ *
+ * public void doNegativeClick() {
+ * // Do stuff here.
+ * Log.i("MainActivity", "Negative click!");
+ * }
+ * }</pre>
+ *
+ * <p>Note that in this case the fragment is not placed on the back stack, it
+ * is just added as an indefinitely running fragment. Because dialogs normally
+ * are modal, this will still operate as a back stack, since the dialog will
+ * capture user input until it is dismissed. When it is dismissed, DialogFragment
+ * will take care of removing itself from its fragment manager.
+ *
+ * <a name="DialogOrEmbed"></a>
+ * <h3>Selecting Between Dialog or Embedding</h3>
+ *
+ * <p>A DialogFragment can still optionally be used as a normal fragment, if
+ * desired. This is useful if you have a fragment that in some cases should
+ * be shown as a dialog and others embedded in a larger UI. This behavior
+ * will normally be automatically selected for you based on how you are using
+ * the fragment, but can be customized with {@link #setShowsDialog(boolean)}.
+ *
+ * <p>For example, here is a simple dialog fragment:
+ *
+ * <pre>{@code
+ * public static class MyDialogFragment extends DialogFragment {
+ * static MyDialogFragment newInstance() {
+ * return new MyDialogFragment();
+ * }
+ *
+ * {@literal @}Override
+ * public void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ *
+ * // this fragment will be displayed in a dialog
+ * setShowsDialog(true);
+ * }
+ *
+ * {@literal @}Override
+ * public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ * Bundle savedInstanceState) {
+ * View v = inflater.inflate(R.layout.hello_world, container, false);
+ * View tv = v.findViewById(R.id.text);
+ * ((TextView)tv).setText("This is an instance of MyDialogFragment");
+ * return v;
+ * }
+ * }
+ * }</pre>
+ *
+ * <p>An instance of this fragment can be created and shown as a dialog:
+ *
+ * <pre>{@code
+ * void showDialog() {
+ * // Create the fragment and show it as a dialog.
+ * DialogFragment newFragment = MyDialogFragment.newInstance();
+ * newFragment.show(getSupportFragmentManager(), "dialog");
+ * }
+ * }</pre>
+ *
+ * <p>It can also be added as content in a view hierarchy:
+ *
+ * <pre>{@code
+ * FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ * DialogFragment newFragment = MyDialogFragment.newInstance();
+ * ft.add(R.id.embedded, newFragment);
+ * ft.commit();
+ * }</pre>
*/
public class DialogFragment extends Fragment
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/build.gradle b/glance/glance-appwidget/integration-tests/macrobenchmark-target/build.gradle
new file mode 100644
index 0000000..c4be7d4
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/build.gradle
@@ -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.
+ */
+
+
+plugins {
+ id("AndroidXPlugin")
+ id("AndroidXComposePlugin")
+ id("com.android.application")
+ id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+ implementation(libs.kotlinStdlib)
+ implementation(project(":glance:glance"))
+ implementation(project(":glance:glance-appwidget"))
+}
+
+android {
+ namespace "androidx.glance.appwidget.macrobenchmark.target"
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"),
+ 'proguard-rules.pro'
+ }
+ }
+
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/proguard-rules.pro b/glance/glance-appwidget/integration-tests/macrobenchmark-target/proguard-rules.pro
new file mode 100644
index 0000000..0674e77
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/proguard-rules.pro
@@ -0,0 +1 @@
+-dontobfuscate
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2791119
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?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"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <application
+ android:allowBackup="false"
+ android:label="glance-appwidget macrobenchmark target"
+ android:supportsRtl="true">
+ <!-- Profileable to enable macrobenchmark profiling -->
+ <profileable android:shell="true"/>
+
+ <receiver
+ android:name="androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetReceiver"
+ android:label="BasicAppWidget Receiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ <action android:name="androidx.glance.appwidget.action.DEBUG_UPDATE" />
+ <action android:name="android.intent.action.LOCALE_CHANGED" />
+ </intent-filter>
+ <meta-data
+ android:name="android.appwidget.provider"
+ android:resource="@xml/default_app_widget_info" />
+ </receiver>
+ <receiver
+ android:name="androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetWithSessionReceiver"
+ android:label="BasicAppWidget Receiver with sessions enabled"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ <action android:name="androidx.glance.appwidget.action.DEBUG_UPDATE" />
+ <action android:name="android.intent.action.LOCALE_CHANGED" />
+ </intent-filter>
+ <meta-data
+ android:name="android.appwidget.provider"
+ android:resource="@xml/default_app_widget_info" />
+ </receiver>
+ </application>
+</manifest>
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/java/androidx/glance/appwidget/macrobenchmark/target/BasicAppWidget.kt b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/java/androidx/glance/appwidget/macrobenchmark/target/BasicAppWidget.kt
new file mode 100644
index 0000000..b12aaca
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/java/androidx/glance/appwidget/macrobenchmark/target/BasicAppWidget.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.glance.appwidget.macrobenchmark.target
+
+import androidx.compose.runtime.Composable
+import androidx.datastore.preferences.core.Preferences
+import androidx.glance.GlanceModifier
+import androidx.glance.LocalSize
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.glance.appwidget.SizeMode
+import androidx.glance.appwidget.Tracing
+import androidx.glance.currentState
+import androidx.glance.layout.Column
+import androidx.glance.layout.fillMaxSize
+import androidx.glance.session.GlanceSessionManager
+import androidx.glance.text.Text
+import kotlin.math.roundToInt
+
+open class BasicAppWidget : GlanceAppWidget() {
+ init {
+ // Ensure tracing is enabled before starting updates.
+ Tracing.enabled.set(true)
+ }
+ override val sizeMode: SizeMode = SizeMode.Single
+
+ @Composable
+ override fun Content() {
+ val size = LocalSize.current
+ // Even though this widget does not use it, accessing state will ensure that this
+ // is recomposed every time state updates, which is useful for testing.
+ currentState<Preferences>()
+ Column(
+ modifier = GlanceModifier.fillMaxSize(),
+ ) {
+ Text(
+ " Current size: ${size.width.value.roundToInt()} dp x " +
+ "${size.height.value.roundToInt()} dp"
+ )
+ }
+ }
+}
+
+class BasicAppWidgetWithSession : BasicAppWidget() {
+ override val sessionManager = GlanceSessionManager
+}
+
+class BasicAppWidgetReceiver : GlanceAppWidgetReceiver() {
+ override val glanceAppWidget: GlanceAppWidget = BasicAppWidget()
+}
+
+class BasicAppWidgetWithSessionReceiver : GlanceAppWidgetReceiver() {
+ override val glanceAppWidget: GlanceAppWidget = BasicAppWidgetWithSession()
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/layout/glance_default_loading_layout.xml b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/layout/glance_default_loading_layout.xml
new file mode 100644
index 0000000..509cb21
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/layout/glance_default_loading_layout.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/xml/default_app_widget_info.xml b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/xml/default_app_widget_info.xml
new file mode 100644
index 0000000..35afa69
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark-target/src/main/res/xml/default_app_widget_info.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="100dp"
+ android:minHeight="100dp"
+ android:initialLayout="@layout/glance_default_loading_layout"
+ android:minResizeHeight="40dp"
+ android:minResizeWidth="40dp"
+ android:resizeMode="horizontal|vertical"
+ android:widgetCategory="home_screen" />
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle b/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle
new file mode 100644
index 0000000..8bc801c
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+android {
+ namespace "androidx.glance.appwidget.macrobenchmark"
+}
+
+android.defaultConfig {
+ minSdkVersion 23
+}
+
+dependencies {
+ implementation 'androidx.compose.ui:ui-unit:1.2.1'
+ androidTestImplementation('androidx.benchmark:benchmark-junit4:1.1.0')
+ androidTestImplementation('androidx.benchmark:benchmark-macro-junit4:1.1.0')
+ androidTestImplementation('androidx.core:core-ktx:1.7.0')
+ androidTestImplementation(project(":glance:glance"))
+ androidTestImplementation(project(":glance:glance-appwidget"))
+ androidTestImplementation(project(":internal-testutils-macrobenchmark"))
+ androidTestImplementation(libs.kotlinTest)
+ androidTestImplementation(libs.testRules)
+ androidTestImplementation(libs.testExtJunit)
+ androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testRunner)
+ androidTestImplementation(libs.testUiautomator)
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..05a1d72
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-permission android:name="android.permission.BIND_APPWIDGET" />
+
+ <application
+ android:supportsRtl="true">
+ <uses-library android:name="android.test.runner" />
+ <activity
+ android:name="androidx.glance.appwidget.macrobenchmark.AppWidgetHostTestActivity"
+ android:configChanges="orientation|screenLayout|screenSize"
+ android:exported="true"/>
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt
new file mode 100644
index 0000000..93939ba
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.glance.appwidget.macrobenchmark
+
+import android.Manifest
+import android.appwidget.AppWidgetManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.Trace
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+@SdkSuppress(minSdkVersion = 29)
+class AppWidgetHostRule(
+ private var mPortraitSize: DpSize = DpSize(200.dp, 300.dp),
+ private var mLandscapeSize: DpSize = DpSize(300.dp, 200.dp),
+ useSession: Boolean = false,
+) : TestRule {
+ private val mInstrumentation = InstrumentationRegistry.getInstrumentation()
+ private val mUiAutomation = mInstrumentation.uiAutomation
+ private val targetComponent =
+ ComponentName(
+ "androidx.glance.appwidget.macrobenchmark.target",
+ if (useSession) {
+ "androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetWithSessionReceiver"
+ } else {
+ "androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetReceiver"
+ }
+ )
+
+ private val mActivityRule: ActivityScenarioRule<AppWidgetHostTestActivity> =
+ ActivityScenarioRule(
+ Intent()
+ .setComponent(
+ ComponentName(
+ ApplicationProvider.getApplicationContext(),
+ AppWidgetHostTestActivity::class.java,
+ )
+ )
+ .putExtra(
+ AppWidgetHostTestActivity.EXTRA_TARGET_RECEIVER,
+ targetComponent
+ )
+ )
+
+ private val mUiDevice = UiDevice.getInstance(mInstrumentation)
+
+ // Ensure the screen starts in portrait and restore the orientation on leaving
+ private val mOrientationRule = TestRule { base, _ ->
+ object : Statement() {
+ override fun evaluate() {
+ mUiDevice.freezeRotation()
+ mUiDevice.setOrientationNatural()
+ base.evaluate()
+ mUiDevice.unfreezeRotation()
+ }
+ }
+ }
+
+ private val mInnerRules = RuleChain.outerRule(mActivityRule).around(mOrientationRule)
+
+ private var mHostStarted = false
+ private var mMaybeHostView: TestAppWidgetHostView? = null
+ private var mAppWidgetId = 0
+ private val mContext = ApplicationProvider.getApplicationContext<Context>()
+
+ override fun apply(base: Statement, description: Description) = object : Statement() {
+ override fun evaluate() {
+ mInnerRules.apply(base, description).evaluate()
+ if (mHostStarted) {
+ mUiAutomation.dropShellPermissionIdentity()
+ }
+ }
+ }
+
+ /**
+ * Start the host and bind the app widget.
+ * Measures time from binding an app widget to receiving the first RemoteViews.
+ */
+ fun startHost() {
+ mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.BIND_APPWIDGET)
+ mHostStarted = true
+
+ Trace.beginSection("appWidgetInitialUpdate")
+ mActivityRule.scenario.onActivity { activity ->
+ mMaybeHostView = activity.bindAppWidget(mPortraitSize, mLandscapeSize)
+ }
+
+ val hostView = checkNotNull(mMaybeHostView) { "Host view wasn't successfully started" }
+
+ mAppWidgetId = hostView.appWidgetId
+ hostView.waitForRemoteViews()
+ Trace.endSection()
+ }
+
+ /**
+ * Measures time from sending APPWIDGET_UPDATE broadcast to receiving RemoteViews.
+ */
+ fun updateAppWidget() {
+ val intent = Intent(GlanceAppWidgetReceiver.ACTION_DEBUG_UPDATE)
+ .setPackage("androidx.glance.appwidget.macrobenchmark.target")
+ .setComponent(
+ ComponentName(
+ "androidx.glance.appwidget.macrobenchmark.target",
+ "androidx.glance.appwidget.macrobenchmark.target.BasicAppWidgetReceiver"
+ )
+ )
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(mAppWidgetId))
+ val hostView = checkNotNull(mMaybeHostView) { "Host view not started" }
+ Trace.beginSection("appWidgetUpdate")
+ hostView.resetRemoteViewsLatch()
+ mContext.sendBroadcast(intent)
+ hostView.waitForRemoteViews()
+ Trace.endSection()
+ }
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt
new file mode 100644
index 0000000..41098fc
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt
@@ -0,0 +1,234 @@
+/*
+ * 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.glance.appwidget.macrobenchmark
+
+import android.app.Activity
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetHostView
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.os.LocaleList
+import android.util.DisplayMetrics
+import android.util.Log
+import android.util.SizeF
+import android.util.TypedValue
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.RemoteViews
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.max
+import androidx.compose.ui.unit.min
+import java.util.Locale
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.Assert.fail
+
+@RequiresApi(26)
+class AppWidgetHostTestActivity : Activity() {
+ private var mHost: AppWidgetHost? = null
+ private val mHostViews = mutableListOf<TestAppWidgetHostView>()
+
+ companion object {
+ const val EXTRA_TARGET_RECEIVER = "targetReceiver"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ setContentView(android.R.layout.list_content)
+
+ mHost = TestAppWidgetHost(this, 1025).also {
+ it.appWidgetIds.forEach(it::deleteAppWidgetId)
+ it.startListening()
+ }
+ }
+
+ override fun onDestroy() {
+ try {
+ mHost?.stopListening()
+ mHost?.deleteHost()
+ } catch (ex: Throwable) {
+ Log.w("AppWidgetHostTestActivity", "Error stopping the AppWidget Host", ex)
+ }
+ mHost = null
+ super.onDestroy()
+ }
+
+ private fun rootView() =
+ this.findViewById<ViewGroup>(android.R.id.content).getChildAt(0) as ViewGroup
+
+ fun bindAppWidget(portraitSize: DpSize, landscapeSize: DpSize): TestAppWidgetHostView? {
+ val host = mHost ?: error("App widgets can only be bound while the activity is created")
+
+ val appWidgetManager = AppWidgetManager.getInstance(this)
+ val appWidgetId = host.allocateAppWidgetId()
+
+ @Suppress("DEPRECATION")
+ val componentName = intent.getParcelableExtra<ComponentName>(EXTRA_TARGET_RECEIVER)!!
+
+ val wasBound = appWidgetManager.bindAppWidgetIdIfAllowed(
+ appWidgetId,
+ componentName,
+ optionsBundleOf(listOf(portraitSize, landscapeSize))
+ )
+ if (!wasBound) {
+ fail("Failed to bind the app widget")
+ mHost?.deleteHost()
+ mHost = null
+ return null
+ }
+
+ val info = appWidgetManager.getAppWidgetInfo(appWidgetId)
+ val locale = Locale.getDefault()
+ val config = resources.configuration
+ config.setLocales(LocaleList(locale))
+ config.setLayoutDirection(locale)
+ val context = this.createConfigurationContext(config)
+
+ val hostView = host.createView(context, appWidgetId, info) as TestAppWidgetHostView
+ hostView.setPadding(0, 0, 0, 0)
+ rootView().addView(hostView)
+ hostView.setSizes(portraitSize, landscapeSize)
+ hostView.setBackgroundColor(Color.WHITE)
+ mHostViews += hostView
+ return hostView
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ updateAllSizes(newConfig.orientation)
+ reapplyRemoteViews()
+ }
+
+ fun updateAllSizes(orientation: Int) {
+ mHostViews.forEach { it.updateSize(orientation) }
+ }
+
+ fun reapplyRemoteViews() {
+ mHostViews.forEach { it.reapplyRemoteViews() }
+ }
+}
+
+@RequiresApi(26)
+class TestAppWidgetHost(context: Context, hostId: Int) : AppWidgetHost(context, hostId) {
+ override fun onCreateView(
+ context: Context,
+ appWidgetId: Int,
+ appWidget: AppWidgetProviderInfo?
+ ): AppWidgetHostView = TestAppWidgetHostView(context)
+}
+
+@RequiresApi(26)
+class TestAppWidgetHostView(context: Context) : AppWidgetHostView(context) {
+
+ init {
+ // Prevent asynchronous inflation of the App Widget
+ setExecutor(null)
+ layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+ }
+
+ private var mLatch: CountDownLatch? = null
+ private var mRemoteViews: RemoteViews? = null
+ private var mPortraitSize: DpSize = DpSize(0.dp, 0.dp)
+ private var mLandscapeSize: DpSize = DpSize(0.dp, 0.dp)
+
+ /**
+ * Wait for the new remote views to be received. If a remote views was already received, return
+ * immediately.
+ */
+ fun waitForRemoteViews() {
+ synchronized(this) {
+ mRemoteViews?.let { return }
+ mLatch = CountDownLatch(1)
+ }
+ val result = mLatch?.await(5, TimeUnit.SECONDS)!!
+ require(result) { "Timeout before getting RemoteViews" }
+ }
+
+ override fun updateAppWidget(remoteViews: RemoteViews?) {
+ super.updateAppWidget(remoteViews)
+ synchronized(this) {
+ mRemoteViews = remoteViews
+ if (remoteViews != null) {
+ mLatch?.countDown()
+ }
+ }
+ }
+
+ /** Reset the latch used to detect the arrival of a new RemoteViews. */
+ fun resetRemoteViewsLatch() {
+ synchronized(this) {
+ mRemoteViews = null
+ mLatch = null
+ }
+ }
+
+ fun setSizes(portraitSize: DpSize, landscapeSize: DpSize) {
+ mPortraitSize = portraitSize
+ mLandscapeSize = landscapeSize
+ updateSize(resources.configuration.orientation)
+ }
+
+ fun updateSize(orientation: Int) {
+ val size = when (orientation) {
+ Configuration.ORIENTATION_LANDSCAPE -> mLandscapeSize
+ Configuration.ORIENTATION_PORTRAIT -> mPortraitSize
+ else -> error("Unknown orientation ${context.resources.configuration.orientation}")
+ }
+ val displayMetrics = resources.displayMetrics
+ val width = size.width.toPixels(displayMetrics)
+ val height = size.height.toPixels(displayMetrics)
+ layoutParams = LayoutParams(width, height, Gravity.CENTER)
+ requestLayout()
+ }
+
+ private fun Dp.toPixels(displayMetrics: DisplayMetrics) =
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, displayMetrics).toInt()
+
+ fun reapplyRemoteViews() {
+ mRemoteViews?.let { super.updateAppWidget(it) }
+ }
+}
+
+fun optionsBundleOf(sizes: List<DpSize>): Bundle {
+ require(sizes.isNotEmpty()) { "There must be at least one size" }
+ val (minSize, maxSize) = sizes.fold(sizes[0] to sizes[0]) { acc, s ->
+ DpSize(min(acc.first.width, s.width), min(acc.first.height, s.height)) to
+ DpSize(max(acc.second.width, s.width), max(acc.second.height, s.height))
+ }
+ return Bundle().apply {
+ putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minSize.width.value.toInt())
+ putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minSize.height.value.toInt())
+ putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxSize.width.value.toInt())
+ putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxSize.height.value.toInt())
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ val sizeList = ArrayList<SizeF>(sizes.map { SizeF(it.width.value, it.height.value) })
+ putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, sizeList)
+ }
+ }
+}
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt
new file mode 100644
index 0000000..578f12f
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget.macrobenchmark
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.TraceSectionMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.testutils.createStartupCompilationParams
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RequiresApi(Build.VERSION_CODES.Q)
+@RunWith(Parameterized::class)
+class AppWidgetUpdateBenchmark(
+ private val startupMode: StartupMode,
+ private val compilationMode: CompilationMode,
+ useGlanceSession: Boolean,
+) {
+ @get:Rule
+ val benchmarkRule = MacrobenchmarkRule()
+
+ @get:Rule
+ val appWidgetHostRule = AppWidgetHostRule(useSession = useGlanceSession)
+
+ @OptIn(ExperimentalMetricApi::class)
+ @Test
+ fun initialUpdate() = benchmarkRule.measureRepeated(
+ packageName = "androidx.glance.appwidget.macrobenchmark.target",
+ metrics = listOf(
+ TraceSectionMetric("appWidgetInitialUpdate"),
+ TraceSectionMetric("GlanceAppWidget::update"),
+ ),
+ iterations = 100,
+ compilationMode = compilationMode,
+ startupMode = startupMode,
+ ) {
+ appWidgetHostRule.startHost()
+ }
+
+ @OptIn(ExperimentalMetricApi::class)
+ @Test
+ fun appWidgetUpdate() = benchmarkRule.measureRepeated(
+ packageName = "androidx.glance.appwidget.macrobenchmark.target",
+ metrics = listOf(
+ TraceSectionMetric("appWidgetUpdate"),
+ TraceSectionMetric("GlanceAppWidget::update"),
+ ),
+ iterations = 100,
+ compilationMode = compilationMode,
+ startupMode = startupMode,
+ setupBlock = {
+ appWidgetHostRule.startHost()
+ if (startupMode == StartupMode.COLD) killProcess()
+ }
+ ) {
+ appWidgetHostRule.updateAppWidget()
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "startup={0},compilation={1},useSession={2}")
+ @JvmStatic
+ fun parameters() =
+ createStartupCompilationParams(
+ startupModes = listOf(StartupMode.COLD, StartupMode.WARM),
+ compilationModes = listOf(CompilationMode.DEFAULT)
+ ).flatMap {
+ listOf(
+ it + true,
+ it + false,
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/res/layout/app_widget_host_activity.xml b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/res/layout/app_widget_host_activity.xml
new file mode 100644
index 0000000..aed767b
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/res/layout/app_widget_host_activity.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#AAAAAA"/>
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5b41847
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest />
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetSession.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetSession.kt
index 6bad985..c3ddc3e 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetSession.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetSession.kt
@@ -144,6 +144,7 @@
lastRemoteViews = rv
} finally {
layoutConfig.save()
+ Tracing.endGlanceAppWidgetUpdate()
}
}
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetUtils.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetUtils.kt
index e9063f8a..1ae6a21 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetUtils.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AppWidgetUtils.kt
@@ -18,12 +18,18 @@
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
+import android.os.Build
import android.os.Bundle
+import android.os.Trace
import android.util.DisplayMetrics
import android.util.Log
import android.util.SizeF
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
+import java.util.concurrent.atomic.AtomicBoolean
import kotlin.math.ceil
import kotlin.math.min
@@ -148,4 +154,41 @@
internal fun logException(throwable: Throwable) {
Log.e(GlanceAppWidgetTag, "Error in Glance App Widget", throwable)
+}
+
+/**
+ * [Tracing] contains methods for tracing sections of GlanceAppWidget.
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object Tracing {
+ val enabled = AtomicBoolean(false)
+
+ fun beginGlanceAppWidgetUpdate() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && enabled.get()) {
+ TracingApi29Impl.beginAsyncSection("GlanceAppWidget::update", 0)
+ }
+ }
+
+ fun endGlanceAppWidgetUpdate() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && enabled.get()) {
+ TracingApi29Impl.endAsyncSection("GlanceAppWidget::update", 0)
+ }
+ }
+}
+
+@RequiresApi(Build.VERSION_CODES.Q)
+internal object TracingApi29Impl {
+ @DoNotInline
+ fun beginAsyncSection(
+ methodName: String,
+ cookie: Int,
+ ) = Trace.beginAsyncSection(methodName, cookie)
+
+ @DoNotInline
+ fun endAsyncSection(
+ methodName: String,
+ cookie: Int,
+ ) = Trace.endAsyncSection(methodName, cookie)
}
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
index 49361f2..d12e0c6 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
@@ -164,6 +164,7 @@
appWidgetId: Int,
options: Bundle? = null,
) {
+ Tracing.beginGlanceAppWidgetUpdate()
sessionManager?.let {
val glanceId = AppWidgetId(appWidgetId)
if (!it.isSessionRunning(context, glanceId.toSessionKey())) {
@@ -517,6 +518,8 @@
logException(throwable)
val rv = RemoteViews(context.packageName, errorUiLayout)
appWidgetManager.updateAppWidget(appWidgetId, rv)
+ } finally {
+ Tracing.endGlanceAppWidgetUpdate()
}
}
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt
index 42f64cc..93d8962 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt
@@ -130,10 +130,15 @@
val appWidgetManager = AppWidgetManager.getInstance(context)
val componentName =
ComponentName(context.packageName, checkNotNull(javaClass.canonicalName))
+ val ids = if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)) {
+ intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)!!
+ } else {
+ appWidgetManager.getAppWidgetIds(componentName)
+ }
onUpdate(
context,
appWidgetManager,
- appWidgetManager.getAppWidgetIds(componentName)
+ ids,
)
}
LambdaActionBroadcasts.ActionTriggerLambda -> {
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/template/ListTemplateLayouts.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/template/ListTemplateLayouts.kt
index 77ef0cf..b3fb6fa 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/template/ListTemplateLayouts.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/template/ListTemplateLayouts.kt
@@ -23,6 +23,7 @@
import androidx.glance.GlanceComposable
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
+import androidx.glance.LocalSize
import androidx.glance.appwidget.lazy.LazyColumn
import androidx.glance.appwidget.lazy.itemsIndexed
import androidx.glance.background
@@ -34,6 +35,7 @@
import androidx.glance.layout.height
import androidx.glance.layout.padding
import androidx.glance.layout.width
+import androidx.glance.template.ImageBlock
import androidx.glance.template.ListStyle
import androidx.glance.template.ListTemplateData
import androidx.glance.template.LocalTemplateMode
@@ -86,13 +88,19 @@
itemsIndexed(data.listContent) { _, item ->
val itemSpacer = if (data.listStyle == ListStyle.Full) 8.dp else 0.dp
val itemModifier = GlanceModifier.fillMaxSize().padding(vertical = itemSpacer)
+ var itemImageBlock: ImageBlock? = null
+ if (LocalSize.current.width > GlanceTemplateAppWidget.sizeMin &&
+ LocalSize.current.height > GlanceTemplateAppWidget.sizeMin
+ ) {
+ itemImageBlock = item.imageBlock
+ }
Row(
modifier = itemModifier,
verticalAlignment = Alignment.Vertical.CenterVertically,
) {
TextAndImageBlockTemplate(
item.textBlock,
- item.imageBlock,
+ itemImageBlock,
GlanceModifier.defaultWeight()
)
Spacer(modifier = GlanceModifier.width(16.dp))
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
index 8d6eee1..068157e 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
@@ -119,7 +119,7 @@
) {
init {
- requireNonNegative(value = beatsPerMinute, name = "beatsPerMinute")
+ beatsPerMinute.requireNotLess(other = 1, name = "beatsPerMinute")
beatsPerMinute.requireNotMore(other = 300, name = "beatsPerMinute")
}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HeartRateRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HeartRateRecordTest.kt
index d5c8715..3152da4 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HeartRateRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HeartRateRecordTest.kt
@@ -82,4 +82,24 @@
)
}
}
+
+ @Test
+ fun invalidBeatsPerMinute_lessThan1_throws() {
+ assertFailsWith<IllegalArgumentException> {
+ HeartRateRecord.Sample(
+ time = Instant.ofEpochMilli(1235L),
+ beatsPerMinute = 0L,
+ )
+ }
+ }
+
+ @Test
+ fun invalidBeatsPerMinute_moreThan300_throws() {
+ assertFailsWith<IllegalArgumentException> {
+ HeartRateRecord.Sample(
+ time = Instant.ofEpochMilli(1235L),
+ beatsPerMinute = 301L,
+ )
+ }
+ }
}
diff --git a/input/input-motionprediction/api/current.txt b/input/input-motionprediction/api/current.txt
index a2cdf99..6611119 100644
--- a/input/input-motionprediction/api/current.txt
+++ b/input/input-motionprediction/api/current.txt
@@ -1,11 +1,11 @@
// Signature format: 4.0
package androidx.input.motionprediction {
- public interface MotionEventPredictor {
- method public void dispose();
+ public interface MotionEventPredictor extends java.lang.AutoCloseable {
+ method public void close();
method public static androidx.input.motionprediction.MotionEventPredictor newInstance(android.view.View);
method public android.view.MotionEvent? predict();
- method public void recordMovement(android.view.MotionEvent);
+ method public void record(android.view.MotionEvent);
}
}
diff --git a/input/input-motionprediction/api/public_plus_experimental_current.txt b/input/input-motionprediction/api/public_plus_experimental_current.txt
index a2cdf99..6611119 100644
--- a/input/input-motionprediction/api/public_plus_experimental_current.txt
+++ b/input/input-motionprediction/api/public_plus_experimental_current.txt
@@ -1,11 +1,11 @@
// Signature format: 4.0
package androidx.input.motionprediction {
- public interface MotionEventPredictor {
- method public void dispose();
+ public interface MotionEventPredictor extends java.lang.AutoCloseable {
+ method public void close();
method public static androidx.input.motionprediction.MotionEventPredictor newInstance(android.view.View);
method public android.view.MotionEvent? predict();
- method public void recordMovement(android.view.MotionEvent);
+ method public void record(android.view.MotionEvent);
}
}
diff --git a/input/input-motionprediction/api/restricted_current.txt b/input/input-motionprediction/api/restricted_current.txt
index a2cdf99..6611119 100644
--- a/input/input-motionprediction/api/restricted_current.txt
+++ b/input/input-motionprediction/api/restricted_current.txt
@@ -1,11 +1,11 @@
// Signature format: 4.0
package androidx.input.motionprediction {
- public interface MotionEventPredictor {
- method public void dispose();
+ public interface MotionEventPredictor extends java.lang.AutoCloseable {
+ method public void close();
method public static androidx.input.motionprediction.MotionEventPredictor newInstance(android.view.View);
method public android.view.MotionEvent? predict();
- method public void recordMovement(android.view.MotionEvent);
+ method public void record(android.view.MotionEvent);
}
}
diff --git a/input/input-motionprediction/build.gradle b/input/input-motionprediction/build.gradle
index 9475cdb..8d3d5e2 100644
--- a/input/input-motionprediction/build.gradle
+++ b/input/input-motionprediction/build.gradle
@@ -33,7 +33,7 @@
android {
defaultConfig {
- minSdkVersion 16
+ minSdkVersion 19
}
namespace "androidx.input.motionprediction"
}
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/MotionEventPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/MotionEventPredictor.java
index c27d293..31442ee 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/MotionEventPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/MotionEventPredictor.java
@@ -33,7 +33,7 @@
* rendered on the display. Once no more predictions are needed, call {@link #dispose()} to stop it
* and clean up resources.
*/
-public interface MotionEventPredictor {
+public interface MotionEventPredictor extends AutoCloseable {
/**
* Record a user's movement to the predictor. You should call this for every
* {@link android.view.MotionEvent} that is received by the associated
@@ -41,7 +41,7 @@
* @param event the {@link android.view.MotionEvent} the associated view received and that
* needs to be recorded.
*/
- void recordMovement(@NonNull MotionEvent event);
+ void record(@NonNull MotionEvent event);
/**
* Compute a prediction
@@ -55,7 +55,8 @@
* Notify the predictor that no more predictions are needed. Any subsequent call to
* {@link #predict()} will return null.
*/
- void dispose();
+ @Override
+ void close();
/**
* Create a new motion predictor associated to a specific {@link android.view.View}
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanMotionEventPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanMotionEventPredictor.java
index e5abbd9..df88594 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanMotionEventPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanMotionEventPredictor.java
@@ -31,7 +31,7 @@
@RestrictTo(LIBRARY)
public class KalmanMotionEventPredictor implements MotionEventPredictor {
private MultiPointerPredictor mMultiPointerPredictor;
- private boolean mDisposed = false;
+ private boolean mClosed = false;
public KalmanMotionEventPredictor() {
mMultiPointerPredictor = new MultiPointerPredictor();
@@ -43,21 +43,21 @@
}
@Override
- public void recordMovement(@NonNull MotionEvent event) {
+ public void record(@NonNull MotionEvent event) {
mMultiPointerPredictor.onTouchEvent(event);
}
@Nullable
@Override
public MotionEvent predict() {
- if (mDisposed) {
+ if (mClosed) {
return null;
}
return mMultiPointerPredictor.predict();
}
@Override
- public void dispose() {
- mDisposed = true;
+ public void close() {
+ mClosed = true;
}
}
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/InkPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanPredictor.java
similarity index 97%
rename from input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/InkPredictor.java
rename to input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanPredictor.java
index 98f4a44..f655aec 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/InkPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanPredictor.java
@@ -30,7 +30,7 @@
* @hide
*/
@RestrictTo(LIBRARY)
-public interface InkPredictor {
+public interface KalmanPredictor {
/** Gets the current prediction target */
int getPredictionTarget();
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/MultiPointerPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/MultiPointerPredictor.java
index c97dd19..1b0b3cd 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/MultiPointerPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/MultiPointerPredictor.java
@@ -32,11 +32,11 @@
* @hide
*/
@RestrictTo(LIBRARY)
-public class MultiPointerPredictor implements InkPredictor {
+public class MultiPointerPredictor implements KalmanPredictor {
private static final String TAG = "MultiPointerPredictor";
private static final boolean DEBUG_PREDICTION = Log.isLoggable(TAG, Log.DEBUG);
- private SparseArray<KalmanInkPredictor> mPredictorMap = new SparseArray<>();
+ private SparseArray<SinglePointerPredictor> mPredictorMap = new SparseArray<>();
private int mPredictionTargetMs = 0;
private int mReportRateMs = 0;
@@ -77,7 +77,7 @@
int action = event.getActionMasked();
int pointerId = event.getPointerId(event.getActionIndex());
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {
- KalmanInkPredictor predictor = new KalmanInkPredictor();
+ SinglePointerPredictor predictor = new SinglePointerPredictor();
predictor.setPredictionTarget(mPredictionTargetMs);
if (mReportRateMs > 0) {
predictor.setReportRate(mReportRateMs);
@@ -86,14 +86,14 @@
predictor.onTouchEvent(event);
mPredictorMap.put(pointerId, predictor);
} else if (action == MotionEvent.ACTION_UP) {
- KalmanInkPredictor predictor = mPredictorMap.get(pointerId);
+ SinglePointerPredictor predictor = mPredictorMap.get(pointerId);
if (predictor != null) {
mPredictorMap.remove(pointerId);
predictor.onTouchEvent(event);
}
mPredictorMap.clear();
} else if (action == MotionEvent.ACTION_POINTER_UP) {
- KalmanInkPredictor predictor = mPredictorMap.get(pointerId);
+ SinglePointerPredictor predictor = mPredictorMap.get(pointerId);
if (predictor != null) {
mPredictorMap.remove(pointerId);
predictor.onTouchEvent(event);
@@ -126,7 +126,7 @@
return null;
}
if (pointerCount == 1) {
- KalmanInkPredictor predictor = mPredictorMap.valueAt(0);
+ SinglePointerPredictor predictor = mPredictorMap.valueAt(0);
MotionEvent predictedEv = predictor.predict();
if (DEBUG_PREDICTION) {
Log.d(TAG, "predict() -> MotionEvent: " + predictedEv);
@@ -139,7 +139,7 @@
MotionEvent[] singlePointerEvents = new MotionEvent[pointerCount];
for (int i = 0; i < pointerCount; ++i) {
pointerIds[i] = mPredictorMap.keyAt(i);
- KalmanInkPredictor predictor = mPredictorMap.valueAt(i);
+ SinglePointerPredictor predictor = mPredictorMap.valueAt(i);
singlePointerEvents[i] = predictor.predict();
// If predictor consumer expect more sample, generate sample where position and
// pressure are constant
@@ -161,7 +161,9 @@
if (foundNullPrediction) {
for (MotionEvent ev : singlePointerEvents) {
- ev.recycle();
+ if (ev != null) {
+ ev.recycle();
+ }
}
return null;
}
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PenKalmanFilter.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PointerKalmanFilter.java
similarity index 97%
rename from input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PenKalmanFilter.java
rename to input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PointerKalmanFilter.java
index a70b21e..6a80269 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PenKalmanFilter.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/PointerKalmanFilter.java
@@ -29,7 +29,7 @@
* @hide
*/
@RestrictTo(LIBRARY)
-public class PenKalmanFilter {
+public class PointerKalmanFilter {
private KalmanFilter mXKalman;
private KalmanFilter mYKalman;
private KalmanFilter mPKalman;
@@ -54,7 +54,7 @@
* @param sigmaProcess lower value = more filtering
* @param sigmaMeasurement higher value = more filtering
*/
- public PenKalmanFilter(double sigmaProcess, double sigmaMeasurement) {
+ public PointerKalmanFilter(double sigmaProcess, double sigmaMeasurement) {
mSigmaProcess = sigmaProcess;
mSigmaMeasurement = sigmaMeasurement;
mXKalman = createAxisKalmanFilter();
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanInkPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java
similarity index 98%
rename from input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanInkPredictor.java
rename to input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java
index 73c1f4a..0656734 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/KalmanInkPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java
@@ -34,7 +34,7 @@
* @hide
*/
@RestrictTo(LIBRARY)
-public class KalmanInkPredictor implements InkPredictor {
+public class SinglePointerPredictor implements KalmanPredictor {
private static final String TAG = "KalmanInkPredictor";
// Influence of jank during each prediction sample
@@ -67,7 +67,7 @@
// The Kalman filter is tuned to smooth noise while maintaining fast reaction to direction
// changes. The stronger the filter, the smoother the prediction result will be, at the
// cost of possible prediction errors.
- private final PenKalmanFilter mKalman = new PenKalmanFilter(0.01, 1.0);
+ private final PointerKalmanFilter mKalman = new PointerKalmanFilter(0.01, 1.0);
private final DVector2 mLastPosition = new DVector2();
private long mPrevEventTime;
@@ -93,7 +93,7 @@
* achieving close-to-zero latency, prediction errors can be more visible and the target should
* be reduced to 20ms.
*/
- public KalmanInkPredictor() {
+ public SinglePointerPredictor() {
mKalman.reset();
mPrevEventTime = 0;
}
diff --git a/lifecycle/lifecycle-common/api/current.txt b/lifecycle/lifecycle-common/api/current.txt
index 1e1ed43..9520799 100644
--- a/lifecycle/lifecycle-common/api/current.txt
+++ b/lifecycle/lifecycle-common/api/current.txt
@@ -1,7 +1,7 @@
// Signature format: 4.0
package androidx.lifecycle {
- public interface DefaultLifecycleObserver extends androidx.lifecycle.LifecycleObserver androidx.lifecycle.LifecycleObserver {
+ public interface DefaultLifecycleObserver extends androidx.lifecycle.LifecycleObserver {
method public default void onCreate(androidx.lifecycle.LifecycleOwner);
method public default void onDestroy(androidx.lifecycle.LifecycleOwner);
method public default void onPause(androidx.lifecycle.LifecycleOwner);
diff --git a/lifecycle/lifecycle-common/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-common/api/public_plus_experimental_current.txt
index 1e1ed43..9520799 100644
--- a/lifecycle/lifecycle-common/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-common/api/public_plus_experimental_current.txt
@@ -1,7 +1,7 @@
// Signature format: 4.0
package androidx.lifecycle {
- public interface DefaultLifecycleObserver extends androidx.lifecycle.LifecycleObserver androidx.lifecycle.LifecycleObserver {
+ public interface DefaultLifecycleObserver extends androidx.lifecycle.LifecycleObserver {
method public default void onCreate(androidx.lifecycle.LifecycleOwner);
method public default void onDestroy(androidx.lifecycle.LifecycleOwner);
method public default void onPause(androidx.lifecycle.LifecycleOwner);
diff --git a/lifecycle/lifecycle-common/api/restricted_current.txt b/lifecycle/lifecycle-common/api/restricted_current.txt
index cdf6954..138ab792 100644
--- a/lifecycle/lifecycle-common/api/restricted_current.txt
+++ b/lifecycle/lifecycle-common/api/restricted_current.txt
@@ -1,7 +1,7 @@
// Signature format: 4.0
package androidx.lifecycle {
- public interface DefaultLifecycleObserver extends androidx.lifecycle.LifecycleObserver androidx.lifecycle.LifecycleObserver {
+ public interface DefaultLifecycleObserver extends androidx.lifecycle.LifecycleObserver {
method public default void onCreate(androidx.lifecycle.LifecycleOwner);
method public default void onDestroy(androidx.lifecycle.LifecycleOwner);
method public default void onPause(androidx.lifecycle.LifecycleOwner);
diff --git a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/DefaultLifecycleObserver.java b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/DefaultLifecycleObserver.java
index e1188a7..63b0688 100644
--- a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/DefaultLifecycleObserver.java
+++ b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/DefaultLifecycleObserver.java
@@ -28,7 +28,7 @@
* annotations will be ignored.
*/
@SuppressWarnings("unused")
-public interface DefaultLifecycleObserver extends FullLifecycleObserver {
+public interface DefaultLifecycleObserver extends LifecycleObserver {
/**
* Notifies that {@code ON_CREATE} event occurred.
@@ -38,7 +38,6 @@
*
* @param owner the component, whose state was changed
*/
- @Override
default void onCreate(@NonNull LifecycleOwner owner) {
}
@@ -49,7 +48,6 @@
*
* @param owner the component, whose state was changed
*/
- @Override
default void onStart(@NonNull LifecycleOwner owner) {
}
@@ -61,7 +59,6 @@
*
* @param owner the component, whose state was changed
*/
- @Override
default void onResume(@NonNull LifecycleOwner owner) {
}
@@ -73,7 +70,6 @@
*
* @param owner the component, whose state was changed
*/
- @Override
default void onPause(@NonNull LifecycleOwner owner) {
}
@@ -85,7 +81,6 @@
*
* @param owner the component, whose state was changed
*/
- @Override
default void onStop(@NonNull LifecycleOwner owner) {
}
@@ -97,7 +92,6 @@
*
* @param owner the component, whose state was changed
*/
- @Override
default void onDestroy(@NonNull LifecycleOwner owner) {
}
}
diff --git a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/FullLifecycleObserverAdapter.java b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/DefaultLifecycleObserverAdapter.java
similarity index 71%
rename from lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/FullLifecycleObserverAdapter.java
rename to lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/DefaultLifecycleObserverAdapter.java
index 10223d4..8d87eac 100644
--- a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/FullLifecycleObserverAdapter.java
+++ b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/DefaultLifecycleObserverAdapter.java
@@ -18,14 +18,14 @@
import androidx.annotation.NonNull;
-class FullLifecycleObserverAdapter implements LifecycleEventObserver {
+class DefaultLifecycleObserverAdapter implements LifecycleEventObserver {
- private final FullLifecycleObserver mFullLifecycleObserver;
+ private final DefaultLifecycleObserver mDefaultLifecycleObserver;
private final LifecycleEventObserver mLifecycleEventObserver;
- FullLifecycleObserverAdapter(FullLifecycleObserver fullLifecycleObserver,
+ DefaultLifecycleObserverAdapter(DefaultLifecycleObserver defaultLifecycleObserver,
LifecycleEventObserver lifecycleEventObserver) {
- mFullLifecycleObserver = fullLifecycleObserver;
+ mDefaultLifecycleObserver = defaultLifecycleObserver;
mLifecycleEventObserver = lifecycleEventObserver;
}
@@ -33,22 +33,22 @@
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
switch (event) {
case ON_CREATE:
- mFullLifecycleObserver.onCreate(source);
+ mDefaultLifecycleObserver.onCreate(source);
break;
case ON_START:
- mFullLifecycleObserver.onStart(source);
+ mDefaultLifecycleObserver.onStart(source);
break;
case ON_RESUME:
- mFullLifecycleObserver.onResume(source);
+ mDefaultLifecycleObserver.onResume(source);
break;
case ON_PAUSE:
- mFullLifecycleObserver.onPause(source);
+ mDefaultLifecycleObserver.onPause(source);
break;
case ON_STOP:
- mFullLifecycleObserver.onStop(source);
+ mDefaultLifecycleObserver.onStop(source);
break;
case ON_DESTROY:
- mFullLifecycleObserver.onDestroy(source);
+ mDefaultLifecycleObserver.onDestroy(source);
break;
case ON_ANY:
throw new IllegalArgumentException("ON_ANY must not been send by anybody");
diff --git a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/FullLifecycleObserver.java b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/FullLifecycleObserver.java
deleted file mode 100644
index c959d03..0000000
--- a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/FullLifecycleObserver.java
+++ /dev/null
@@ -1,32 +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.lifecycle;
-
-interface FullLifecycleObserver extends LifecycleObserver {
-
- void onCreate(LifecycleOwner owner);
-
- void onStart(LifecycleOwner owner);
-
- void onResume(LifecycleOwner owner);
-
- void onPause(LifecycleOwner owner);
-
- void onStop(LifecycleOwner owner);
-
- void onDestroy(LifecycleOwner owner);
-}
diff --git a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/LifecycleObserver.java b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/LifecycleObserver.kt
similarity index 80%
rename from lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/LifecycleObserver.java
rename to lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/LifecycleObserver.kt
index 71ce3b1..9c7e5a0 100644
--- a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/LifecycleObserver.java
+++ b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/LifecycleObserver.kt
@@ -13,17 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package androidx.lifecycle;
+package androidx.lifecycle
/**
* Marks a class as a LifecycleObserver. Don't use this interface directly. Instead implement either
- * {@link DefaultLifecycleObserver} or {@link LifecycleEventObserver} to be notified about
+ * [DefaultLifecycleObserver] or [LifecycleEventObserver] to be notified about
* lifecycle events.
*
* @see Lifecycle Lifecycle - for samples and usage patterns.
*/
-@SuppressWarnings("WeakerAccess")
-public interface LifecycleObserver {
-
-}
+public interface LifecycleObserver
diff --git a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycling.java b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycling.java
index 2714780..722f0bf 100644
--- a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycling.java
+++ b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycling.java
@@ -67,13 +67,13 @@
@SuppressWarnings("deprecation")
static LifecycleEventObserver lifecycleEventObserver(Object object) {
boolean isLifecycleEventObserver = object instanceof LifecycleEventObserver;
- boolean isFullLifecycleObserver = object instanceof FullLifecycleObserver;
- if (isLifecycleEventObserver && isFullLifecycleObserver) {
- return new FullLifecycleObserverAdapter((FullLifecycleObserver) object,
+ boolean isDefaultLifecycleObserver = object instanceof DefaultLifecycleObserver;
+ if (isLifecycleEventObserver && isDefaultLifecycleObserver) {
+ return new DefaultLifecycleObserverAdapter((DefaultLifecycleObserver) object,
(LifecycleEventObserver) object);
}
- if (isFullLifecycleObserver) {
- return new FullLifecycleObserverAdapter((FullLifecycleObserver) object, null);
+ if (isDefaultLifecycleObserver) {
+ return new DefaultLifecycleObserverAdapter((DefaultLifecycleObserver) object, null);
}
if (isLifecycleEventObserver) {
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/FullLifecycleObserverTest.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/DefaultLifecycleObserverTest.java
similarity index 80%
rename from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/FullLifecycleObserverTest.java
rename to lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/DefaultLifecycleObserverTest.java
index f706eeb..958274c 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/FullLifecycleObserverTest.java
+++ b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/DefaultLifecycleObserverTest.java
@@ -42,7 +42,7 @@
import org.mockito.Mockito;
@RunWith(JUnit4.class)
-public class FullLifecycleObserverTest {
+public class DefaultLifecycleObserverTest {
private LifecycleOwner mOwner;
private Lifecycle mLifecycle;
@@ -55,8 +55,8 @@
@Test
public void eachEvent() {
- FullLifecycleObserver obj = mock(FullLifecycleObserver.class);
- FullLifecycleObserverAdapter observer = new FullLifecycleObserverAdapter(obj, null);
+ DefaultLifecycleObserver obj = mock(DefaultLifecycleObserver.class);
+ DefaultLifecycleObserverAdapter observer = new DefaultLifecycleObserverAdapter(obj, null);
when(mLifecycle.getCurrentState()).thenReturn(CREATED);
observer.onStateChanged(mOwner, ON_CREATE);
@@ -91,36 +91,36 @@
}
@Test
- public void fullLifecycleObserverAndLifecycleEventObserver() {
- class AllObservers implements FullLifecycleObserver, LifecycleEventObserver {
+ public void defaultLifecycleObserverAndLifecycleEventObserver() {
+ class AllObservers implements DefaultLifecycleObserver, LifecycleEventObserver {
@Override
- public void onCreate(LifecycleOwner owner) {
+ public void onCreate(@NonNull LifecycleOwner owner) {
}
@Override
- public void onStart(LifecycleOwner owner) {
+ public void onStart(@NonNull LifecycleOwner owner) {
}
@Override
- public void onResume(LifecycleOwner owner) {
+ public void onResume(@NonNull LifecycleOwner owner) {
}
@Override
- public void onPause(LifecycleOwner owner) {
+ public void onPause(@NonNull LifecycleOwner owner) {
}
@Override
- public void onStop(LifecycleOwner owner) {
+ public void onStop(@NonNull LifecycleOwner owner) {
}
@Override
- public void onDestroy(LifecycleOwner owner) {
+ public void onDestroy(@NonNull LifecycleOwner owner) {
}
@@ -132,7 +132,7 @@
}
AllObservers obj = mock(AllObservers.class);
- FullLifecycleObserverAdapter observer = new FullLifecycleObserverAdapter(obj, obj);
+ DefaultLifecycleObserverAdapter observer = new DefaultLifecycleObserverAdapter(obj, obj);
when(mLifecycle.getCurrentState()).thenReturn(CREATED);
observer.onStateChanged(mOwner, ON_CREATE);
@@ -174,7 +174,7 @@
public void fullLifecycleObserverAndAnnotations() {
@SuppressWarnings("deprecation")
- class AnnotatedFullLifecycleObserver implements FullLifecycleObserver {
+ class AnnotatedFullLifecycleObserver implements DefaultLifecycleObserver {
@OnLifecycleEvent(ON_ANY)
public void onAny() {
throw new IllegalStateException("Annotations in FullLifecycleObserver "
@@ -182,32 +182,32 @@
}
@Override
- public void onCreate(LifecycleOwner owner) {
+ public void onCreate(@NonNull LifecycleOwner owner) {
}
@Override
- public void onStart(LifecycleOwner owner) {
+ public void onStart(@NonNull LifecycleOwner owner) {
}
@Override
- public void onResume(LifecycleOwner owner) {
+ public void onResume(@NonNull LifecycleOwner owner) {
}
@Override
- public void onPause(LifecycleOwner owner) {
+ public void onPause(@NonNull LifecycleOwner owner) {
}
@Override
- public void onStop(LifecycleOwner owner) {
+ public void onStop(@NonNull LifecycleOwner owner) {
}
@Override
- public void onDestroy(LifecycleOwner owner) {
+ public void onDestroy(@NonNull LifecycleOwner owner) {
}
}
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/LifecyclingTest.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/LifecyclingTest.java
index b5ab5fd..0fc7f0e 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/LifecyclingTest.java
+++ b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/LifecyclingTest.java
@@ -116,8 +116,8 @@
}
@Test
- public void fullLifecycleObserverAndAnnotations() {
- class AnnotatedFullLifecycleObserver implements FullLifecycleObserver {
+ public void defaultLifecycleObserverAndAnnotations() {
+ class AnnotatedFullLifecycleObserver implements DefaultLifecycleObserver {
@SuppressWarnings("deprecation")
@OnLifecycleEvent(ON_ANY)
public void onAny() {
@@ -126,32 +126,32 @@
}
@Override
- public void onCreate(LifecycleOwner owner) {
+ public void onCreate(@NonNull LifecycleOwner owner) {
}
@Override
- public void onStart(LifecycleOwner owner) {
+ public void onStart(@NonNull LifecycleOwner owner) {
}
@Override
- public void onResume(LifecycleOwner owner) {
+ public void onResume(@NonNull LifecycleOwner owner) {
}
@Override
- public void onPause(LifecycleOwner owner) {
+ public void onPause(@NonNull LifecycleOwner owner) {
}
@Override
- public void onStop(LifecycleOwner owner) {
+ public void onStop(@NonNull LifecycleOwner owner) {
}
@Override
- public void onDestroy(LifecycleOwner owner) {
+ public void onDestroy(@NonNull LifecycleOwner owner) {
}
}
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
index 28ded62..39680f5 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -1640,6 +1640,23 @@
@UiThreadTest
@Test
+ fun testNavigateMultipleParentsOnHierarchy() {
+ val navController = createNavController()
+ navController.setGraph(R.navigation.nav_root)
+ assertThat(navController.currentDestination?.id ?: 0).isEqualTo(R.id.root_start)
+
+ // nav_second has two parents: nav_root and nav_first
+ // nav_first has one parent: nav_root
+ // they share common parent of nav_root
+ navController.navigate(Uri.parse("http://www.second.com"))
+ assertThat(navController.currentDestination?.id ?: 0).isEqualTo(R.id.second_start)
+
+ navController.popBackStack()
+ assertThat(navController.currentDestination?.id).isEqualTo(R.id.root_start)
+ }
+
+ @UiThreadTest
+ @Test
fun testNavigateWithOverriddenDefaultArgs() {
val args = Bundle()
args.putString(TEST_OVERRIDDEN_VALUE_ARG, TEST_OVERRIDDEN_VALUE_ARG_VALUE)
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_first.xml b/navigation/navigation-runtime/src/androidTest/res/navigation/nav_first.xml
new file mode 100644
index 0000000..5951898
--- /dev/null
+++ b/navigation/navigation-runtime/src/androidTest/res/navigation/nav_first.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/nav_first"
+ android:label=""
+ app:startDestination="@+id/first_start">
+
+ <include app:graph="@navigation/nav_second" />
+
+ <test android:id="@+id/first_start">
+ <deepLink app:uri="http://www.first.com" />
+ </test>
+</navigation>
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_root.xml b/navigation/navigation-runtime/src/androidTest/res/navigation/nav_root.xml
new file mode 100644
index 0000000..df94bf6
--- /dev/null
+++ b/navigation/navigation-runtime/src/androidTest/res/navigation/nav_root.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/nav_root"
+ android:label=""
+ app:startDestination="@+id/root_start">
+
+ <include app:graph="@navigation/nav_first" />
+ <include app:graph="@navigation/nav_second" />
+
+ <test android:id="@+id/root_start" />
+
+</navigation>
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_second.xml b/navigation/navigation-runtime/src/androidTest/res/navigation/nav_second.xml
new file mode 100644
index 0000000..4f77c8f
--- /dev/null
+++ b/navigation/navigation-runtime/src/androidTest/res/navigation/nav_second.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/nav_second"
+ android:label=""
+ app:startDestination="@+id/second_start">
+
+ <test android:id="@+id/second_start">
+ <deepLink app:uri="http://www.second.com" />
+ </test>
+</navigation>
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index 094e8a4..b6c957c 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -1866,9 +1866,11 @@
}
// Now collect the set of all intermediate NavGraphs that need to be put onto
- // the back stack
+ // the back stack. Destinations can have multiple parents, so we check referential
+ // equality to ensure that same destinations with a parent that is not this _graph
+ // will also have their parents added to the hierarchy.
destination = if (hierarchy.isEmpty()) newDest else hierarchy.first().destination
- while (destination != null && findDestination(destination.id) == null) {
+ while (destination != null && findDestination(destination.id) !== destination) {
val parent = destination.parent
if (parent != null) {
val entry = restoredEntries.lastOrNull { restoredEntry ->
diff --git a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt
index 83c849d..d5a45dc 100644
--- a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt
+++ b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/AppBarConfigurationTest.kt
@@ -24,6 +24,7 @@
import androidx.navigation.NavHostController
import androidx.navigation.createGraph
import androidx.navigation.plusAssign
+import androidx.navigation.ui.test.R
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
diff --git a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/NavigationUITest.kt b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/NavigationUITest.kt
index 0796ff3..992ee49 100644
--- a/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/NavigationUITest.kt
+++ b/navigation/navigation-ui/src/androidTest/java/androidx/navigation/ui/NavigationUITest.kt
@@ -26,6 +26,7 @@
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.createGraph
+import androidx.navigation.ui.test.R
import androidx.test.annotation.UiThreadTest
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/navigation/navigation-ui/src/main/res/menu/menu.xml b/navigation/navigation-ui/src/androidTest/res/menu/menu.xml
similarity index 100%
rename from navigation/navigation-ui/src/main/res/menu/menu.xml
rename to navigation/navigation-ui/src/androidTest/res/menu/menu.xml
diff --git a/navigation/navigation-ui/src/main/res/navigation/menu_item_graph.xml b/navigation/navigation-ui/src/androidTest/res/navigation/menu_item_graph.xml
similarity index 100%
rename from navigation/navigation-ui/src/main/res/navigation/menu_item_graph.xml
rename to navigation/navigation-ui/src/androidTest/res/navigation/menu_item_graph.xml
diff --git a/navigation/navigation-ui/src/main/res/navigation/simple_graph.xml b/navigation/navigation-ui/src/androidTest/res/navigation/simple_graph.xml
similarity index 100%
rename from navigation/navigation-ui/src/main/res/navigation/simple_graph.xml
rename to navigation/navigation-ui/src/androidTest/res/navigation/simple_graph.xml
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index 368f02f..e168416 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -14,7 +14,7 @@
stackFrame
}.toTypedArray()
throwable.cause?.let {
- parcel.cause = toThrowableParcel(it)
+ parcel.cause = arrayOf(toThrowableParcel(it))
}
parcel.suppressedExceptions =
throwable.suppressedExceptions.map {
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index 368f02f..e168416 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkruntimelibrarysdk/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -14,7 +14,7 @@
stackFrame
}.toTypedArray()
throwable.cause?.let {
- parcel.cause = toThrowableParcel(it)
+ parcel.cause = arrayOf(toThrowableParcel(it))
}
parcel.suppressedExceptions =
throwable.suppressedExceptions.map {
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt
index 4b0857c..73d230c 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/PrivacySandboxThrowableParcelConverter.kt
@@ -9,7 +9,10 @@
val stackTrace = throwableParcel.stackTrace
val exception = PrivacySandboxException(
"[$exceptionClass] $errorMessage",
- throwableParcel.cause?.let { fromThrowableParcel(it) })
+ throwableParcel.cause?.firstOrNull()?.let {
+ fromThrowableParcel(it)
+ }
+ )
for (suppressed in throwableParcel.suppressedExceptions) {
exception.addSuppressed(fromThrowableParcel(suppressed))
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
index 42297ec..7797726 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/PrivacySandboxThrowableParcelConverter.kt
@@ -9,7 +9,10 @@
val stackTrace = throwableParcel.stackTrace
val exception = PrivacySandboxException(
"[$exceptionClass] $errorMessage",
- throwableParcel.cause?.let { fromThrowableParcel(it) })
+ throwableParcel.cause?.firstOrNull()?.let {
+ fromThrowableParcel(it)
+ }
+ )
for (suppressed in throwableParcel.suppressedExceptions) {
exception.addSuppressed(fromThrowableParcel(suppressed))
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt
index 34de1d7..d86cb2b 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/PrivacySandboxThrowableParcelConverter.kt
@@ -9,7 +9,10 @@
val stackTrace = throwableParcel.stackTrace
val exception = PrivacySandboxException(
"[$exceptionClass] $errorMessage",
- throwableParcel.cause?.let { fromThrowableParcel(it) })
+ throwableParcel.cause?.firstOrNull()?.let {
+ fromThrowableParcel(it)
+ }
+ )
for (suppressed in throwableParcel.suppressedExceptions) {
exception.addSuppressed(fromThrowableParcel(suppressed))
}
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
index 4c245ac..1df8157 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
@@ -170,7 +170,7 @@
addProperty("exceptionClass", primitive("String"))
addProperty("errorMessage", primitive("String"))
addProperty("stackTrace", AidlTypeSpec(parcelableStackFrameType(), isList = true))
- addProperty("cause", AidlTypeSpec(throwableParcelType()), isNullable = true)
+ addProperty("cause", AidlTypeSpec(throwableParcelType(), isList = true))
addProperty(
"suppressedExceptions", AidlTypeSpec(throwableParcelType(), isList = true)
)
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt
index 4ac7844..b45aca8 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ThrowableParcelConverterFileGenerator.kt
@@ -77,7 +77,7 @@
stackFrame
}.toTypedArray()
throwable.cause?.let {
- parcel.cause = ${toThrowableParcelNameSpec.simpleName}(it)
+ parcel.cause = arrayOf(${toThrowableParcelNameSpec.simpleName}(it))
}
parcel.suppressedExceptions =
throwable.suppressedExceptions.map {
@@ -103,7 +103,10 @@
val stackTrace = throwableParcel.stackTrace
val exception = PrivacySandboxException(
"[${'$'}exceptionClass] ${'$'}errorMessage",
- throwableParcel.cause?.let { ${fromThrowableParcelNameSpec.simpleName}(it) })
+ throwableParcel.cause?.firstOrNull()?.let {
+ ${fromThrowableParcelNameSpec.simpleName}(it)
+ }
+ )
for (suppressed in throwableParcel.suppressedExceptions) {
exception.addSuppressed(${fromThrowableParcelNameSpec.simpleName}(suppressed))
}
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
index 716a0f9..cff4771 100644
--- a/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlinterfacegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
@@ -3,8 +3,8 @@
import com.mysdk.ParcelableStackFrame;
parcelable PrivacySandboxThrowableParcel {
- @nullable(heap=true) PrivacySandboxThrowableParcel cause;
ParcelableStackFrame[] stackTrace;
+ PrivacySandboxThrowableParcel[] cause;
PrivacySandboxThrowableParcel[] suppressedExceptions;
String errorMessage;
String exceptionClass;
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
index 716a0f9..cff4771 100644
--- a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
@@ -3,8 +3,8 @@
import com.mysdk.ParcelableStackFrame;
parcelable PrivacySandboxThrowableParcel {
- @nullable(heap=true) PrivacySandboxThrowableParcel cause;
ParcelableStackFrame[] stackTrace;
+ PrivacySandboxThrowableParcel[] cause;
PrivacySandboxThrowableParcel[] suppressedExceptions;
String errorMessage;
String exceptionClass;
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
index 716a0f9..cff4771 100644
--- a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/PrivacySandboxThrowableParcel.aidl
@@ -3,8 +3,8 @@
import com.mysdk.ParcelableStackFrame;
parcelable PrivacySandboxThrowableParcel {
- @nullable(heap=true) PrivacySandboxThrowableParcel cause;
ParcelableStackFrame[] stackTrace;
+ PrivacySandboxThrowableParcel[] cause;
PrivacySandboxThrowableParcel[] suppressedExceptions;
String errorMessage;
String exceptionClass;
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt
index cedb6fb6..b79f94a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XType.kt
@@ -60,7 +60,11 @@
val nullability: XNullability
/**
- * The resolved types of the super classes/interfaces of this type.
+ * The resolved direct super types of this type.
+ *
+ * The interface types, if any, will appear last in the list.
+ *
+ * See [Types#directSupertypes()](https://docs.oracle.com/javase/7/docs/api/javax/lang/model/util/Types.html#directSupertypes(javax.lang.model.type.TypeMirror))
*/
val superTypes: List<XType>
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
index 80525af..1faa32c9 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
@@ -46,13 +46,6 @@
get() = superClass
/**
- * The direct super types of this element.
- *
- * See [JLS 4.10.2](https://docs.oracle.com/javase/specs/jls/se18/html/jls-4.html#jls-4.10.2)
- */
- val superTypes: List<XType>
-
- /**
* The super class of this element if it represents a class.
*/
val superClass: XType?
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
index 403d6fb..a2beb5a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
@@ -25,7 +25,6 @@
import androidx.room.compiler.processing.XMemberContainer
import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.XNullability
-import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.XTypeElement
import androidx.room.compiler.processing.XTypeParameterElement
import androidx.room.compiler.processing.collectAllMethods
@@ -36,7 +35,6 @@
import com.google.auto.common.MoreElements
import com.google.auto.common.MoreTypes
import com.squareup.javapoet.ClassName
-import com.squareup.javapoet.TypeName
import com.squareup.kotlinpoet.javapoet.JClassName
import javax.lang.model.element.ElementKind
import javax.lang.model.element.TypeElement
@@ -195,17 +193,6 @@
)
}
- override val superTypes: List<XType> by lazy {
- buildList {
- if (isInterface() && superInterfaces.isEmpty()) {
- add(env.requireType(TypeName.OBJECT))
- } else {
- superClass?.let { add(it) }
- addAll(superInterfaces)
- }
- }
- }
-
override val superClass: JavacType? by lazy {
// javac models non-existing types as TypeKind.NONE but we prefer to make it nullable.
// just makes more sense and safer as we don't need to check for none.
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
index 3e072561..3acfe4a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
@@ -26,9 +26,13 @@
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.KSTypeArgument
+import com.google.devtools.ksp.symbol.KSTypeParameter
import com.google.devtools.ksp.symbol.KSTypeReference
import com.google.devtools.ksp.symbol.Nullability
+import com.google.devtools.ksp.symbol.Variance
import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.WildcardTypeName
import com.squareup.kotlinpoet.javapoet.JTypeName
import com.squareup.kotlinpoet.javapoet.KTypeName
import kotlin.reflect.KClass
@@ -91,13 +95,60 @@
}
override val superTypes: List<XType> by lazy {
- val declaration = ksType.declaration as? KSClassDeclaration
- declaration?.superTypes?.toList()?.map {
+ if (typeName == TypeName.OBJECT) {
+ // The object class doesn't have any supertypes.
+ return@lazy emptyList<XType>()
+ }
+ val resolvedTypeArguments: Map<String, KSTypeArgument> =
+ ksType.declaration.typeParameters.mapIndexed { i, parameter ->
+ parameter.name.asString() to ksType.arguments[i]
+ }.toMap()
+ val superTypes = (ksType.declaration as? KSClassDeclaration)?.superTypes?.toList()?.map {
env.wrap(
- ksType = it.resolve(),
+ ksType = resolveTypeArguments(it.resolve(), resolvedTypeArguments),
allowPrimitives = false
)
} ?: emptyList()
+ val (superClasses, superInterfaces) = superTypes.partition {
+ it.typeElement?.isClass() == true
+ }
+ // Per documentation, always return the class before the interfaces.
+ if (superClasses.isEmpty()) {
+ // Return Object when there's no explicit super class specified on the class/interface.
+ // This matches javac's Types#directSupertypes().
+ listOf(env.requireType(TypeName.OBJECT)) + superInterfaces
+ } else {
+ check(superClasses.size == 1)
+ superClasses + superInterfaces
+ }
+ }
+
+ private fun resolveTypeArguments(
+ type: KSType,
+ resolvedTypeArguments: Map<String, KSTypeArgument>
+ ): KSType {
+ return type.replace(
+ type.arguments.map { argument ->
+ val argDeclaration = argument.type?.resolve()?.declaration
+ if (argDeclaration is KSTypeParameter) {
+ // If this is a type parameter, replace it with the resolved type argument.
+ resolvedTypeArguments[argDeclaration.name.asString()] ?: argument
+ } else if (
+ argument.type != null && argument.type?.resolve()?.arguments?.isEmpty() == false
+ ) {
+ // If this is a type with arguments, the arguments may contain a type parameter,
+ // e.g. Foo<T>, so try to resolve the type and then convert to a type argument.
+ env.resolver.getTypeArgument(
+ env.resolver.createKSTypeReferenceFromKSType(
+ resolveTypeArguments(argument.type!!.resolve(), resolvedTypeArguments)
+ ),
+ variance = Variance.INVARIANT
+ )
+ } else {
+ argument
+ }
+ }.toList()
+ )
}
override val typeElement by lazy {
@@ -136,7 +187,14 @@
}
override fun isError(): Boolean {
- return ksType.isError
+ // Avoid returning true if this type represents a java wildcard type, e.g. "? extends Foo"
+ // since in that case the wildcard type is not the error type itself. Instead, the error
+ // type should be on the XType#extendsBound() type, "Foo", instead.
+ return ksType.isError && !isJavaWildcardType()
+ }
+
+ private fun isJavaWildcardType(): Boolean {
+ return asTypeName().java is WildcardTypeName
}
override fun defaultValue(): String {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index 75517f7..7674249 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -45,7 +45,6 @@
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.Modifier
import com.squareup.javapoet.ClassName
-import com.squareup.javapoet.TypeName
import com.squareup.kotlinpoet.javapoet.JClassName
import com.squareup.kotlinpoet.javapoet.KClassName
@@ -89,17 +88,6 @@
)
}
- override val superTypes: List<XType> by lazy {
- buildList {
- if (isInterface() && superInterfaces.isEmpty()) {
- add(env.requireType(TypeName.OBJECT))
- } else {
- superClass?.let { add(it) }
- addAll(superInterfaces)
- }
- }
- }
-
override val superClass: XType? by lazy {
if (isInterface()) {
// interfaces don't have super classes (they do have super types)
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index 36836e4..b49b301 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -145,7 +145,7 @@
assertThat(it.superClass).isEqualTo(
invocation.processingEnv.requireType("foo.bar.AbstractClass")
)
- assertThat(it.superTypes).containsExactly(
+ assertThat(it.type.superTypes).containsExactly(
invocation.processingEnv.requireType("foo.bar.AbstractClass"),
invocation.processingEnv.requireType("foo.bar.MyInterface")
)
@@ -160,7 +160,7 @@
assertThat(it.superClass).isEqualTo(
invocation.processingEnv.requireType(JTypeName.OBJECT)
)
- assertThat(it.superTypes).containsExactly(
+ assertThat(it.type.superTypes).containsExactly(
invocation.processingEnv.requireType(JTypeName.OBJECT)
)
assertThat(it.isAbstract()).isTrue()
@@ -171,7 +171,7 @@
}
invocation.processingEnv.requireTypeElement("foo.bar.MyInterface").let {
assertThat(it.superClass).isNull()
- assertThat(it.superTypes).containsExactly(
+ assertThat(it.type.superTypes).containsExactly(
invocation.processingEnv.requireType(JTypeName.OBJECT)
)
assertThat(it.isInterface()).isTrue()
@@ -181,7 +181,8 @@
}
invocation.processingEnv.requireTypeElement("foo.bar.AnotherInterface").let {
assertThat(it.superClass).isNull()
- assertThat(it.superTypes).containsExactly(
+ assertThat(it.type.superTypes).containsExactly(
+ invocation.processingEnv.requireType("java.lang.Object"),
invocation.processingEnv.requireType("foo.bar.MyInterface")
)
assertThat(it.isInterface()).isTrue()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index b42a29c..4e5c8b2 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -37,6 +37,7 @@
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeVariableName
+import com.squareup.javapoet.WildcardTypeName
import com.squareup.kotlinpoet.INT
import com.squareup.kotlinpoet.MUTABLE_SET
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
@@ -1037,4 +1038,273 @@
""".trimIndent()
))) { it.checkPrimitiveType() }
}
+
+ @Test
+ fun setSuperTypeNames() {
+ fun superTypeHierarchy(type: XType, depth: Int = 0): String {
+ val sb: StringBuilder = StringBuilder()
+ sb.append("${" ".repeat(depth)}> ${type.typeName}")
+ type.superTypes.forEach {
+ sb.append("\n").append(superTypeHierarchy(it, depth + 1))
+ }
+ return sb.toString()
+ }
+
+ fun XTestInvocation.checkType() {
+ val fooElement = processingEnv.requireTypeElement("test.Foo")
+ val method1 = fooElement.getMethodByJvmName("method1")
+ val method2 = fooElement.getMethodByJvmName("method2")
+
+ // Check the return types of the unresolved methods
+ assertThat(method1.returnType.typeName).isEqualTo(TypeVariableName.get("T1"))
+ assertThat(method2.returnType.typeName).isEqualTo(TypeVariableName.get("T2"))
+
+ // Check the return types of the methods resolved into Usage
+ val usageElement = processingEnv.requireTypeElement("test.Usage")
+ assertThat(method1.asMemberOf(usageElement.type).returnType.typeName.toString())
+ .isEqualTo("test.Baz<java.lang.Long, java.lang.Number>")
+ assertThat(method2.asMemberOf(usageElement.type).returnType.typeName.toString())
+ .isEqualTo("java.lang.Integer")
+
+ // Check the supertypes of the unresolved Foo
+ assertThat(superTypeHierarchy(fooElement.type)).isEqualTo(
+ """
+ > test.Foo<V1, V2>
+ > java.lang.Object
+ > test.Bar<test.Baz<V1, java.lang.Number>, V2>
+ > java.lang.Object
+ > test.Baz<test.Baz<V1, java.lang.Number>, V2>
+ > java.lang.Object
+ """.trimIndent()
+ )
+
+ // Check the supertypes of Foo<Long, Integer>
+ assertThat(superTypeHierarchy(usageElement.type)).isEqualTo(
+ """
+ > test.Usage
+ > java.lang.Object
+ > test.Foo<java.lang.Long, java.lang.Integer>
+ > java.lang.Object
+ > test.Bar<test.Baz<java.lang.Long, java.lang.Number>, java.lang.Integer>
+ > java.lang.Object
+ > test.Baz<test.Baz<java.lang.Long, java.lang.Number>, java.lang.Integer>
+ > java.lang.Object
+ """.trimIndent()
+ )
+
+ // Check the supertypes of Foo<String, Integer>
+ val methodFoo = usageElement.getMethodByJvmName("foo")
+ assertThat(superTypeHierarchy(methodFoo.returnType)).isEqualTo(
+ """
+ > test.Foo<java.lang.String, java.lang.Integer>
+ > java.lang.Object
+ > test.Bar<test.Baz<java.lang.String, java.lang.Number>, java.lang.Integer>
+ > java.lang.Object
+ > test.Baz<test.Baz<java.lang.String, java.lang.Number>, java.lang.Integer>
+ > java.lang.Object
+ """.trimIndent()
+ )
+
+ // Check the supertypes of Foo<Double, Integer>
+ assertThat(superTypeHierarchy(methodFoo.parameters[0].type)).isEqualTo(
+ """
+ > test.Foo<java.lang.Double, java.lang.Integer>
+ > java.lang.Object
+ > test.Bar<test.Baz<java.lang.Double, java.lang.Number>, java.lang.Integer>
+ > java.lang.Object
+ > test.Baz<test.Baz<java.lang.Double, java.lang.Number>, java.lang.Integer>
+ > java.lang.Object
+ """.trimIndent()
+ )
+ }
+
+ runProcessorTest(listOf(Source.java(
+ "test.Usage",
+ """
+ package test;
+ interface Usage extends Foo<Long, Integer> {
+ Foo<String, Integer> foo(Foo<Double, Integer> param);
+ }
+ interface Foo<V1, V2 extends Integer> extends Bar<Baz<V1, Number>, V2> {}
+ interface Bar<U1, U2 extends Integer> extends Baz<U1, U2> {}
+ interface Baz<T1, T2 extends Number> {
+ T1 method1();
+ T2 method2();
+ }
+ """.trimIndent()
+ ))) { it.checkType() }
+
+ runProcessorTest(listOf(Source.kotlin(
+ "test.Usage.kt",
+ """
+ package test
+ interface Usage : Foo<Long, Integer> {
+ fun foo(param: Foo<Double, Integer>): Foo<String, Integer>
+ }
+ interface Foo<V1, V2: Integer> : Bar<Baz<V1, Number>, V2> {}
+ interface Bar<U1, U2: Integer> : Baz<U1, U2> {}
+ interface Baz<T1, T2: Number> {
+ fun method1(): T1
+ fun method2(): T2
+ }
+ """.trimIndent()
+ ))) { it.checkType() }
+ }
+
+ @Test
+ fun typeArgumentMissingType() {
+ class TypeArgumentProcessingStep : XProcessingStep {
+ override fun annotations() = setOf("test.Inspect")
+
+ override fun process(
+ env: XProcessingEnv,
+ elementsByAnnotation: Map<String, Set<XElement>>,
+ isLastRound: Boolean
+ ): Set<XElement> {
+ val barElement = env.requireTypeElement("test.Bar")
+ val missingTypeName = if (
+ env.backend == XProcessingEnv.Backend.KSP ||
+ // There's a bug in KAPT that doesn't replace NonExistentClass even when
+ // correctErrorTypes is enabled, so we account for that here.
+ // https://youtrack.jetbrains.com/issue/KT-34193/Kapt-CorrectErrorTypes-doesnt-work-for-generics
+ barElement.hasAnnotation(Metadata::class)
+ ) {
+ ClassName.get("error", "NonExistentClass")
+ } else {
+ ClassName.get("", "MissingType")
+ }
+ val barType = barElement.type
+ val fooTypeName = ParameterizedTypeName.get(
+ ClassName.get("test", "Foo"),
+ missingTypeName
+ )
+
+ val fooType = barType.superTypes.single()
+ assertThat(fooType.typeName).isEqualTo(fooTypeName)
+ assertThat(fooType.isError()).isFalse()
+
+ val typeArgument = fooType.typeArguments.single()
+ assertThat(typeArgument.typeName).isEqualTo(missingTypeName)
+ assertThat(typeArgument.isError()).isTrue()
+
+ return emptySet()
+ }
+ }
+
+ runProcessorTest(
+ sources = listOf(Source.java(
+ "test.Foo",
+ """
+ package test;
+ @Inspect
+ class Bar extends Foo<MissingType> {}
+ class Foo<T> {}
+ @interface Inspect {}
+ """.trimIndent()
+ )),
+ createProcessingStep = { TypeArgumentProcessingStep() }
+ ) { result ->
+ result.hasError()
+ result.hasErrorCount(1)
+ result.hasErrorContaining("cannot find symbol")
+ }
+
+ runProcessorTest(
+ sources = listOf(Source.kotlin(
+ "test.Foo.kt",
+ """
+ package test
+ class Bar : Foo<MissingType>()
+ open class Foo<T>
+ """.trimIndent()
+ )),
+ kotlincArguments = listOf(
+ "-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true"
+ ),
+ createProcessingStep = { TypeArgumentProcessingStep() }
+ ) { result ->
+ result.hasError()
+ result.hasErrorCount(1)
+ result.hasErrorContaining("Unresolved reference")
+ }
+ }
+
+ @Test
+ fun wildcardWithMissingType() {
+ class WildcardProcessingStep : XProcessingStep {
+ override fun annotations() = setOf("test.Inspect")
+
+ override fun process(
+ env: XProcessingEnv,
+ elementsByAnnotation: Map<String, Set<XElement>>,
+ isLastRound: Boolean
+ ): Set<XElement> {
+ val missingTypeName = if (env.backend == XProcessingEnv.Backend.KSP) {
+ ClassName.get("error", "NonExistentClass")
+ } else {
+ ClassName.get("", "MissingType")
+ }
+ val wildcardTypeName = WildcardTypeName.subtypeOf(missingTypeName)
+ val fooTypeName = ParameterizedTypeName.get(
+ ClassName.get("test", "Foo"),
+ wildcardTypeName
+ )
+
+ val fooElement = env.requireTypeElement("test.Foo")
+ val fooType = fooElement.getField("foo").type
+ assertThat(fooType.typeName).isEqualTo(fooTypeName)
+ assertThat(fooType.isError()).isFalse()
+
+ val wildcardType = fooType.typeArguments.single()
+ assertThat(wildcardType.typeName).isEqualTo(wildcardTypeName)
+ assertThat(wildcardType.isError()).isFalse()
+
+ assertThat(wildcardType.extendsBound()).isNotNull()
+ val errorType = wildcardType.extendsBound()!!
+ assertThat(errorType.typeName).isEqualTo(missingTypeName)
+ assertThat(errorType.isError()).isTrue()
+
+ return emptySet()
+ }
+ }
+
+ runProcessorTest(
+ sources = listOf(Source.java(
+ "test.Foo",
+ """
+ package test;
+ @Inspect
+ class Foo<T> {
+ Foo<? extends MissingType> foo;
+ }
+ @interface Inspect {}
+ """.trimIndent()
+ )),
+ createProcessingStep = { WildcardProcessingStep() }
+ ) { result ->
+ result.hasError()
+ result.hasErrorCount(1)
+ result.hasErrorContaining("cannot find symbol")
+ }
+
+ runProcessorTest(
+ sources = listOf(Source.kotlin(
+ "test.Foo.kt",
+ """
+ package test
+ class Foo<T> {
+ val foo: Foo<out MissingType> = TODO()
+ }
+ """.trimIndent()
+ )),
+ kotlincArguments = listOf(
+ "-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true"
+ ),
+ createProcessingStep = { WildcardProcessingStep() }
+ ) { result ->
+ result.hasError()
+ result.hasErrorCount(1)
+ result.hasErrorContaining("Unresolved reference")
+ }
+ }
}
diff --git a/settings.gradle b/settings.gradle
index 66f7240..3d2ec6a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -578,6 +578,8 @@
includeProject(":concurrent:concurrent-futures-ktx", [BuildType.MAIN, BuildType.CAMERA])
includeProject(":constraintlayout:constraintlayout-compose", [BuildType.COMPOSE])
includeProject(":constraintlayout:constraintlayout-compose:integration-tests:constraintlayout-compose-demos", [BuildType.COMPOSE])
+includeProject(":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark", [BuildType.COMPOSE])
+includeProject(":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark-target", [BuildType.COMPOSE])
includeProject(":constraintlayout:constraintlayout", [BuildType.MAIN])
includeProject(":constraintlayout:constraintlayout-core", [BuildType.MAIN, BuildType.COMPOSE])
includeProject(":contentpager:contentpager", [BuildType.MAIN])
@@ -657,6 +659,8 @@
includeProject(":glance:glance-appwidget-preview", [BuildType.GLANCE])
includeProject(":glance:glance-appwidget-proto", [BuildType.GLANCE])
includeProject(":glance:glance-appwidget:integration-tests:demos", [BuildType.GLANCE])
+includeProject(":glance:glance-appwidget:integration-tests:macrobenchmark", [BuildType.GLANCE])
+includeProject(":glance:glance-appwidget:integration-tests:macrobenchmark-target", [BuildType.GLANCE])
includeProject(":glance:glance-appwidget:integration-tests:template-demos", [BuildType.GLANCE])
includeProject(":glance:glance-appwidget:glance-layout-generator", [BuildType.GLANCE])
includeProject(":glance:glance-preview", [BuildType.GLANCE])
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiCollection.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiCollection.java
index 40b8b2a..5322d2a 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiCollection.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiCollection.java
@@ -50,7 +50,6 @@
@NonNull
public UiObject getChildByDescription(@NonNull UiSelector childPattern, @NonNull String text)
throws UiObjectNotFoundException {
- Tracer.trace(childPattern, text);
if (text != null) {
int count = getChildCount(childPattern);
for (int x = 0; x < count; x++) {
@@ -84,7 +83,6 @@
@NonNull
public UiObject getChildByInstance(@NonNull UiSelector childPattern, int instance)
throws UiObjectNotFoundException {
- Tracer.trace(childPattern, instance);
UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
UiSelector.patternBuilder(childPattern).instance(instance));
return new UiObject(patternSelector);
@@ -108,7 +106,6 @@
@NonNull
public UiObject getChildByText(@NonNull UiSelector childPattern, @NonNull String text)
throws UiObjectNotFoundException {
- Tracer.trace(childPattern, text);
if (text != null) {
int count = getChildCount(childPattern);
for (int x = 0; x < count; x++) {
@@ -137,7 +134,6 @@
* @return the number of matched childPattern under the current {@link UiCollection}
*/
public int getChildCount(@NonNull UiSelector childPattern) {
- Tracer.trace(childPattern);
UiSelector patternSelector =
UiSelector.patternBuilder(getSelector(), UiSelector.patternBuilder(childPattern));
return getQueryController().getPatternCount(patternSelector);
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
index 18177fe..e1c2e70 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
@@ -56,6 +56,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -69,8 +70,7 @@
* such as pressing the d-pad or pressing the Home and Menu buttons.
*/
public class UiDevice implements Searchable {
-
- private static final String LOG_TAG = UiDevice.class.getSimpleName();
+ private static final String TAG = UiDevice.class.getSimpleName();
// Use a short timeout after HOME or BACK key presses, as no events might be generated if
// already on the home page or if there is nothing to go back to.
@@ -165,6 +165,7 @@
* was not met before the {@code timeout}.
*/
public <U> U wait(@NonNull SearchCondition<U> condition, long timeout) {
+ Log.d(TAG, String.format("Waiting %dms for condition %s.", timeout, condition));
return mWaitMixin.wait(condition, timeout);
}
@@ -179,11 +180,14 @@
public <U> U performActionAndWait(@NonNull Runnable action,
@NonNull EventCondition<U> condition, long timeout) {
AccessibilityEvent event = null;
+ Log.d(TAG, String.format("Performing action %s and waiting %dms for condition %s.",
+ action, timeout, condition));
try {
event = getUiAutomation().executeAndWaitForEvent(
action, new EventForwardingFilter(condition), timeout);
} catch (TimeoutException e) {
// Ignore
+ Log.w(TAG, String.format("Timed out waiting %dms on the condition.", timeout));
}
if (event != null) {
@@ -263,7 +267,6 @@
*/
@NonNull
public Point getDisplaySizeDp() {
- Tracer.trace();
Display display = getDefaultDisplay();
Point p = new Point();
display.getRealSize(p);
@@ -286,7 +289,6 @@
*/
@NonNull
public String getProductName() {
- Tracer.trace();
return Build.PRODUCT;
}
@@ -306,7 +308,6 @@
*/
@SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getLastTraversedText() {
- Tracer.trace();
return getQueryController().getLastTraversedText();
}
@@ -315,7 +316,7 @@
* See {@link #getLastTraversedText()}.
*/
public void clearLastTraversedText() {
- Tracer.trace();
+ Log.d(TAG, "Clearing last traversed text.");
getQueryController().clearLastTraversedText();
}
@@ -324,8 +325,8 @@
* @return true if successful, else return false
*/
public boolean pressMenu() {
- Tracer.trace();
waitForIdle();
+ Log.d(TAG, "Pressing menu button.");
return getInteractionController().sendKeyAndWaitForEvent(
KeyEvent.KEYCODE_MENU, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
KEY_PRESS_EVENT_TIMEOUT);
@@ -336,8 +337,8 @@
* @return true if successful, else return false
*/
public boolean pressBack() {
- Tracer.trace();
waitForIdle();
+ Log.d(TAG, "Pressing back button.");
return getInteractionController().sendKeyAndWaitForEvent(
KeyEvent.KEYCODE_BACK, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
KEY_PRESS_EVENT_TIMEOUT);
@@ -348,8 +349,8 @@
* @return true if successful, else return false
*/
public boolean pressHome() {
- Tracer.trace();
waitForIdle();
+ Log.d(TAG, "Pressing home button.");
return getInteractionController().sendKeyAndWaitForEvent(
KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
KEY_PRESS_EVENT_TIMEOUT);
@@ -360,7 +361,6 @@
* @return true if successful, else return false
*/
public boolean pressSearch() {
- Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_SEARCH);
}
@@ -369,7 +369,6 @@
* @return true if successful, else return false
*/
public boolean pressDPadCenter() {
- Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER);
}
@@ -378,7 +377,6 @@
* @return true if successful, else return false
*/
public boolean pressDPadDown() {
- Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN);
}
@@ -387,7 +385,6 @@
* @return true if successful, else return false
*/
public boolean pressDPadUp() {
- Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_UP);
}
@@ -396,7 +393,6 @@
* @return true if successful, else return false
*/
public boolean pressDPadLeft() {
- Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_LEFT);
}
@@ -405,7 +401,6 @@
* @return true if successful, else return false
*/
public boolean pressDPadRight() {
- Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT);
}
@@ -414,7 +409,6 @@
* @return true if successful, else return false
*/
public boolean pressDelete() {
- Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_DEL);
}
@@ -423,7 +417,6 @@
* @return true if successful, else return false
*/
public boolean pressEnter() {
- Tracer.trace();
return pressKeyCode(KeyEvent.KEYCODE_ENTER);
}
@@ -434,8 +427,8 @@
* @return true if successful, else return false
*/
public boolean pressKeyCode(int keyCode) {
- Tracer.trace(keyCode);
waitForIdle();
+ Log.d(TAG, String.format("Pressing keycode %d.", keyCode));
return getInteractionController().sendKey(keyCode, 0);
}
@@ -448,8 +441,9 @@
* @return true if successful, else return false
*/
public boolean pressKeyCode(int keyCode, int metaState) {
- Tracer.trace(keyCode, metaState);
waitForIdle();
+ Log.d(TAG, String.format("Pressing keycode %d with modifier %d.", keyCode,
+ metaState));
return getInteractionController().sendKey(keyCode, metaState);
}
@@ -460,8 +454,8 @@
* @throws RemoteException
*/
public boolean pressRecentApps() throws RemoteException {
- Tracer.trace();
waitForIdle();
+ Log.d(TAG, "Pressing recent apps button.");
return getInteractionController().toggleRecentApps();
}
@@ -471,8 +465,8 @@
* @return true if successful, else return false
*/
public boolean openNotification() {
- Tracer.trace();
waitForIdle();
+ Log.d(TAG, "Opening notification.");
return getInteractionController().openNotification();
}
@@ -482,8 +476,8 @@
* @return true if successful, else return false
*/
public boolean openQuickSettings() {
- Tracer.trace();
waitForIdle();
+ Log.d(TAG, "Opening quick settings.");
return getInteractionController().openQuickSettings();
}
@@ -493,7 +487,6 @@
* @return width in pixels or zero on failure
*/
public int getDisplayWidth() {
- Tracer.trace();
Display display = getDefaultDisplay();
Point p = new Point();
display.getRealSize(p);
@@ -506,7 +499,6 @@
* @return height in pixels or zero on failure
*/
public int getDisplayHeight() {
- Tracer.trace();
Display display = getDefaultDisplay();
Point p = new Point();
display.getRealSize(p);
@@ -521,10 +513,12 @@
* @return true if the click succeeded else false
*/
public boolean click(int x, int y) {
- Tracer.trace(x, y);
if (x >= getDisplayWidth() || y >= getDisplayHeight()) {
+ Log.w(TAG, String.format("Cannot click. Point (%d, %d) is outside display (%d, %d).",
+ x, y, getDisplayWidth(), getDisplayHeight()));
return false;
}
+ Log.d(TAG, String.format("Clicking on (%d, %d).", x, y));
return getInteractionController().clickNoSync(x, y);
}
@@ -541,7 +535,8 @@
* @return false if the operation fails or the coordinates are invalid
*/
public boolean swipe(int startX, int startY, int endX, int endY, int steps) {
- Tracer.trace(startX, startY, endX, endY, steps);
+ Log.d(TAG, String.format("Swiping from (%d, %d) to (%d, %d) in %d steps.", startX, startY,
+ endX, endY, steps));
return getInteractionController()
.swipe(startX, startY, endX, endY, steps);
}
@@ -561,7 +556,8 @@
* or the coordinates are invalid
*/
public boolean drag(int startX, int startY, int endX, int endY, int steps) {
- Tracer.trace(startX, startY, endX, endY, steps);
+ Log.d(TAG, String.format("Dragging from (%d, %d) to (%d, %d) in %d steps.", startX, startY,
+ endX, endY, steps));
return getInteractionController()
.swipe(startX, startY, endX, endY, steps, true);
}
@@ -575,7 +571,8 @@
* @return true on success
*/
public boolean swipe(@NonNull Point[] segments, int segmentSteps) {
- Tracer.trace(segments, segmentSteps);
+ Log.d(TAG, String.format("Swiping between %s in %d steps.", Arrays.toString(segments),
+ segmentSteps * (segments.length - 1)));
return getInteractionController().swipe(segments, segmentSteps);
}
@@ -584,7 +581,6 @@
* Default wait timeout is 10 seconds
*/
public void waitForIdle() {
- Tracer.trace();
getQueryController().waitForIdle();
}
@@ -593,7 +589,6 @@
* @param timeout in milliseconds
*/
public void waitForIdle(long timeout) {
- Tracer.trace(timeout);
getQueryController().waitForIdle(timeout);
}
@@ -605,7 +600,6 @@
@Deprecated
@SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getCurrentActivityName() {
- Tracer.trace();
return getQueryController().getCurrentActivityName();
}
@@ -615,7 +609,6 @@
*/
@SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
public String getCurrentPackageName() {
- Tracer.trace();
return getQueryController().getCurrentPackageName();
}
@@ -627,7 +620,7 @@
* @param watcher {@link UiWatcher}
*/
public void registerWatcher(@Nullable String name, @Nullable UiWatcher watcher) {
- Tracer.trace(name, watcher);
+ Log.d(TAG, String.format("Registering watcher %s.", name));
if (mInWatcherContext) {
throw new IllegalStateException("Cannot register new watcher from within another");
}
@@ -641,7 +634,7 @@
* @param name used to register the UiWatcher
*/
public void removeWatcher(@Nullable String name) {
- Tracer.trace(name);
+ Log.d(TAG, String.format("Removing watcher %s.", name));
if (mInWatcherContext) {
throw new IllegalStateException("Cannot remove a watcher from within another");
}
@@ -653,7 +646,6 @@
* See {@link #registerWatcher(String, UiWatcher)}
*/
public void runWatchers() {
- Tracer.trace();
if (mInWatcherContext) {
return;
}
@@ -667,7 +659,7 @@
setWatcherTriggered(watcherName);
}
} catch (Exception e) {
- Log.e(LOG_TAG, "Exceuting watcher: " + watcherName, e);
+ Log.e(TAG, String.format("Failed to execute watcher %s.", watcherName), e);
} finally {
mInWatcherContext = false;
}
@@ -682,7 +674,7 @@
* See {@link #registerWatcher(String, UiWatcher)}
*/
public void resetWatcherTriggers() {
- Tracer.trace();
+ Log.d(TAG, "Resetting all watchers.");
mWatchersTriggers.clear();
}
@@ -697,7 +689,6 @@
* @return true if triggered else false
*/
public boolean hasWatcherTriggered(@Nullable String watcherName) {
- Tracer.trace(watcherName);
return mWatchersTriggers.contains(watcherName);
}
@@ -708,7 +699,6 @@
* See {@link #hasWatcherTriggered(String)}
*/
public boolean hasAnyWatcherTriggered() {
- Tracer.trace();
return mWatchersTriggers.size() > 0;
}
@@ -717,7 +707,6 @@
* @param watcherName
*/
private void setWatcherTriggered(String watcherName) {
- Tracer.trace(watcherName);
if (!hasWatcherTriggered(watcherName)) {
mWatchersTriggers.add(watcherName);
}
@@ -729,7 +718,6 @@
* @return true if it is in natural orientation
*/
public boolean isNaturalOrientation() {
- Tracer.trace();
waitForIdle();
int ret = getDisplayRotation();
return ret == UiAutomation.ROTATION_FREEZE_0 ||
@@ -740,7 +728,6 @@
* Returns the current rotation of the display, as defined in {@link Surface}
*/
public int getDisplayRotation() {
- Tracer.trace();
waitForIdle();
return getDefaultDisplay().getRotation();
}
@@ -751,7 +738,7 @@
* @throws RemoteException
*/
public void freezeRotation() throws RemoteException {
- Tracer.trace();
+ Log.d(TAG, "Freezing rotation.");
getInteractionController().freezeRotation();
}
@@ -762,7 +749,7 @@
* @throws RemoteException
*/
public void unfreezeRotation() throws RemoteException {
- Tracer.trace();
+ Log.d(TAG, "Unfreezing rotation.");
getInteractionController().unfreezeRotation();
}
@@ -775,7 +762,7 @@
* @throws RemoteException
*/
public void setOrientationLeft() throws RemoteException {
- Tracer.trace();
+ Log.d(TAG, "Setting orientation to left.");
getInteractionController().setRotationLeft();
waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit
}
@@ -789,7 +776,7 @@
* @throws RemoteException
*/
public void setOrientationRight() throws RemoteException {
- Tracer.trace();
+ Log.d(TAG, "Setting orientation to right.");
getInteractionController().setRotationRight();
waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit
}
@@ -803,7 +790,7 @@
* @throws RemoteException
*/
public void setOrientationNatural() throws RemoteException {
- Tracer.trace();
+ Log.d(TAG, "Setting orientation to natural.");
getInteractionController().setRotationNatural();
waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit
}
@@ -817,7 +804,7 @@
* @throws RemoteException
*/
public void wakeUp() throws RemoteException {
- Tracer.trace();
+ Log.d(TAG, "Turning on screen.");
if(getInteractionController().wakeDevice()) {
// sync delay to allow the window manager to start accepting input
// after the device is awakened.
@@ -832,7 +819,6 @@
* @throws RemoteException
*/
public boolean isScreenOn() throws RemoteException {
- Tracer.trace();
return getInteractionController().isScreenOn();
}
@@ -843,7 +829,7 @@
* @throws RemoteException
*/
public void sleep() throws RemoteException {
- Tracer.trace();
+ Log.d(TAG, "Turning off screen.");
getInteractionController().sleepDevice();
}
@@ -857,7 +843,6 @@
*/
@Deprecated
public void dumpWindowHierarchy(@NonNull String fileName) {
- Tracer.trace(fileName);
File dumpFile = new File(fileName);
if (!dumpFile.isAbsolute()) {
@@ -906,9 +891,10 @@
* window does not have the specified package name
*/
public boolean waitForWindowUpdate(@Nullable String packageName, long timeout) {
- Tracer.trace(packageName, timeout);
if (packageName != null) {
if (!packageName.equals(getCurrentPackageName())) {
+ Log.w(TAG, String.format("Skipping wait as package %s does not match current "
+ + "window %s.", packageName, getCurrentPackageName()));
return false;
}
}
@@ -927,12 +913,15 @@
return false;
}
};
+ Log.d(TAG, String.format("Waiting %dms for window update of package %s.", timeout,
+ packageName));
try {
getUiAutomation().executeAndWaitForEvent(emptyRunnable, checkWindowUpdate, timeout);
} catch (TimeoutException e) {
+ Log.w(TAG, String.format("Timed out waiting %dms on window update.", timeout));
return false;
} catch (Exception e) {
- Log.e(LOG_TAG, "waitForWindowUpdate: general exception from bridge", e);
+ Log.e(TAG, "Failed to wait for window update.", e);
return false;
}
return true;
@@ -948,7 +937,6 @@
* @return true if screen shot is created successfully, false otherwise
*/
public boolean takeScreenshot(@NonNull File storePath) {
- Tracer.trace(storePath);
return takeScreenshot(storePath, 1.0f, 90);
}
@@ -963,9 +951,11 @@
* @return true if screen shot is created successfully, false otherwise
*/
public boolean takeScreenshot(@NonNull File storePath, float scale, int quality) {
- Tracer.trace(storePath, scale, quality);
+ Log.d(TAG, String.format("Taking screenshot (scale=%f, quality=%d) and storing at %s.",
+ scale, quality, storePath));
Bitmap screenshot = getUiAutomation().takeScreenshot();
if (screenshot == null) {
+ Log.w(TAG, "Failed to take screenshot.");
return false;
}
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(storePath))) {
@@ -976,7 +966,7 @@
bos.flush();
return true;
} catch (IOException ioe) {
- Log.e(LOG_TAG, "failed to save screen shot to file", ioe);
+ Log.e(TAG, "Failed to save screenshot.", ioe);
return false;
} finally {
screenshot.recycle();
@@ -1022,6 +1012,7 @@
@RequiresApi(21)
@NonNull
public String executeShellCommand(@NonNull String cmd) throws IOException {
+ Log.d(TAG, String.format("Executing shell command: %s", cmd));
try (ParcelFileDescriptor pfd = Api21Impl.executeShellCommand(getUiAutomation(), cmd);
FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
byte[] buf = new byte[512];
@@ -1073,7 +1064,7 @@
for (final AccessibilityWindowInfo window : getWindows()) {
final AccessibilityNodeInfo root = Api21Impl.getRoot(window);
if (root == null) {
- Log.w(LOG_TAG, "Skipping null root node for window: " + window);
+ Log.w(TAG, "Skipping null root node for window: " + window);
continue;
}
roots.add(root);
@@ -1107,7 +1098,7 @@
uiAutomation = Api24Impl.getUiAutomation(getInstrumentation(), flags);
} else {
if (flags != Configurator.DEFAULT_UIAUTOMATION_FLAGS) {
- Log.w(LOG_TAG, "UiAutomation flags not supported prior to API 24");
+ Log.w(TAG, "UiAutomation flags not supported prior to API 24");
}
uiAutomation = getInstrumentation().getUiAutomation();
}
@@ -1126,7 +1117,7 @@
} else {
serviceInfo.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
}
- Log.d(LOG_TAG,
+ Log.d(TAG,
String.format("Setting accessibility service flags: %d", serviceInfo.flags));
uiAutomation.setServiceInfo(serviceInfo);
mCachedServiceFlags = serviceInfo.flags;
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
index db7e3d3..974fb8f 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
@@ -1121,7 +1121,7 @@
* <code>false</code> otherwise
*/
public boolean performMultiPointerGesture(@NonNull PointerCoords[]... touches) {
- Log.d(TAG, String.format("Performing multi-point gesture %s", touchesToString(touches)));
+ Log.d(TAG, String.format("Performing multi-point gesture %s.", touchesToString(touches)));
return getInteractionController().performMultiPointerGesture(touches);
}
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
index 54dd5e5..cb83ac0ab 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
@@ -153,6 +153,7 @@
* condition} was not met before the {@code timeout}.
*/
public <U> U wait(@NonNull UiObject2Condition<U> condition, long timeout) {
+ Log.d(TAG, String.format("Waiting %dms for condition %s.", timeout, condition));
return mWaitMixin.wait(condition, timeout);
}
@@ -165,6 +166,7 @@
* condition} was not met before the {@code timeout}.
*/
public <U> U wait(@NonNull SearchCondition<U> condition, long timeout) {
+ Log.d(TAG, String.format("Waiting %dms for condition %s.", timeout, condition));
return mWaitMixin.wait(condition, timeout);
}
@@ -450,7 +452,7 @@
/** Clicks on this object's center. */
public void click() {
Point center = getVisibleCenter();
- Log.v(TAG, String.format("click(center=%s)", center));
+ Log.d(TAG, String.format("Clicking on (%d, %d).", center.x, center.y));
mGestureController.performGesture(Gestures.click(center, getDisplayId()));
}
@@ -461,14 +463,14 @@
*/
public void click(@NonNull Point point) {
clipToGestureBounds(point);
- Log.v(TAG, String.format("click(point=%s)", point));
+ Log.d(TAG, String.format("Clicking on (%d, %d).", point.x, point.y));
mGestureController.performGesture(Gestures.click(point, getDisplayId()));
}
/** Clicks on this object's center for {@code duration} milliseconds. */
public void click(long duration) {
Point center = getVisibleCenter();
- Log.v(TAG, String.format("click(center=%s,duration=%d)", center, duration));
+ Log.d(TAG, String.format("Clicking on (%d, %d) for %dms.", center.x, center.y, duration));
mGestureController.performGesture(Gestures.click(center, duration, getDisplayId()));
}
@@ -480,7 +482,7 @@
*/
public void click(@NonNull Point point, long duration) {
clipToGestureBounds(point);
- Log.v(TAG, String.format("click(point=%s,duration=%d)", point, duration));
+ Log.d(TAG, String.format("Clicking on (%d, %d) for %dms.", point.x, point.y, duration));
mGestureController.performGesture(Gestures.click(point, duration, getDisplayId()));
}
@@ -492,7 +494,8 @@
*/
public <U> U clickAndWait(@NonNull EventCondition<U> condition, long timeout) {
Point center = getVisibleCenter();
- Log.v(TAG, String.format("clickAndWait(center=%s,timeout=%d)", center, timeout));
+ Log.d(TAG, String.format("Clicking on (%d, %d) and waiting %dms for condition %s.",
+ center.x, center.y, timeout, condition));
return mGestureController.performGestureAndWait(condition, timeout,
Gestures.click(center, getDisplayId()));
}
@@ -508,7 +511,8 @@
public <U> U clickAndWait(@NonNull Point point, @NonNull EventCondition<U> condition,
long timeout) {
clipToGestureBounds(point);
- Log.v(TAG, String.format("clickAndWait(point=%s,timeout=%d)", point, timeout));
+ Log.d(TAG, String.format("Clicking on (%d, %d) and waiting %dms for condition %s.",
+ point.x, point.y, timeout, condition));
return mGestureController.performGestureAndWait(
condition, timeout, Gestures.click(point, getDisplayId()));
}
@@ -533,14 +537,15 @@
throw new IllegalArgumentException("Speed cannot be negative");
}
Point center = getVisibleCenter();
- Log.v(TAG, String.format("drag(start=%s,dest=%s,speed=%d)", center, dest, speed));
+ Log.d(TAG, String.format("Dragging from (%d, %d) to (%d, %d) at %dpx/s.", center.x,
+ center.y, dest.x, dest.y, speed));
mGestureController.performGesture(Gestures.drag(center, dest, speed, getDisplayId()));
}
/** Performs a long click on this object's center. */
public void longClick() {
Point center = getVisibleCenter();
- Log.v(TAG, String.format("longClick(center=%s)", center));
+ Log.d(TAG, String.format("Long-clicking on (%d, %d).", center.x, center.y));
mGestureController.performGesture(Gestures.longClick(center, getDisplayId()));
}
@@ -567,8 +572,8 @@
throw new IllegalArgumentException("Speed cannot be negative");
}
Rect bounds = getVisibleBoundsForGestures();
- Log.v(TAG, String.format("pinchClose(bounds=%s,percent=%f,speed=%d)",
- bounds, percent, speed));
+ Log.d(TAG, String.format("Pinching close (bounds=%s, percent=%f) at %dpx/s.", bounds,
+ percent, speed));
mGestureController.performGesture(
Gestures.pinchClose(bounds, percent, speed, getDisplayId()));
}
@@ -596,8 +601,8 @@
throw new IllegalArgumentException("Speed cannot be negative");
}
Rect bounds = getVisibleBoundsForGestures();
- Log.v(TAG, String.format("pinchOpen(bounds=%s,percent=%f,speed=%d)",
- bounds, percent, speed));
+ Log.d(TAG, String.format("Pinching open (bounds=%s, percent=%f) at %dpx/s.", bounds,
+ percent, speed));
mGestureController.performGesture(
Gestures.pinchOpen(bounds, percent, speed, getDisplayId()));
}
@@ -627,8 +632,8 @@
throw new IllegalArgumentException("Speed cannot be negative");
}
Rect bounds = getVisibleBoundsForGestures();
- Log.v(TAG, String.format("swipe(bounds=%s,direction=%s,percent=%f,speed=%d)",
- bounds, direction, percent, speed));
+ Log.d(TAG, String.format("Swiping %s (bounds=%s, percent=%f) at %dpx/s.",
+ direction.name().toLowerCase(), bounds, percent, speed));
mGestureController.performGesture(
Gestures.swipeRect(bounds, direction, percent, speed, getDisplayId()));
}
@@ -665,8 +670,8 @@
// Scroll by performing repeated swipes
Rect bounds = getVisibleBoundsForGestures();
- Log.v(TAG, String.format("scroll(bounds=%s,direction=%s,percent=%f,speed=%d)",
- direction, bounds, percent, speed));
+ Log.d(TAG, String.format("Scrolling %s (bounds=%s, percent=%f) at %dpx/s.",
+ direction.name().toLowerCase(), bounds, percent, speed));
for (; percent > 0.0f; percent -= 1.0f) {
float segment = Math.min(percent, 1.0f);
PointerGesture swipe = Gestures.swipeRect(
@@ -709,12 +714,12 @@
final Direction swipeDirection = Direction.reverse(direction);
Rect bounds = getVisibleBoundsForGestures();
- Log.v(TAG, String.format("fling(bounds=%s,direction=%s,speed=%d)",
- bounds, direction, speed));
PointerGesture swipe = Gestures.swipeRect(
bounds, swipeDirection, 1.0f, speed, getDisplayId());
// Perform the gesture and return true if we did not reach the end
+ Log.d(TAG, String.format("Flinging %s (bounds=%s) at %dpx/s.",
+ direction.name().toLowerCase(), bounds, speed));
return !mGestureController.performGestureAndWait(
Until.scrollFinished(direction), FLING_TIMEOUT, swipe);
}
@@ -732,6 +737,7 @@
text = "";
}
+ Log.d(TAG, String.format("Setting text to '%s'.", text));
CharSequence currentText = node.getText();
if (currentText == null || !text.contentEquals(currentText)) {
InteractionController ic = getDevice().getInteractionController();
@@ -760,8 +766,8 @@
if (text == null) {
text = "";
}
- Log.v(TAG, String.format("setText(text=\"%s\")", text));
+ Log.d(TAG, String.format("Setting text to '%s'.", text));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// ACTION_SET_TEXT is added in API 21.
Bundle args = new Bundle();
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java
index c60e8c0..8dcfbf5 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiScrollable.java
@@ -28,7 +28,7 @@
* horizontally or vertically scrollable controls.
*/
public class UiScrollable extends UiCollection {
- private static final String LOG_TAG = UiScrollable.class.getSimpleName();
+ private static final String TAG = UiScrollable.class.getSimpleName();
// More steps slows the swipe and prevents contents from being flung too far
private static final int SCROLL_STEPS = 55;
@@ -64,7 +64,6 @@
*/
@NonNull
public UiScrollable setAsVerticalList() {
- Tracer.trace();
mIsVerticalList = true;
return this;
}
@@ -75,7 +74,6 @@
*/
@NonNull
public UiScrollable setAsHorizontalList() {
- Tracer.trace();
mIsVerticalList = false;
return this;
}
@@ -115,7 +113,6 @@
public UiObject getChildByDescription(
@NonNull UiSelector childPattern, @NonNull String text)
throws UiObjectNotFoundException {
- Tracer.trace(childPattern, text);
return getChildByDescription(childPattern, text, true);
}
@@ -137,7 +134,6 @@
@NonNull
public UiObject getChildByDescription(@NonNull UiSelector childPattern, @NonNull String text,
boolean allowScrollSearch) throws UiObjectNotFoundException {
- Tracer.trace(childPattern, text, allowScrollSearch);
if (text != null) {
if (allowScrollSearch) {
scrollIntoView(new UiSelector().descriptionContains(text));
@@ -161,7 +157,6 @@
@Override
public UiObject getChildByInstance(@NonNull UiSelector childPattern, int instance)
throws UiObjectNotFoundException {
- Tracer.trace(childPattern, instance);
UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
UiSelector.patternBuilder(childPattern).instance(instance));
return new UiObject(patternSelector);
@@ -186,7 +181,6 @@
@Override
public UiObject getChildByText(@NonNull UiSelector childPattern, @NonNull String text)
throws UiObjectNotFoundException {
- Tracer.trace(childPattern, text);
return getChildByText(childPattern, text, true);
}
@@ -208,7 +202,6 @@
public UiObject getChildByText(@NonNull UiSelector childPattern,
@NonNull String text,
boolean allowScrollSearch) throws UiObjectNotFoundException {
- Tracer.trace(childPattern, text, allowScrollSearch);
if (text != null) {
if (allowScrollSearch) {
scrollIntoView(new UiSelector().text(text));
@@ -229,7 +222,6 @@
*/
public boolean scrollDescriptionIntoView(@NonNull String text)
throws UiObjectNotFoundException {
- Tracer.trace(text);
return scrollIntoView(new UiSelector().description(text));
}
@@ -241,7 +233,6 @@
* @return true if the item was found and now is in view else false
*/
public boolean scrollIntoView(@NonNull UiObject obj) throws UiObjectNotFoundException {
- Tracer.trace(obj.getSelector());
return scrollIntoView(obj.getSelector());
}
@@ -255,7 +246,7 @@
* @return true if the item was found and now is in view; else, false
*/
public boolean scrollIntoView(@NonNull UiSelector selector) throws UiObjectNotFoundException {
- Tracer.trace(selector);
+ Log.d(TAG, String.format("Scrolling %s into view.", selector));
// if we happen to be on top of the text we want then return here
UiSelector childSelector = getSelector().childSelector(selector);
if (exists(childSelector)) {
@@ -292,6 +283,7 @@
*/
public boolean ensureFullyVisible(@NonNull UiObject childObject)
throws UiObjectNotFoundException {
+ Log.d(TAG, String.format("Ensuring %s is fully visible.", childObject.getSelector()));
Rect actual = childObject.getBounds();
Rect visible = childObject.getVisibleBounds();
if (visible.width() * visible.height() == actual.width() * actual.height()) {
@@ -332,7 +324,6 @@
* @return true if item is found; else, false
*/
public boolean scrollTextIntoView(@NonNull String text) throws UiObjectNotFoundException {
- Tracer.trace(text);
return scrollIntoView(new UiSelector().text(text));
}
@@ -347,7 +338,6 @@
*/
@NonNull
public UiScrollable setMaxSearchSwipes(int swipes) {
- Tracer.trace(swipes);
mMaxSearchSwipes = swipes;
return this;
}
@@ -361,7 +351,6 @@
* @return max the number of search swipes to perform until giving up
*/
public int getMaxSearchSwipes() {
- Tracer.trace();
return mMaxSearchSwipes;
}
@@ -376,7 +365,6 @@
* @return true if scrolled, false if can't scroll anymore
*/
public boolean flingForward() throws UiObjectNotFoundException {
- Tracer.trace();
return scrollForward(FLING_STEPS);
}
@@ -391,7 +379,6 @@
* @return true if scrolled, false if can't scroll anymore
*/
public boolean scrollForward() throws UiObjectNotFoundException {
- Tracer.trace();
return scrollForward(SCROLL_STEPS);
}
@@ -406,8 +393,6 @@
* @return true if scrolled, false if can't scroll anymore
*/
public boolean scrollForward(int steps) throws UiObjectNotFoundException {
- Tracer.trace(steps);
- Log.d(LOG_TAG, "scrollForward() on selector = " + getSelector());
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if(node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -438,6 +423,8 @@
upX = rect.left + swipeAreaAdjust;
upY = rect.centerY();
}
+ Log.d(TAG, String.format("Scrolling forward from (%d, %d) to (%d, %d) in %d steps.", downX,
+ downY, upX, upY, steps));
return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
}
@@ -452,7 +439,6 @@
* @return true if scrolled, and false if can't scroll anymore
*/
public boolean flingBackward() throws UiObjectNotFoundException {
- Tracer.trace();
return scrollBackward(FLING_STEPS);
}
@@ -467,7 +453,6 @@
* @return true if scrolled, and false if can't scroll anymore
*/
public boolean scrollBackward() throws UiObjectNotFoundException {
- Tracer.trace();
return scrollBackward(SCROLL_STEPS);
}
@@ -482,8 +467,6 @@
* @return true if scrolled, false if can't scroll anymore
*/
public boolean scrollBackward(int steps) throws UiObjectNotFoundException {
- Tracer.trace(steps);
- Log.d(LOG_TAG, "scrollBackward() on selector = " + getSelector());
AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
if (node == null) {
throw new UiObjectNotFoundException(getSelector().toString());
@@ -500,7 +483,6 @@
// set otherwise by setAsHorizontalContainer()
if(mIsVerticalList) {
int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
- Log.d(LOG_TAG, "scrollToBeginning() using vertical scroll");
// scroll vertically: swipe up -> down
downX = rect.centerX();
downY = rect.top + swipeAreaAdjust;
@@ -508,7 +490,6 @@
upY = rect.bottom - swipeAreaAdjust;
} else {
int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
- Log.d(LOG_TAG, "scrollToBeginning() using hotizontal scroll");
// scroll horizontally: swipe left -> right
// TODO: Assuming device is not in right to left language
downX = rect.left + swipeAreaAdjust;
@@ -516,6 +497,8 @@
upX = rect.right - swipeAreaAdjust;
upY = rect.centerY();
}
+ Log.d(TAG, String.format("Scrolling backward from (%d, %d) to (%d, %d) in %d steps.", downX,
+ downY, upX, upY, steps));
return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
}
@@ -529,8 +512,6 @@
* @return true on scrolled else false
*/
public boolean scrollToBeginning(int maxSwipes, int steps) throws UiObjectNotFoundException {
- Tracer.trace(maxSwipes, steps);
- Log.d(LOG_TAG, "scrollToBeginning() on selector = " + getSelector());
// protect against potential hanging and return after preset attempts
for(int x = 0; x < maxSwipes; x++) {
if(!scrollBackward(steps)) {
@@ -550,7 +531,6 @@
* @return true on scrolled else false
*/
public boolean scrollToBeginning(int maxSwipes) throws UiObjectNotFoundException {
- Tracer.trace(maxSwipes);
return scrollToBeginning(maxSwipes, SCROLL_STEPS);
}
@@ -564,7 +544,6 @@
* @return true on scrolled else false
*/
public boolean flingToBeginning(int maxSwipes) throws UiObjectNotFoundException {
- Tracer.trace(maxSwipes);
return scrollToBeginning(maxSwipes, FLING_STEPS);
}
@@ -578,7 +557,6 @@
* @return true on scrolled else false
*/
public boolean scrollToEnd(int maxSwipes, int steps) throws UiObjectNotFoundException {
- Tracer.trace(maxSwipes, steps);
// protect against potential hanging and return after preset attempts
for(int x = 0; x < maxSwipes; x++) {
if(!scrollForward(steps)) {
@@ -598,7 +576,6 @@
* @return true on scrolled, else false
*/
public boolean scrollToEnd(int maxSwipes) throws UiObjectNotFoundException {
- Tracer.trace(maxSwipes);
return scrollToEnd(maxSwipes, SCROLL_STEPS);
}
@@ -612,7 +589,6 @@
* @return true on scrolled, else false
*/
public boolean flingToEnd(int maxSwipes) throws UiObjectNotFoundException {
- Tracer.trace(maxSwipes);
return scrollToEnd(maxSwipes, FLING_STEPS);
}
@@ -627,7 +603,6 @@
* @return a value between 0 and 1
*/
public double getSwipeDeadZonePercentage() {
- Tracer.trace();
return mSwipeDeadZonePercentage;
}
@@ -645,7 +620,6 @@
*/
@NonNull
public UiScrollable setSwipeDeadZonePercentage(double swipeDeadZonePercentage) {
- Tracer.trace(swipeDeadZonePercentage);
mSwipeDeadZonePercentage = swipeDeadZonePercentage;
return this;
}
diff --git a/tracing/tracing-perfetto/build.gradle b/tracing/tracing-perfetto/build.gradle
index ca9af2c..f69a48a 100644
--- a/tracing/tracing-perfetto/build.gradle
+++ b/tracing/tracing-perfetto/build.gradle
@@ -67,5 +67,8 @@
}
android {
+ buildTypes.all {
+ consumerProguardFiles "proguard-rules.pro"
+ }
namespace "androidx.tracing.perfetto"
}
diff --git a/tracing/tracing-perfetto/proguard-rules.pro b/tracing/tracing-perfetto/proguard-rules.pro
new file mode 100644
index 0000000..526c4f3
--- /dev/null
+++ b/tracing/tracing-perfetto/proguard-rules.pro
@@ -0,0 +1,7 @@
+# Preserve class methods (that we explicitly register in native code), if the class is preserved
+-keepclassmembers class androidx.tracing.perfetto.jni.PerfettoNative {
+ java.lang.String nativeVersion();
+ void nativeRegisterWithPerfetto();
+ void nativeTraceEventBegin(int, java.lang.String);
+ void nativeTraceEventEnd();
+}
\ No newline at end of file
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
deleted file mode 100644
index 5c954de..0000000
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
+++ /dev/null
@@ -1,2553 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.wearable.complications;
-
-import android.annotation.SuppressLint;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.os.BadParcelableException;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
-import androidx.wear.watchface.complications.data.ComplicationDisplayPolicies;
-import androidx.wear.watchface.complications.data.ComplicationDisplayPolicy;
-import androidx.wear.watchface.complications.data.ComplicationPersistencePolicies;
-import androidx.wear.watchface.complications.data.ComplicationPersistencePolicy;
-
-import java.io.IOException;
-import java.io.InvalidObjectException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Container for complication data of all types.
- *
- * <p>A {@link androidx.wear.watchface.complications.ComplicationProviderService} should create
- * instances of
- * this class using {@link ComplicationData.Builder} and send them to the complication system in
- * response to
- * {@link androidx.wear.watchface.complications.ComplicationProviderService#onComplicationRequest}.
- * Depending on the type of complication data, some fields will be required and some will be
- * optional - see the documentation for each type, and for the builder's set methods, for details.
- *
- * <p>A watch face will receive instances of this class as long as providers are configured.
- *
- * <p>When rendering the complication data for a given time, the watch face should first call {@link
- * #isActiveAt} to determine whether the data is valid at that time. See the documentation for each
- * of the complication types below for details of which fields are expected to be displayed.
- *
- * @hide
- */
-@SuppressLint("BanParcelableUsage")
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class ComplicationData implements Parcelable, Serializable {
-
- private static final String TAG = "ComplicationData";
- public static final String PLACEHOLDER_STRING = "__placeholder__";
-
- /** @hide */
- @IntDef({
- TYPE_EMPTY,
- TYPE_NOT_CONFIGURED,
- TYPE_SHORT_TEXT,
- TYPE_LONG_TEXT,
- TYPE_RANGED_VALUE,
- TYPE_ICON,
- TYPE_SMALL_IMAGE,
- TYPE_LARGE_IMAGE,
- TYPE_NO_PERMISSION,
- TYPE_NO_DATA,
- TYPE_GOAL_PROGRESS,
- TYPE_WEIGHTED_ELEMENTS,
- EXP_TYPE_PROTO_LAYOUT,
- EXP_TYPE_LIST
- })
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- @Retention(RetentionPolicy.SOURCE)
- public @interface ComplicationType {
- }
-
- /**
- * Type sent when a complication does not have a provider configured. The system will send data
- * of this type to watch faces when the user has not chosen a provider for an active
- * complication, and the watch face has not set a default provider. Providers cannot send data
- * of this type.
- *
- * <p>No fields may be populated for complication data of this type.
- */
- public static final int TYPE_NOT_CONFIGURED = 1;
-
- /**
- * Type sent when the user has specified that an active complication should have no provider,
- * i.e. when the user has chosen "Empty" in the provider chooser. Providers cannot send data of
- * this type.
- *
- * <p>No fields may be populated for complication data of this type.
- */
- public static final int TYPE_EMPTY = 2;
-
- /**
- * Type that can be sent by any provider, regardless of the configured type, when the provider
- * has no data to be displayed. Watch faces may choose whether to render this in some way or
- * leave the slot empty.
- *
- * <p>No fields may be populated for complication data of this type.
- */
- public static final int TYPE_NO_DATA = 10;
-
- /**
- * Type used for complications where the primary piece of data is a short piece of text
- * (expected to be no more than seven characters in length). The short text may be accompanied
- * by an icon or a short title (or both, but if both are provided then a watch face may choose
- * to display only one).
- *
- * <p>The <i>short text</i> field is required for this type, and is expected to always be
- * displayed.
- *
- * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>) and <i>short title</i> fields are
- * optional for this type. If only one of these is provided, it is expected that it will be
- * displayed. If both are provided, it is expected that one of these will be displayed.
- */
- public static final int TYPE_SHORT_TEXT = 3;
-
- /**
- * Type used for complications where the primary piece of data is a piece of text. The text may
- * be accompanied by an icon and/or a title.
- *
- * <p>The <i>long text</i> field is required for this type, and is expected to always be
- * displayed.
- *
- * <p>The <i>long title</i> field is optional for this type. If provided, it is expected that
- * this field will be displayed.
- *
- * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>) and <i>small image</i> fields are also
- * optional for this type. If provided, at least one of these should be displayed.
- */
- public static final int TYPE_LONG_TEXT = 4;
-
- /**
- * Type used for complications including a numerical value within a range, such as a percentage.
- * The value may be accompanied by an icon and/or short text and title.
- *
- * <p>The <i>value</i>, <i>min value</i>, and <i>max value</i> fields are required for this
- * type, and the value within the range is expected to always be displayed.
- *
- * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>), <i>short title</i>, and <i>short
- * text</i> fields are optional for this type, but at least one must be defined. The watch face
- * may choose which of these fields to display, if any.
- */
- public static final int TYPE_RANGED_VALUE = 5;
-
- /**
- * Type used for complications which consist only of a tintable icon.
- *
- * <p>The <i>icon</i> field is required for this type, and is expected to always be displayed,
- * unless the device is in ambient mode with burn-in protection enabled, in which case the
- * <i>burnInProtectionIcon</i> field should be used instead.
- *
- * <p>The contentDescription field is recommended for this type. Use it to describe what data
- * the icon represents. If the icon is purely stylistic, and does not convey any information to
- * the user, then enter the empty string as the contentDescription.
- *
- * <p>No other fields are valid for this type.
- */
- public static final int TYPE_ICON = 6;
-
- /**
- * Type used for complications which consist only of a small image.
- *
- * <p>The <i>small image</i> field is required for this type, and is expected to always be
- * displayed, unless the device is in ambient mode, in which case either nothing or the
- * <i>burnInProtectionSmallImage</i> field may be used instead.
- *
- * <p>The contentDescription field is recommended for this type. Use it to describe what data
- * the image represents. If the image is purely stylistic, and does not convey any information
- * to the user, then enter the empty string as the contentDescription.
- *
- * <p>No other fields are valid for this type.
- */
- public static final int TYPE_SMALL_IMAGE = 7;
-
- /**
- * Type used for complications which consist only of a large image. A large image here is one
- * that could be used to fill the watch face, for example as the background.
- *
- * <p>The <i>large image</i> field is required for this type, and is expected to always be
- * displayed, unless the device is in ambient mode.
- *
- * <p>The contentDescription field is recommended for this type. Use it to describe what data
- * the image represents. If the image is purely stylistic, and does not convey any information
- * to the user, then enter the empty string as the contentDescription.
- *
- * <p>No other fields are valid for this type.
- */
- public static final int TYPE_LARGE_IMAGE = 8;
-
- /**
- * Type sent by the system when the watch face does not have permission to receive complication
- * data.
- *
- * <p>Fields will be populated to allow the data to be rendered as if it were of {@link
- * #TYPE_SHORT_TEXT} or {@link #TYPE_ICON} for consistency and convenience, but watch faces may
- * render this as they see fit.
- *
- * <p>It is recommended that, where possible, tapping on the complication when in this state
- * should trigger a permission request.
- */
- public static final int TYPE_NO_PERMISSION = 9;
-
- /**
- * Type used for complications which indicate progress towards a goal. The value may be
- * accompanied by an icon and/or short text and title.
- *
- * <p>The <i>value</i>, and <i>target value</i> fields are required for this type, and the
- * value is expected to always be displayed. The value must be >= 0 and may be > target value.
- * E.g. 15000 out of a target of 10000 steps.
- *
- * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>), <i>short title</i>, and <i>short
- * text</i> fields are optional for this type, but at least one must be defined. The watch face
- * may choose which of these fields to display, if any.
- */
- public static final int TYPE_GOAL_PROGRESS = 13;
-
- /**
- * Type used for complications to display a series of weighted values e.g. in a pie chart. The
- * weighted values may be accompanied by an icon and/or short text and title.
- *
- * <p>The <i>element weights</i> and <i>element colors</i> fields are required for this type,
- * and the value within the range is expected to always be displayed.
- *
- * <p>The <i>icon</i> (and <i>burnInProtectionIcon</i>), <i>short title</i>, and <i>short
- * text</i> fields are optional for this type, but at least one must be defined. The watch face
- * may choose which of these fields to display, if any.
- */
- public static final int TYPE_WEIGHTED_ELEMENTS = 14;
-
- // The following types are experimental, and they have negative IDs.
-
- /** Type that specifies a proto layout based complication. */
- public static final int EXP_TYPE_PROTO_LAYOUT = -11;
-
- /** Type that specifies a list of complication values. E.g. to support linear 3. */
- public static final int EXP_TYPE_LIST = -12;
-
- /** @hide */
- @IntDef({IMAGE_STYLE_PHOTO, IMAGE_STYLE_ICON})
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- @Retention(RetentionPolicy.SOURCE)
- public @interface ImageStyle {
- }
-
- /**
- * Style for small images which are photos that are expected to fill the space available. Images
- * of this style may be cropped to fit the shape of the complication - in particular, the image
- * may be cropped to a circle. Photos my not be recolored.
- *
- * <p>This is the default value.
- */
- public static final int IMAGE_STYLE_PHOTO = 1;
-
- /**
- * Style for small images that have a transparent background and are expected to be drawn
- * entirely within the space available, such as a launcher icon. Watch faces may add padding
- * when drawing these images, but should never crop these images. Icons may be recolored to fit
- * the complication style.
- */
- public static final int IMAGE_STYLE_ICON = 2;
-
- private static final String FIELD_COLOR_RAMP = "COLOR_RAMP";
- private static final String FIELD_COLOR_RAMP_INTERPOLATED = "COLOR_RAMP_INTERPOLATED";
- private static final String FIELD_DATA_SOURCE = "FIELD_DATA_SOURCE";
- private static final String FIELD_DISPLAY_POLICY = "DISPLAY_POLICY";
- private static final String FIELD_ELEMENT_BACKGROUND_COLOR = "ELEMENT_BACKGROUND_COLOR";
- private static final String FIELD_ELEMENT_COLORS = "ELEMENT_COLORS";
- private static final String FIELD_ELEMENT_WEIGHTS = "ELEMENT_WEIGHTS";
- private static final String FIELD_END_TIME = "END_TIME";
- private static final String FIELD_ICON = "ICON";
- private static final String FIELD_ICON_BURN_IN_PROTECTION = "ICON_BURN_IN_PROTECTION";
- private static final String FIELD_IMAGE_STYLE = "IMAGE_STYLE";
- private static final String FIELD_LARGE_IMAGE = "LARGE_IMAGE";
- private static final String FIELD_LONG_TITLE = "LONG_TITLE";
- private static final String FIELD_LONG_TEXT = "LONG_TEXT";
- private static final String FIELD_MAX_VALUE = "MAX_VALUE";
- private static final String FIELD_MIN_VALUE = "MIN_VALUE";
- private static final String FIELD_PERSISTENCE_POLICY = "PERSISTENCE_POLICY";
- private static final String FIELD_PLACEHOLDER_FIELDS = "PLACEHOLDER_FIELDS";
- private static final String FIELD_PLACEHOLDER_TYPE = "PLACEHOLDER_TYPE";
- private static final String FIELD_SMALL_IMAGE = "SMALL_IMAGE";
- private static final String FIELD_SMALL_IMAGE_BURN_IN_PROTECTION =
- "SMALL_IMAGE_BURN_IN_PROTECTION";
- private static final String FIELD_SHORT_TITLE = "SHORT_TITLE";
- private static final String FIELD_SHORT_TEXT = "SHORT_TEXT";
- private static final String FIELD_START_TIME = "START_TIME";
- private static final String FIELD_TAP_ACTION = "TAP_ACTION";
- private static final String FIELD_TAP_ACTION_LOST = "FIELD_TAP_ACTION_LOST";
- private static final String FIELD_TARGET_VALUE = "TARGET_VALUE";
- private static final String FIELD_TIMELINE_START_TIME = "TIMELINE_START_TIME";
- private static final String FIELD_TIMELINE_END_TIME = "TIMELINE_END_TIME";
- private static final String FIELD_TIMELINE_ENTRIES = "TIMELINE";
- private static final String FIELD_TIMELINE_ENTRY_TYPE = "TIMELINE_ENTRY_TYPE";
- private static final String FIELD_VALUE = "VALUE";
- private static final String FIELD_VALUE_TYPE = "VALUE_TYPE";
-
- // Experimental fields, these are subject to change without notice.
- private static final String EXP_FIELD_LIST_ENTRIES = "EXP_LIST_ENTRIES";
- private static final String EXP_FIELD_LIST_ENTRY_TYPE = "EXP_LIST_ENTRY_TYPE";
- private static final String EXP_FIELD_LIST_STYLE_HINT = "EXP_LIST_STYLE_HINT";
- private static final String EXP_FIELD_PROTO_LAYOUT_AMBIENT = "EXP_FIELD_PROTO_LAYOUT_AMBIENT";
- private static final String EXP_FIELD_PROTO_LAYOUT_INTERACTIVE =
- "EXP_FIELD_PROTO_LAYOUT_INTERACTIVE";
- private static final String EXP_FIELD_PROTO_LAYOUT_RESOURCES =
- "EXP_FIELD_PROTO_LAYOUT_RESOURCES";
-
- // Originally it was planned to support both content and image content descriptions.
- private static final String FIELD_CONTENT_DESCRIPTION = "IMAGE_CONTENT_DESCRIPTION";
-
- // The set of valid types.
- private static final Set<Integer> VALID_TYPES = validTypes();
-
- private static Set<Integer> validTypes() {
- HashSet<Integer> set = new HashSet<>();
- set.add(TYPE_NOT_CONFIGURED);
- set.add(TYPE_EMPTY);
- set.add(TYPE_SHORT_TEXT);
- set.add(TYPE_LONG_TEXT);
- set.add(TYPE_RANGED_VALUE);
- set.add(TYPE_ICON);
- set.add(TYPE_SMALL_IMAGE);
- set.add(TYPE_LARGE_IMAGE);
- set.add(TYPE_NO_PERMISSION);
- set.add(TYPE_NO_DATA);
- set.add(EXP_TYPE_PROTO_LAYOUT);
- set.add(EXP_TYPE_LIST);
- set.add(TYPE_GOAL_PROGRESS);
- set.add(TYPE_WEIGHTED_ELEMENTS);
- return set;
- }
-
- // Used for validation. REQUIRED_FIELDS[i] is an array containing all the fields which must be
- // populated for @ComplicationType i.
- private static final Map<Integer, String[]> REQUIRED_FIELDS = requiredFieldsMap();
-
- private static Map<Integer, String[]> requiredFieldsMap() {
- HashMap<Integer, String[]> map = new HashMap<>();
- map.put(TYPE_NOT_CONFIGURED, new String[0]);
- map.put(TYPE_EMPTY, new String[0]);
- map.put(TYPE_SHORT_TEXT, new String[]{FIELD_SHORT_TEXT});
- map.put(TYPE_LONG_TEXT, new String[]{FIELD_LONG_TEXT});
- map.put(TYPE_RANGED_VALUE, new String[]{FIELD_VALUE, FIELD_MIN_VALUE, FIELD_MAX_VALUE});
- map.put(TYPE_ICON, new String[]{FIELD_ICON});
- map.put(TYPE_SMALL_IMAGE, new String[]{FIELD_SMALL_IMAGE, FIELD_IMAGE_STYLE});
- map.put(TYPE_LARGE_IMAGE, new String[]{FIELD_LARGE_IMAGE});
- map.put(TYPE_NO_PERMISSION, new String[0]);
- map.put(TYPE_NO_DATA, new String[0]);
- map.put(EXP_TYPE_PROTO_LAYOUT, new String[]{EXP_FIELD_PROTO_LAYOUT_AMBIENT,
- EXP_FIELD_PROTO_LAYOUT_INTERACTIVE,
- EXP_FIELD_PROTO_LAYOUT_RESOURCES});
- map.put(EXP_TYPE_LIST, new String[]{EXP_FIELD_LIST_ENTRIES});
- map.put(TYPE_GOAL_PROGRESS, new String[]{FIELD_VALUE, FIELD_TARGET_VALUE});
- map.put(TYPE_WEIGHTED_ELEMENTS, new String[]{FIELD_ELEMENT_WEIGHTS,
- FIELD_ELEMENT_COLORS,
- FIELD_ELEMENT_BACKGROUND_COLOR});
- return map;
- }
-
- // Used for validation. OPTIONAL_FIELDS[i] is an array containing all the fields which are
- // valid but not required for type i.
- private static final Map<Integer, String[]> OPTIONAL_FIELDS = optionalFieldsMap();
-
- private static Map<Integer, String[]> optionalFieldsMap() {
- HashMap<Integer, String[]> map = new HashMap<>();
- map.put(TYPE_NOT_CONFIGURED, new String[0]);
- map.put(TYPE_EMPTY, new String[0]);
- map.put(TYPE_SHORT_TEXT, new String[]{
- FIELD_SHORT_TITLE,
- FIELD_ICON,
- FIELD_ICON_BURN_IN_PROTECTION,
- FIELD_SMALL_IMAGE,
- FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
- FIELD_IMAGE_STYLE,
- FIELD_TAP_ACTION,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY});
- map.put(TYPE_LONG_TEXT, new String[]{
- FIELD_LONG_TITLE,
- FIELD_ICON,
- FIELD_ICON_BURN_IN_PROTECTION,
- FIELD_SMALL_IMAGE,
- FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
- FIELD_IMAGE_STYLE,
- FIELD_TAP_ACTION,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY});
- map.put(TYPE_RANGED_VALUE, new String[]{
- FIELD_SHORT_TEXT,
- FIELD_SHORT_TITLE,
- FIELD_ICON,
- FIELD_ICON_BURN_IN_PROTECTION,
- FIELD_SMALL_IMAGE,
- FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
- FIELD_IMAGE_STYLE,
- FIELD_TAP_ACTION,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_COLOR_RAMP,
- FIELD_COLOR_RAMP_INTERPOLATED,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY,
- FIELD_VALUE_TYPE});
- map.put(TYPE_ICON, new String[]{
- FIELD_TAP_ACTION,
- FIELD_ICON_BURN_IN_PROTECTION,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY});
- map.put(TYPE_SMALL_IMAGE, new String[]{
- FIELD_TAP_ACTION,
- FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY});
- map.put(TYPE_LARGE_IMAGE, new String[]{
- FIELD_TAP_ACTION,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY});
- map.put(TYPE_NO_PERMISSION, new String[]{
- FIELD_SHORT_TEXT,
- FIELD_SHORT_TITLE,
- FIELD_ICON,
- FIELD_ICON_BURN_IN_PROTECTION,
- FIELD_SMALL_IMAGE,
- FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
- FIELD_IMAGE_STYLE,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY
- });
- map.put(TYPE_NO_DATA, new String[]{
- FIELD_CONTENT_DESCRIPTION,
- FIELD_ICON,
- FIELD_ICON_BURN_IN_PROTECTION,
- FIELD_IMAGE_STYLE,
- FIELD_LARGE_IMAGE,
- FIELD_LONG_TEXT,
- FIELD_LONG_TITLE,
- FIELD_MAX_VALUE,
- FIELD_MIN_VALUE,
- FIELD_PLACEHOLDER_FIELDS,
- FIELD_PLACEHOLDER_TYPE,
- FIELD_SHORT_TEXT,
- FIELD_SHORT_TITLE,
- FIELD_SMALL_IMAGE,
- FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
- FIELD_TAP_ACTION,
- FIELD_VALUE,
- FIELD_VALUE_TYPE,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY
- });
- map.put(EXP_TYPE_PROTO_LAYOUT, new String[]{
- FIELD_TAP_ACTION,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY});
- map.put(EXP_TYPE_LIST, new String[]{
- FIELD_TAP_ACTION,
- EXP_FIELD_LIST_STYLE_HINT,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY});
- map.put(TYPE_GOAL_PROGRESS, new String[]{
- FIELD_SHORT_TEXT,
- FIELD_SHORT_TITLE,
- FIELD_ICON,
- FIELD_ICON_BURN_IN_PROTECTION,
- FIELD_SMALL_IMAGE,
- FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
- FIELD_IMAGE_STYLE,
- FIELD_TAP_ACTION,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_COLOR_RAMP,
- FIELD_COLOR_RAMP_INTERPOLATED,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY});
- map.put(TYPE_WEIGHTED_ELEMENTS, new String[]{
- FIELD_SHORT_TEXT,
- FIELD_SHORT_TITLE,
- FIELD_ICON,
- FIELD_ICON_BURN_IN_PROTECTION,
- FIELD_SMALL_IMAGE,
- FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
- FIELD_IMAGE_STYLE,
- FIELD_TAP_ACTION,
- FIELD_CONTENT_DESCRIPTION,
- FIELD_DATA_SOURCE,
- FIELD_PERSISTENCE_POLICY,
- FIELD_DISPLAY_POLICY});
- return map;
- }
-
- @NonNull
- public static final Creator<ComplicationData> CREATOR =
- new Creator<ComplicationData>() {
- @SuppressLint("SyntheticAccessor")
- @NonNull
- @Override
- public ComplicationData createFromParcel(@NonNull Parcel source) {
- return new ComplicationData(source);
- }
-
- @NonNull
- @Override
- public ComplicationData[] newArray(int size) {
- return new ComplicationData[size];
- }
- };
-
- @ComplicationType
- final int mType;
- final Bundle mFields;
-
- ComplicationData(@NonNull Builder builder) {
- mType = builder.mType;
- mFields = builder.mFields;
- }
-
- ComplicationData(int type, Bundle fields) {
- mType = type;
- mFields = fields;
- mFields.setClassLoader(getClass().getClassLoader());
- }
-
- private ComplicationData(@NonNull Parcel in) {
- mType = in.readInt();
- mFields = in.readBundle(getClass().getClassLoader());
- }
-
- @RequiresApi(api = Build.VERSION_CODES.P)
- private static class SerializedForm implements Serializable {
- private static final int VERSION_NUMBER = 19;
-
- @NonNull
- ComplicationData mComplicationData;
-
- SerializedForm() {
- }
-
- SerializedForm(@NonNull ComplicationData complicationData) {
- mComplicationData = complicationData;
- }
-
- @SuppressLint("SyntheticAccessor") // For mComplicationData.mFields
- private void writeObject(ObjectOutputStream oos) throws IOException {
- oos.writeInt(VERSION_NUMBER);
- int type = mComplicationData.getType();
- oos.writeInt(type);
- oos.writeInt(mComplicationData.getPersistencePolicy());
- oos.writeInt(mComplicationData.getDisplayPolicy());
-
- if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
- oos.writeObject(mComplicationData.getLongText());
- }
- if (isFieldValidForType(FIELD_LONG_TITLE, type)) {
- oos.writeObject(mComplicationData.getLongTitle());
- }
- if (isFieldValidForType(FIELD_SHORT_TEXT, type)) {
- oos.writeObject(mComplicationData.getShortText());
- }
- if (isFieldValidForType(FIELD_SHORT_TITLE, type)) {
- oos.writeObject(mComplicationData.getShortTitle());
- }
- if (isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type)) {
- oos.writeObject(mComplicationData.getContentDescription());
- }
- if (isFieldValidForType(FIELD_ICON, type)) {
- oos.writeObject(IconSerializableHelper.create(mComplicationData.getIcon()));
- }
- if (isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type)) {
- oos.writeObject(
- IconSerializableHelper.create(mComplicationData.getBurnInProtectionIcon()));
- }
- if (isFieldValidForType(FIELD_SMALL_IMAGE, type)) {
- oos.writeObject(IconSerializableHelper.create(mComplicationData.getSmallImage()));
- }
- if (isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type)) {
- oos.writeObject(IconSerializableHelper.create(
- mComplicationData.getBurnInProtectionSmallImage()));
- }
- if (isFieldValidForType(FIELD_IMAGE_STYLE, type)) {
- oos.writeInt(mComplicationData.getSmallImageStyle());
- }
- if (isFieldValidForType(FIELD_LARGE_IMAGE, type)) {
- oos.writeObject(IconSerializableHelper.create(mComplicationData.getLargeImage()));
- }
- if (isFieldValidForType(FIELD_VALUE, type)) {
- oos.writeFloat(mComplicationData.getRangedValue());
- }
- if (isFieldValidForType(FIELD_VALUE_TYPE, type)) {
- oos.writeInt(mComplicationData.getRangedValueType());
- }
- if (isFieldValidForType(FIELD_MIN_VALUE, type)) {
- oos.writeFloat(mComplicationData.getRangedMinValue());
- }
- if (isFieldValidForType(FIELD_MAX_VALUE, type)) {
- oos.writeFloat(mComplicationData.getRangedMaxValue());
- }
- if (isFieldValidForType(FIELD_TARGET_VALUE, type)) {
- oos.writeFloat(mComplicationData.getTargetValue());
- }
- if (isFieldValidForType(FIELD_COLOR_RAMP, type)) {
- int[] colors = mComplicationData.getColorRamp();
- if (colors != null) {
- oos.writeBoolean(true);
- oos.writeInt(colors.length);
- for (int color : colors) {
- oos.writeInt(color);
- }
- } else {
- oos.writeBoolean(false);
- }
- }
- if (isFieldValidForType(FIELD_COLOR_RAMP_INTERPOLATED, type)) {
- Boolean isColorRampSmoothShaded = mComplicationData.isColorRampInterpolated();
- if (isColorRampSmoothShaded != null) {
- oos.writeBoolean(true);
- oos.writeBoolean(isColorRampSmoothShaded);
- } else {
- oos.writeBoolean(false);
- }
- }
- if (isFieldValidForType(FIELD_ELEMENT_WEIGHTS, type)) {
- float[] weights = mComplicationData.getElementWeights();
- if (weights != null) {
- oos.writeBoolean(true);
- oos.writeInt(weights.length);
- for (float weight : weights) {
- oos.writeFloat(weight);
- }
- } else {
- oos.writeBoolean(false);
- }
- }
- if (isFieldValidForType(FIELD_ELEMENT_COLORS, type)) {
- int[] colors = mComplicationData.getElementColors();
- if (colors != null) {
- oos.writeBoolean(true);
- oos.writeInt(colors.length);
- for (int color : colors) {
- oos.writeInt(color);
- }
- } else {
- oos.writeBoolean(false);
- }
- }
- if (isFieldValidForType(FIELD_ELEMENT_BACKGROUND_COLOR, type)) {
- oos.writeInt(mComplicationData.getElementBackgroundColor());
- }
- if (isFieldValidForType(FIELD_START_TIME, type)) {
- oos.writeLong(mComplicationData.getStartDateTimeMillis());
- }
- if (isFieldValidForType(FIELD_END_TIME, type)) {
- oos.writeLong(mComplicationData.getEndDateTimeMillis());
- }
- oos.writeInt(mComplicationData.mFields.getInt(EXP_FIELD_LIST_ENTRY_TYPE));
- if (isFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)) {
- oos.writeInt(mComplicationData.getListStyleHint());
- }
- if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, type)) {
- byte[] bytes = mComplicationData.getInteractiveLayout();
- if (bytes == null) {
- oos.writeInt(0);
- } else {
- oos.writeInt(bytes.length);
- oos.write(bytes);
- }
- }
- if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_AMBIENT, type)) {
- byte[] bytes = mComplicationData.getAmbientLayout();
- if (bytes == null) {
- oos.writeInt(0);
- } else {
- oos.writeInt(bytes.length);
- oos.write(bytes);
- }
- }
- if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_RESOURCES, type)) {
- byte[] bytes = mComplicationData.getLayoutResources();
- if (bytes == null) {
- oos.writeInt(0);
- } else {
- oos.writeInt(bytes.length);
- oos.write(bytes);
- }
- }
- if (isFieldValidForType(FIELD_DATA_SOURCE, type)) {
- ComponentName componentName = mComplicationData.getDataSource();
- if (componentName == null) {
- oos.writeUTF("");
- } else {
- oos.writeUTF(componentName.flattenToString());
- }
- }
-
- // TapAction unfortunately can't be serialized, instead we record if we've lost it.
- oos.writeBoolean(mComplicationData.hasTapAction()
- || mComplicationData.getTapActionLostDueToSerialization());
- long start = mComplicationData.mFields.getLong(FIELD_TIMELINE_START_TIME, -1);
- oos.writeLong(start);
- long end = mComplicationData.mFields.getLong(FIELD_TIMELINE_END_TIME, -1);
- oos.writeLong(end);
- oos.writeInt(mComplicationData.mFields.getInt(FIELD_TIMELINE_ENTRY_TYPE));
-
- List<ComplicationData> listEntries = mComplicationData.getListEntries();
- int listEntriesLength = (listEntries != null) ? listEntries.size() : 0;
- oos.writeInt(listEntriesLength);
- if (listEntries != null) {
- for (ComplicationData data : listEntries) {
- new SerializedForm(data).writeObject(oos);
- }
- }
-
- if (isFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)) {
- ComplicationData placeholder = mComplicationData.getPlaceholder();
- if (placeholder == null) {
- oos.writeBoolean(false);
- } else {
- oos.writeBoolean(true);
- new SerializedForm(placeholder).writeObject(oos);
- }
- }
-
- // This has to be last, since it's recursive.
- List<ComplicationData> timeline = mComplicationData.getTimelineEntries();
- int timelineLength = (timeline != null) ? timeline.size() : 0;
- oos.writeInt(timelineLength);
- if (timeline != null) {
- for (ComplicationData data : timeline) {
- new SerializedForm(data).writeObject(oos);
- }
- }
- }
-
- private static void putIfNotNull(Bundle fields, String field, Parcelable value) {
- if (value != null) {
- fields.putParcelable(field, value);
- }
- }
-
- @SuppressLint("SyntheticAccessor") // For mComplicationData.mFields
- private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
- int versionNumber = ois.readInt();
- if (versionNumber != VERSION_NUMBER) {
- // Give up if there's a version skew.
- throw new IOException("Unsupported serialization version number " + versionNumber);
- }
- int type = ois.readInt();
- Bundle fields = new Bundle();
- fields.putInt(FIELD_PERSISTENCE_POLICY, ois.readInt());
- fields.putInt(FIELD_DISPLAY_POLICY, ois.readInt());
-
- if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
- putIfNotNull(fields, FIELD_LONG_TEXT, (ComplicationText) ois.readObject());
- }
- if (isFieldValidForType(FIELD_LONG_TITLE, type)) {
- putIfNotNull(fields, FIELD_LONG_TITLE, (ComplicationText) ois.readObject());
- }
- if (isFieldValidForType(FIELD_SHORT_TEXT, type)) {
- putIfNotNull(fields, FIELD_SHORT_TEXT, (ComplicationText) ois.readObject());
- }
- if (isFieldValidForType(FIELD_SHORT_TITLE, type)) {
- putIfNotNull(fields, FIELD_SHORT_TITLE, (ComplicationText) ois.readObject());
- }
- if (isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type)) {
- putIfNotNull(fields, FIELD_CONTENT_DESCRIPTION,
- (ComplicationText) ois.readObject());
- }
- if (isFieldValidForType(FIELD_ICON, type)) {
- putIfNotNull(fields, FIELD_ICON, IconSerializableHelper.read(ois));
- }
- if (isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type)) {
- putIfNotNull(fields, FIELD_ICON_BURN_IN_PROTECTION,
- IconSerializableHelper.read(ois));
- }
- if (isFieldValidForType(FIELD_SMALL_IMAGE, type)) {
- putIfNotNull(fields, FIELD_SMALL_IMAGE, IconSerializableHelper.read(ois));
- }
- if (isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type)) {
- putIfNotNull(fields,
- FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, IconSerializableHelper.read(ois));
- }
- if (isFieldValidForType(FIELD_IMAGE_STYLE, type)) {
- fields.putInt(FIELD_IMAGE_STYLE, ois.readInt());
- }
- if (isFieldValidForType(FIELD_LARGE_IMAGE, type)) {
- fields.putParcelable(FIELD_LARGE_IMAGE, IconSerializableHelper.read(ois));
- }
- if (isFieldValidForType(FIELD_VALUE, type)) {
- fields.putFloat(FIELD_VALUE, ois.readFloat());
- }
- if (isFieldValidForType(FIELD_VALUE_TYPE, type)) {
- fields.putInt(FIELD_VALUE_TYPE, ois.readInt());
- }
- if (isFieldValidForType(FIELD_MIN_VALUE, type)) {
- fields.putFloat(FIELD_MIN_VALUE, ois.readFloat());
- }
- if (isFieldValidForType(FIELD_MAX_VALUE, type)) {
- fields.putFloat(FIELD_MAX_VALUE, ois.readFloat());
- }
- if (isFieldValidForType(FIELD_TARGET_VALUE, type)) {
- fields.putFloat(FIELD_TARGET_VALUE, ois.readFloat());
- }
- if (isFieldValidForType(FIELD_COLOR_RAMP, type) && ois.readBoolean()) {
- int numColors = ois.readInt();
- int[] colors = new int[numColors];
- for (int i = 0; i < numColors; ++i) {
- colors[i] = ois.readInt();
- }
- fields.putIntArray(FIELD_COLOR_RAMP, colors);
- }
- if (isFieldValidForType(FIELD_COLOR_RAMP_INTERPOLATED, type) && ois.readBoolean()) {
- fields.putBoolean(FIELD_COLOR_RAMP_INTERPOLATED, ois.readBoolean());
- }
- if (isFieldValidForType(FIELD_ELEMENT_WEIGHTS, type) && ois.readBoolean()) {
- int numWeights = ois.readInt();
- float[] weights = new float[numWeights];
- for (int i = 0; i < numWeights; ++i) {
- weights[i] = ois.readFloat();
- }
- fields.putFloatArray(FIELD_ELEMENT_WEIGHTS, weights);
- }
- if (isFieldValidForType(FIELD_ELEMENT_COLORS, type) && ois.readBoolean()) {
- int numColors = ois.readInt();
- int[] colors = new int[numColors];
- for (int i = 0; i < numColors; ++i) {
- colors[i] = ois.readInt();
- }
- fields.putIntArray(FIELD_ELEMENT_COLORS, colors);
- }
- if (isFieldValidForType(FIELD_ELEMENT_BACKGROUND_COLOR, type)) {
- fields.putInt(FIELD_ELEMENT_BACKGROUND_COLOR, ois.readInt());
- }
- if (isFieldValidForType(FIELD_START_TIME, type)) {
- fields.putLong(FIELD_START_TIME, ois.readLong());
- }
- if (isFieldValidForType(FIELD_END_TIME, type)) {
- fields.putLong(FIELD_END_TIME, ois.readLong());
- }
- int listEntryType = ois.readInt();
- if (listEntryType != 0) {
- fields.putInt(EXP_FIELD_LIST_ENTRY_TYPE, listEntryType);
- }
- if (isFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)) {
- fields.putInt(EXP_FIELD_LIST_STYLE_HINT, ois.readInt());
- }
- if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, type)) {
- int length = ois.readInt();
- if (length > 0) {
- byte[] protoLayout = new byte[length];
- ois.readFully(protoLayout);
- fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, protoLayout);
- }
- }
- if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_AMBIENT, type)) {
- int length = ois.readInt();
- if (length > 0) {
- byte[] ambientProtoLayout = new byte[length];
- ois.readFully(ambientProtoLayout);
- fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_AMBIENT, ambientProtoLayout);
- }
- }
- if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_RESOURCES, type)) {
- int length = ois.readInt();
- if (length > 0) {
- byte[] protoLayoutResources = new byte[length];
- ois.readFully(protoLayoutResources);
- fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_RESOURCES, protoLayoutResources);
- }
- }
- if (isFieldValidForType(FIELD_DATA_SOURCE, type)) {
- String componentName = ois.readUTF();
- if (componentName.isEmpty()) {
- fields.remove(FIELD_DATA_SOURCE);
- } else {
- fields.putParcelable(
- FIELD_DATA_SOURCE, ComponentName.unflattenFromString(componentName));
- }
- }
- if (ois.readBoolean()) {
- fields.putBoolean(FIELD_TAP_ACTION_LOST, true);
- }
- long start = ois.readLong();
- if (start != -1) {
- fields.putLong(FIELD_TIMELINE_START_TIME, start);
- }
- long end = ois.readLong();
- if (end != -1) {
- fields.putLong(FIELD_TIMELINE_END_TIME, end);
- }
- int timelineEntryType = ois.readInt();
- if (timelineEntryType != 0) {
- fields.putInt(FIELD_TIMELINE_ENTRY_TYPE, timelineEntryType);
- }
-
- int listEntriesLength = ois.readInt();
- if (listEntriesLength != 0) {
- Parcelable[] parcels = new Parcelable[listEntriesLength];
- for (int i = 0; i < listEntriesLength; i++) {
- SerializedForm entry = new SerializedForm();
- entry.readObject(ois);
- parcels[i] = entry.mComplicationData.mFields;
- }
- fields.putParcelableArray(EXP_FIELD_LIST_ENTRIES, parcels);
- }
-
- if (isFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)) {
- if (ois.readBoolean()) {
- SerializedForm serializedPlaceholder = new SerializedForm();
- serializedPlaceholder.readObject(ois);
- fields.putInt(FIELD_PLACEHOLDER_TYPE,
- serializedPlaceholder.mComplicationData.mType);
- fields.putBundle(FIELD_PLACEHOLDER_FIELDS,
- serializedPlaceholder.mComplicationData.mFields);
- }
- }
-
- int timelineLength = ois.readInt();
- if (timelineLength != 0) {
- Parcelable[] parcels = new Parcelable[timelineLength];
- for (int i = 0; i < timelineLength; i++) {
- SerializedForm entry = new SerializedForm();
- entry.readObject(ois);
- parcels[i] = entry.mComplicationData.mFields;
- }
- fields.putParcelableArray(FIELD_TIMELINE_ENTRIES, parcels);
- }
- mComplicationData = new ComplicationData(type, fields);
- }
-
- Object readResolve() {
- return mComplicationData;
- }
- }
-
- @RequiresApi(api = Build.VERSION_CODES.P)
- Object writeReplace() {
- return new SerializedForm(this);
- }
-
- private void readObject(ObjectInputStream stream) throws InvalidObjectException {
- throw new InvalidObjectException("Use SerializedForm");
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mType);
- dest.writeBundle(mFields);
- }
-
- /**
- * Returns the type of this complication data.
- *
- * <p>Will be one of {@link #TYPE_SHORT_TEXT}, {@link #TYPE_LONG_TEXT}, {@link
- * #TYPE_RANGED_VALUE}, {@link #TYPE_ICON}, {@link #TYPE_SMALL_IMAGE}, {@link
- * #TYPE_LARGE_IMAGE}, {@link #TYPE_NOT_CONFIGURED}, {@link #TYPE_EMPTY}, {@link
- * #TYPE_NO_PERMISSION}, or {@link #TYPE_NO_DATA}.
- */
- @ComplicationType
- public int getType() {
- return mType;
- }
-
- /**
- * Returns true if the complication is active and should be displayed at the given time. If this
- * returns false, the complication should not be displayed.
- *
- * <p>This must be checked for any time for which the complication will be displayed.
- */
- public boolean isActiveAt(long dateTimeMillis) {
- return dateTimeMillis >= mFields.getLong(FIELD_START_TIME, 0)
- && dateTimeMillis <= mFields.getLong(FIELD_END_TIME, Long.MAX_VALUE);
- }
-
- /**
- * TapAction unfortunately can't be serialized. Returns true if tapAction has been lost due to
- * serialization (e.g. due to being read from the local cache). The next complication update
- * from the system would replace this with one with a tapAction.
- */
- public boolean getTapActionLostDueToSerialization() {
- return mFields.getBoolean(FIELD_TAP_ACTION_LOST);
- }
-
- /**
- * For timeline entries. Returns the epoch second at which this timeline entry becomes
- * valid or `null` if it's not set.
- */
- @Nullable
- public Long getTimelineStartEpochSecond() {
- long expiresAt = mFields.getLong(FIELD_TIMELINE_START_TIME, -1);
- if (expiresAt == -1) {
- return null;
- } else {
- return expiresAt;
- }
- }
-
- /**
- * For timeline entries. Sets the epoch second at which this timeline entry becomes invalid
- * or clears the field if instant is `null`.
- */
- public void setTimelineStartEpochSecond(@Nullable Long epochSecond) {
- if (epochSecond == null) {
- mFields.remove(FIELD_TIMELINE_START_TIME);
- } else {
- mFields.putLong(FIELD_TIMELINE_START_TIME, epochSecond);
- }
- }
-
- /**
- * For timeline entries. Returns the epoch second at which this timeline entry becomes invalid
- * or `null` if it's not set.
- */
- @Nullable
- public Long getTimelineEndEpochSecond() {
- long expiresAt = mFields.getLong(FIELD_TIMELINE_END_TIME, -1);
- if (expiresAt == -1) {
- return null;
- } else {
- return expiresAt;
- }
- }
-
- /**
- * For timeline entries. Sets the epoch second at which this timeline entry becomes invalid,
- * or clears the field if instant is `null`.
- */
- public void setTimelineEndEpochSecond(@Nullable Long epochSecond) {
- if (epochSecond == null) {
- mFields.remove(FIELD_TIMELINE_END_TIME);
- } else {
- mFields.putLong(FIELD_TIMELINE_END_TIME, epochSecond);
- }
- }
-
- /** Returns the list of {@link ComplicationData} timeline entries. */
- @Nullable
- @SuppressWarnings("deprecation")
- public List<ComplicationData> getTimelineEntries() {
- Parcelable[] bundles = mFields.getParcelableArray(FIELD_TIMELINE_ENTRIES);
- if (bundles == null) {
- return null;
- }
- ArrayList<ComplicationData> entries = new ArrayList<>();
- for (Parcelable parcelable : bundles) {
- Bundle bundle = (Bundle) parcelable;
- bundle.setClassLoader(getClass().getClassLoader());
- // Use the serialized FIELD_TIMELINE_ENTRY_TYPE or the outer type if it's not there.
- // Usually the timeline entry type will be the same as the outer type, unless an entry
- // contains NoDataComplicationData.
- int type = bundle.getInt(FIELD_TIMELINE_ENTRY_TYPE, mType);
- entries.add(new ComplicationData(type, (Bundle) parcelable));
- }
- return entries;
- }
-
- /** Sets the list of {@link ComplicationData} timeline entries. */
- public void setTimelineEntryCollection(@Nullable Collection<ComplicationData> timelineEntries) {
- if (timelineEntries == null) {
- mFields.remove(FIELD_TIMELINE_ENTRIES);
- } else {
- mFields.putParcelableArray(
- FIELD_TIMELINE_ENTRIES,
- timelineEntries.stream().map(
- e -> {
- // This supports timeline entry of NoDataComplicationData.
- e.mFields.putInt(FIELD_TIMELINE_ENTRY_TYPE, e.mType);
- return e.mFields;
- }
- ).toArray(Parcelable[]::new));
- }
- }
-
- /** Returns the list of {@link ComplicationData} entries for a ListComplicationData. */
- @Nullable
- @SuppressWarnings("deprecation")
- public List<ComplicationData> getListEntries() {
- Parcelable[] bundles = mFields.getParcelableArray(EXP_FIELD_LIST_ENTRIES);
- if (bundles == null) {
- return null;
- }
- ArrayList<ComplicationData> entries = new ArrayList<>();
- for (Parcelable parcelable : bundles) {
- Bundle bundle = (Bundle) parcelable;
- bundle.setClassLoader(getClass().getClassLoader());
- entries.add(new ComplicationData(bundle.getInt(EXP_FIELD_LIST_ENTRY_TYPE), bundle));
- }
- return entries;
- }
-
- /**
- * Sets the {@link ComponentName} of the ComplicationDataSourceService that provided this
- * ComplicationData.
- */
- public void setDataSource(@Nullable ComponentName provider) {
- mFields.putParcelable(FIELD_DATA_SOURCE, provider);
- }
-
- /**
- * Gets the {@link ComponentName} of the ComplicationDataSourceService that provided this
- * ComplicationData.
- */
- @Nullable
- @SuppressWarnings("deprecation") // The safer alternative is not available on Wear OS yet.
- public ComponentName getDataSource() {
- return (ComponentName) mFields.getParcelable(FIELD_DATA_SOURCE);
- }
-
- /**
- * Returns true if the ComplicationData contains a ranged max value. I.e. if
- * {@link #getRangedValue} can succeed.
- */
- public boolean hasRangedValue() {
- try {
- return isFieldValidForType(FIELD_VALUE, mType);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>value</i> field for this complication.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE}.
- * Otherwise returns zero.
- */
- public float getRangedValue() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_VALUE, mType);
- return mFields.getFloat(FIELD_VALUE);
- }
-
- /**
- * Returns true if the ComplicationData contains a ranged max type. I.e. if
- * {@link #getRangedValueType} can succeed.
- */
- public boolean hasRangedValueType() {
- try {
- return isFieldValidForType(FIELD_VALUE_TYPE, mType);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>value</i> field for this complication.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE}.
- * Otherwise returns zero.
- */
- public int getRangedValueType() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_VALUE_TYPE, mType);
- return mFields.getInt(FIELD_VALUE_TYPE);
- }
-
- /**
- * Returns true if the ComplicationData contains a ranged max value. I.e. if
- * {@link #getRangedMinValue} can succeed.
- */
- public boolean hasRangedMinValue() {
- try {
- return isFieldValidForType(FIELD_MIN_VALUE, mType);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>min value</i> field for this complication.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE}.
- * Otherwise returns zero.
- */
- public float getRangedMinValue() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_MIN_VALUE, mType);
- return mFields.getFloat(FIELD_MIN_VALUE);
- }
-
- /**
- * Returns true if the ComplicationData contains a ranged max value. I.e. if
- * {@link #getRangedMaxValue} can succeed.
- */
- public boolean hasRangedMaxValue() {
- try {
- return isFieldValidForType(FIELD_MAX_VALUE, mType);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>max value</i> field for this complication.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE}.
- * Otherwise returns zero.
- */
- public float getRangedMaxValue() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_MAX_VALUE, mType);
- return mFields.getFloat(FIELD_MAX_VALUE);
- }
-
- /**
- * Returns true if the ComplicationData contains a ranged max value. I.e. if
- * {@link #getTargetValue} can succeed.
- */
- public boolean hasTargetValue() {
- try {
- return isFieldValidForType(FIELD_TARGET_VALUE, mType);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>value</i> field for this complication.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_GOAL_PROGRESS}.
- * Otherwise returns zero.
- */
- public float getTargetValue() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_TARGET_VALUE, mType);
- return mFields.getFloat(FIELD_TARGET_VALUE);
- }
-
- /**
- * Returns the colors for the progress bar.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE} or
- * {@link #TYPE_GOAL_PROGRESS}.
- */
- @ColorInt
- @Nullable
- public int[] getColorRamp() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_COLOR_RAMP, mType);
- if (mFields.containsKey(FIELD_COLOR_RAMP)) {
- return mFields.getIntArray(FIELD_COLOR_RAMP);
- }
- return null;
- }
-
- /**
- * Returns either a boolean where: true means the color ramp colors should be smoothly
- * interpolatded; false means the color ramp should be rendered in equal sized blocks of
- * solid color; null means this value wasn't set, i.e. the complication is not of type
- * {@link #TYPE_RANGED_VALUE} or {@link #TYPE_GOAL_PROGRESS}.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_RANGED_VALUE} or
- * {@link #TYPE_GOAL_PROGRESS}.
- */
- @Nullable
- public Boolean isColorRampInterpolated() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_COLOR_RAMP_INTERPOLATED, mType);
- if (mFields.containsKey(FIELD_COLOR_RAMP_INTERPOLATED)) {
- return mFields.getBoolean(FIELD_COLOR_RAMP_INTERPOLATED);
- }
- return null;
- }
-
- /**
- * Returns true if the ComplicationData contains a short title. I.e. if {@link #getShortTitle}
- * can succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasShortTitle() {
- try {
- return isFieldValidForType(FIELD_SHORT_TITLE, mType)
- && (mFields.getParcelable(FIELD_SHORT_TITLE) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>short title</i> field for this complication, or {@code null} if no value was
- * provided for the field.
- *
- * <p>The value is provided as a {@link ComplicationText} object, from which the text to display
- * can be obtained for a given point in time.
- *
- * <p>The length of the text, including any time-dependent values at any valid time, is expected
- * to not exceed seven characters. When using this text, the watch face should be able to
- * display any string of up to seven characters (reducing the text size appropriately if the
- * string is very wide). Although not expected, it is possible that strings of more than seven
- * characters might be seen, in which case they may be truncated.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_SHORT_TEXT}, {@link
- * #TYPE_RANGED_VALUE}, or {@link #TYPE_NO_PERMISSION}.
- * Otherwise returns null.
- */
- @Nullable
- public ComplicationText getShortTitle() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_SHORT_TITLE, mType);
- return getParcelableField(FIELD_SHORT_TITLE);
- }
-
- /**
- * Returns true if the ComplicationData contains short text. I.e. if {@link #getShortText} can
- * succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasShortText() {
- try {
- return isFieldValidForType(FIELD_SHORT_TEXT, mType)
- && (mFields.getParcelable(FIELD_SHORT_TEXT) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>short text</i> field for this complication, or {@code null} if no value was
- * provided for the field.
- *
- * <p>The value is provided as a {@link ComplicationText} object, from which the text to display
- * can be obtained for a given point in time.
- *
- * <p>The length of the text, including any time-dependent values at any valid time, is expected
- * to not exceed seven characters. When using this text, the watch face should be able to
- * display any string of up to seven characters (reducing the text size appropriately if the
- * string is very wide). Although not expected, it is possible that strings of more than seven
- * characters might be seen, in which case they may be truncated.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_SHORT_TEXT}, {@link
- * #TYPE_RANGED_VALUE}, or {@link #TYPE_NO_PERMISSION}.
- * Otherwise returns null.
- */
- @Nullable
- public ComplicationText getShortText() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_SHORT_TEXT, mType);
- return getParcelableField(FIELD_SHORT_TEXT);
- }
-
- /**
- * Returns true if the ComplicationData contains a long title. I.e. if {@link #getLongTitle}
- * can succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasLongTitle() {
- try {
- return isFieldValidForType(FIELD_LONG_TITLE, mType)
- && (mFields.getParcelable(FIELD_LONG_TITLE) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>long title</i> field for this complication, or {@code null} if no value was
- * provided for the field.
- *
- * <p>The value is provided as a {@link ComplicationText} object, from which the text to display
- * can be obtained for a given point in time.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_LONG_TEXT}.
- * Otherwise returns null.
- */
- @Nullable
- public ComplicationText getLongTitle() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_LONG_TITLE, mType);
- return getParcelableField(FIELD_LONG_TITLE);
- }
-
- /**
- * Returns true if the ComplicationData contains long text. I.e. if {@link #getLongText} can
- * succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasLongText() {
- try {
- return isFieldValidForType(FIELD_LONG_TEXT, mType)
- && (mFields.getParcelable(FIELD_LONG_TEXT) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>long text</i> field for this complication.
- *
- * <p>The value is provided as a {@link ComplicationText} object, from which the text to display
- * can be obtained for a given point in time.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_LONG_TEXT}.
- * Otherwise returns null.
- */
- @Nullable
- public ComplicationText getLongText() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_LONG_TEXT, mType);
- return getParcelableField(FIELD_LONG_TEXT);
- }
-
- /**
- * Returns true if the ComplicationData contains an Icon. I.e. if {@link #getIcon} can succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasIcon() {
- try {
- return isFieldValidForType(FIELD_ICON, mType)
- && (mFields.getParcelable(FIELD_ICON) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>icon</i> field for this complication, or {@code null} if no value was provided
- * for the field. The image returned is expected to be single-color and so may be tinted to
- * whatever color the watch face requires (but note that {@link Drawable#mutate()} should be
- * called before drawables are tinted).
- *
- * <p>If the device is in ambient mode, and utilises burn-in protection, then the result of
- * {@link #getBurnInProtectionIcon} must be used instead of this.
- *
- * <p>Valid for the types {@link #TYPE_SHORT_TEXT}, {@link #TYPE_LONG_TEXT}, {@link
- * #TYPE_RANGED_VALUE}, {@link #TYPE_ICON}, or {@link #TYPE_NO_PERMISSION}.
- * Otherwise returns null.
- */
- @Nullable
- public Icon getIcon() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_ICON, mType);
- return getParcelableField(FIELD_ICON);
- }
-
- /**
- * Returns true if the ComplicationData contains a burn in protection Icon. I.e. if
- * {@link #getBurnInProtectionIcon} can succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasBurnInProtectionIcon() {
- try {
- return isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, mType)
- && (mFields.getParcelable(FIELD_ICON_BURN_IN_PROTECTION) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the burn-in protection version of the <i>icon</i> field for this complication, or
- * {@code null} if no such icon was provided. The image returned is expected to be an outline
- * image suitable for use in ambient mode on screens with burn-in protection. The image is also
- * expected to be single-color and so may be tinted to whatever color the watch face requires
- * (but note that {@link Drawable#mutate()} should be called before drawables are tinted, and
- * that the color used should be suitable for ambient mode with burn-in protection).
- *
- * <p>If the device is in ambient mode, and utilises burn-in protection, then the result of this
- * method must be used instead of the result of {@link #getIcon}.
- *
- * <p>Valid for the types {@link #TYPE_SHORT_TEXT}, {@link #TYPE_LONG_TEXT}, {@link
- * #TYPE_RANGED_VALUE}, {@link #TYPE_ICON}, or {@link #TYPE_NO_PERMISSION}.
- * Otherwise returns null.
- */
- @Nullable
- public Icon getBurnInProtectionIcon() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_ICON_BURN_IN_PROTECTION, mType);
- return getParcelableField(FIELD_ICON_BURN_IN_PROTECTION);
- }
-
- /**
- * Returns true if the ComplicationData contains a small image. I.e. if {@link #getSmallImage}
- * can succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasSmallImage() {
- try {
- return isFieldValidForType(FIELD_SMALL_IMAGE, mType)
- && (mFields.getParcelable(FIELD_SMALL_IMAGE) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>small image</i> field for this complication, or {@code null} if no value was
- * provided for the field.
- *
- * <p>This may be either a {@link #IMAGE_STYLE_PHOTO photo style} image, which is expected to
- * fill the space available, or an {@link #IMAGE_STYLE_ICON icon style} image, which should be
- * drawn entirely within the space available. Use {@link #getSmallImageStyle} to determine which
- * of these applies.
- *
- * <p>As this may be any image, it is unlikely to be suitable for display in ambient mode when
- * burn-in protection is enabled, or in low-bit ambient mode, and should not be rendered under
- * these circumstances.
- *
- * <p>Valid for the types {@link #TYPE_LONG_TEXT} and {@link #TYPE_SMALL_IMAGE}.
- * Otherwise returns null.
- */
- @Nullable
- public Icon getSmallImage() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_SMALL_IMAGE, mType);
- return getParcelableField(FIELD_SMALL_IMAGE);
- }
-
- /**
- * Returns true if the ComplicationData contains a burn in protection small image. I.e. if
- * {@link #getBurnInProtectionSmallImage} can succeed.
- *
- * @throws IllegalStateException for invalid types
- */
- @SuppressWarnings("deprecation")
- public boolean hasBurnInProtectionSmallImage() {
- try {
- return isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, mType)
- && (mFields.getParcelable(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the burn-in protection version of the <i>small image</i> field for this complication,
- * or {@code null} if no such icon was provided. The image returned is expected to be an outline
- * image suitable for use in ambient mode on screens with burn-in protection. The image is also
- * expected to be single-color and so may be tinted to whatever color the watch face requires
- * (but note that {@link Drawable#mutate()} should be called before drawables are tinted, and
- * that the color used should be suitable for ambient mode with burn-in protection).
- *
- * <p>If the device is in ambient mode, and utilises burn-in protection, then the result of this
- * method must be used instead of the result of {@link #getSmallImage()}.
- *
- * <p>Valid for the types {@link #TYPE_LONG_TEXT} and {@link #TYPE_SMALL_IMAGE}.
- * Otherwise returns null.
- */
- @Nullable
- public Icon getBurnInProtectionSmallImage() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, mType);
- return getParcelableField(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION);
- }
-
- /**
- * Returns the <i>small image style</i> field for this complication.
- *
- * <p>The result of this method should be taken in to account when drawing a small image
- * complication.
- *
- * <p>Valid only for types that contain small images, i.e. {@link #TYPE_SMALL_IMAGE} and {@link
- * #TYPE_LONG_TEXT}.
- * Otherwise returns zero.
- *
- * @see #IMAGE_STYLE_PHOTO which can be cropped but not recolored.
- * @see #IMAGE_STYLE_ICON which can be recolored but not cropped.
- */
- @ImageStyle
- public int getSmallImageStyle() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_IMAGE_STYLE, mType);
- return mFields.getInt(FIELD_IMAGE_STYLE);
- }
-
- /**
- * Returns true if the ComplicationData contains a large image. I.e. if {@link #getLargeImage}
- * can succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasLargeImage() {
- try {
- return isFieldValidForType(FIELD_LARGE_IMAGE, mType)
- && (mFields.getParcelable(FIELD_LARGE_IMAGE) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>large image</i> field for this complication. This image is expected to be of a
- * suitable size to fill the screen of the watch.
- *
- * <p>As this may be any image, it is unlikely to be suitable for display in ambient mode when
- * burn-in protection is enabled, or in low-bit ambient mode, and should not be rendered under
- * these circumstances.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_LARGE_IMAGE}.
- * Otherwise returns null.
- */
- @Nullable
- public Icon getLargeImage() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_LARGE_IMAGE, mType);
- return getParcelableField(FIELD_LARGE_IMAGE);
- }
-
- /**
- * Returns true if the ComplicationData contains a tap action. I.e. if {@link #getTapAction}
- * can succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasTapAction() {
- try {
- return isFieldValidForType(FIELD_TAP_ACTION, mType)
- && (mFields.getParcelable(FIELD_TAP_ACTION) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>tap action</i> field for this complication. The result is a {@link
- * PendingIntent} that should be fired if the complication is tapped on, assuming the
- * complication is tappable, or {@code null} if no tap action has been specified.
- *
- * <p>Valid for all non-empty types.
- * Otherwise returns null.
- */
- @Nullable
- public PendingIntent getTapAction() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_TAP_ACTION, mType);
- return getParcelableField(FIELD_TAP_ACTION);
- }
-
- /**
- * Returns true if the ComplicationData contains a content description. I.e. if
- * {@link #getContentDescription} can succeed.
- */
- @SuppressWarnings("deprecation")
- public boolean hasContentDescription() {
- try {
- return isFieldValidForType(FIELD_CONTENT_DESCRIPTION, mType)
- && (mFields.getParcelable(FIELD_CONTENT_DESCRIPTION) != null);
- } catch (BadParcelableException e) {
- return false;
- }
- }
-
- /**
- * Returns the <i>content description </i> field for this complication, for screen readers. This
- * usually describes the image, but may also describe the overall complication.
- *
- * <p>Valid for all non-empty types.
- */
- @Nullable
- public ComplicationText getContentDescription() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_CONTENT_DESCRIPTION, mType);
- return getParcelableField(FIELD_CONTENT_DESCRIPTION);
- }
-
- /**
- * Returns the element weights for this complication.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_WEIGHTED_ELEMENTS}.
- * Otherwise returns null.
- */
- @Nullable
- public float[] getElementWeights() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_WEIGHTS, mType);
- return mFields.getFloatArray(FIELD_ELEMENT_WEIGHTS);
- }
-
- /**
- * Returns the element colors for this complication.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_WEIGHTED_ELEMENTS}.
- * Otherwise returns null.
- */
- @Nullable
- public int[] getElementColors() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_COLORS, mType);
- return mFields.getIntArray(FIELD_ELEMENT_COLORS);
- }
-
- /**
- * Returns the background color to use between elements for this complication.
- *
- * <p>Valid only if the type of this complication data is {@link #TYPE_WEIGHTED_ELEMENTS}.
- * Otherwise returns 0.
- */
- @ColorInt
- public int getElementBackgroundColor() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_BACKGROUND_COLOR, mType);
- return mFields.getInt(FIELD_ELEMENT_BACKGROUND_COLOR);
- }
-
- /**
- * Returns the placeholder ComplicationData if there is one or `null`.
- */
- @Nullable
- public ComplicationData getPlaceholder() {
- checkFieldValidForType(FIELD_PLACEHOLDER_FIELDS, mType);
- checkFieldValidForType(FIELD_PLACEHOLDER_TYPE, mType);
- if (!mFields.containsKey(FIELD_PLACEHOLDER_FIELDS)
- || !mFields.containsKey(FIELD_PLACEHOLDER_TYPE)) {
- return null;
- }
- return new ComplicationData(mFields.getInt(FIELD_PLACEHOLDER_TYPE),
- mFields.getBundle(FIELD_PLACEHOLDER_FIELDS));
- }
-
- /** Returns the bytes of the proto layout. */
- @Nullable
- public byte[] getInteractiveLayout() {
- return mFields.getByteArray(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE);
- }
-
- /**
- * Returns the list style hint.
- *
- * <p>Valid only if the type of this complication data is {@link #EXP_TYPE_LIST}. Otherwise
- * returns zero.
- */
- public int getListStyleHint() {
- checkFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, mType);
- return mFields.getInt(EXP_FIELD_LIST_STYLE_HINT);
- }
-
- /** Returns the bytes of the ambient proto layout. */
- @Nullable
- public byte[] getAmbientLayout() {
- return mFields.getByteArray(EXP_FIELD_PROTO_LAYOUT_AMBIENT);
- }
-
- /** Returns the bytes of the proto layout resources. */
- @Nullable
- public byte[] getLayoutResources() {
- return mFields.getByteArray(EXP_FIELD_PROTO_LAYOUT_RESOURCES);
- }
-
- /** Return's the complication's [ComplicationCachePolicy]. */
- @ComplicationPersistencePolicy
- public int getPersistencePolicy() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_PERSISTENCE_POLICY, mType);
- return mFields.getInt(
- FIELD_PERSISTENCE_POLICY, ComplicationPersistencePolicies.CACHING_ALLOWED);
- }
-
- /** Return's the complication's [ComplicationDisplayPolicy]. */
- @ComplicationDisplayPolicy
- public int getDisplayPolicy() {
- checkFieldValidForTypeWithoutThrowingException(FIELD_DISPLAY_POLICY, mType);
- return mFields.getInt(FIELD_DISPLAY_POLICY, ComplicationDisplayPolicies.ALWAYS_DISPLAY);
- }
-
- /**
- * Returns the start time for this complication data (i.e. the first time at which it should
- * be considered active and displayed), this may be 0. See also {@link #isActiveAt(long)}.
- */
- public long getStartDateTimeMillis() {
- return mFields.getLong(FIELD_START_TIME, 0);
- }
-
- /**
- * Returns the end time for this complication data (i.e. the last time at which it should be
- * considered active and displayed), this may be {@link Long#MAX_VALUE}. See also {@link
- * #isActiveAt(long)}.
- */
- public long getEndDateTimeMillis() {
- return mFields.getLong(FIELD_END_TIME, Long.MAX_VALUE);
- }
-
- /**
- * Returns true if the complication data contains at least one text field with a value that may
- * change based on the current time.
- */
- public boolean isTimeDependent() {
- return isTimeDependentField(FIELD_SHORT_TEXT)
- || isTimeDependentField(FIELD_SHORT_TITLE)
- || isTimeDependentField(FIELD_LONG_TEXT)
- || isTimeDependentField(FIELD_LONG_TITLE);
- }
-
- private boolean isTimeDependentField(String field) {
- ComplicationText text = getParcelableField(field);
-
- return text != null && text.isTimeDependent();
- }
-
- static boolean isFieldValidForType(String field, @ComplicationType int type) {
- String[] requiredFields = REQUIRED_FIELDS.get(type);
- if (requiredFields == null) {
- return false;
- }
- for (String requiredField : requiredFields) {
- if (requiredField.equals(field)) {
- return true;
- }
- }
- for (String optionalField : Objects.requireNonNull(OPTIONAL_FIELDS.get(type))) {
- if (optionalField.equals(field)) {
- return true;
- }
- }
- return false;
- }
-
- private static boolean isTypeSupported(int type) {
- return VALID_TYPES.contains(type);
- }
-
- /**
- * The unparceling logic needs to remain backward compatible.
- */
- private static void checkFieldValidForTypeWithoutThrowingException(
- String field, @ComplicationType int type) {
- if (!isTypeSupported(type)) {
- Log.w(TAG, "Type " + type + " can not be recognized");
- return;
- }
- if (!isFieldValidForType(field, type)) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Field " + field + " is not supported for type " + type);
- }
- }
- }
-
- private static void checkFieldValidForType(String field, @ComplicationType int type) {
- if (!isTypeSupported(type)) {
- throw new IllegalStateException("Type " + type + " can not be recognized");
- }
- if (!isFieldValidForType(field, type)) {
- throw new IllegalStateException(
- "Field " + field + " is not supported for type " + type);
- }
- }
-
- @SuppressWarnings({"TypeParameterUnusedInFormals", "deprecation"})
- private <T extends Parcelable> T getParcelableField(String field) {
- try {
- return mFields.getParcelable(field);
- } catch (BadParcelableException e) {
- Log.w(
- TAG,
- "Could not unparcel ComplicationData. Provider apps must exclude wearable "
- + "support complication classes from proguard.",
- e);
- return null;
- }
- }
-
- @NonNull
- @Override
- public String toString() {
- if (shouldRedact()) {
- return "ComplicationData{" + "mType=" + mType + ", mFields=REDACTED}";
- }
- return toStringNoRedaction();
- }
-
- /** @hide */
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- public String toStringNoRedaction() {
- return "ComplicationData{" + "mType=" + mType + ", mFields=" + mFields + '}';
- }
-
- /** Builder class for {@link ComplicationData}. */
- public static final class Builder {
- @ComplicationType
- final int mType;
- final Bundle mFields;
-
- /** Creates a builder from given {@link ComplicationData}, copying its type and data. */
- @SuppressLint("SyntheticAccessor")
- public Builder(@NonNull ComplicationData data) {
- mType = data.getType();
- mFields = (Bundle) data.mFields.clone();
- }
-
- public Builder(@ComplicationType int type) {
- mType = type;
- mFields = new Bundle();
- if (type == TYPE_SMALL_IMAGE || type == TYPE_LONG_TEXT) {
- setSmallImageStyle(IMAGE_STYLE_PHOTO);
- }
- }
-
- /** Sets the complication's [ComplicationCachePolicy]. */
- @NonNull
- public Builder setPersistencePolicy(@ComplicationPersistencePolicy int cachePolicy) {
- mFields.putInt(FIELD_PERSISTENCE_POLICY, cachePolicy);
- return this;
- }
-
- /** Sets the complication's [ComplicationDisplayPolicy]. */
- @NonNull
- public Builder setDisplayPolicy(@ComplicationDisplayPolicy int displayPolicy) {
- mFields.putInt(FIELD_DISPLAY_POLICY, displayPolicy);
- return this;
- }
-
- /**
- * Sets the start time for this complication data. This is optional for any type.
- *
- * <p>The complication data will be considered inactive (i.e. should not be displayed) if
- * the current time is less than the start time. If not specified, the data is considered
- * active for all time up to the end time (or always active if end time is also not
- * specified).
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setStartDateTimeMillis(long startDateTimeMillis) {
- mFields.putLong(FIELD_START_TIME, startDateTimeMillis);
- return this;
- }
-
- /**
- * Removes the start time for this complication data.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder clearStartDateTime() {
- mFields.remove(FIELD_START_TIME);
- return this;
- }
-
- /**
- * Sets the end time for this complication data. This is optional for any type.
- *
- * <p>The complication data will be considered inactive (i.e. should not be displayed) if
- * the current time is greater than the end time. If not specified, the data is considered
- * active for all time after the start time (or always active if start time is also not
- * specified).
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setEndDateTimeMillis(long endDateTimeMillis) {
- mFields.putLong(FIELD_END_TIME, endDateTimeMillis);
- return this;
- }
-
- /**
- * Removes the end time for this complication data.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder clearEndDateTime() {
- mFields.remove(FIELD_END_TIME);
- return this;
- }
-
- /**
- * Sets the <i>value</i> field. This is required for the {@link #TYPE_RANGED_VALUE} type,
- * and the {@link #TYPE_GOAL_PROGRESS} type. For {@link #TYPE_RANGED_VALUE} value must
- * be in the range [min .. max] for {@link #TYPE_GOAL_PROGRESS} value must be >= and may
- * be greater than target value.
- *
- * <p>Both the {@link #TYPE_RANGED_VALUE} complication and the
- * {@link #TYPE_GOAL_PROGRESS} complication visually present a single value, which is
- * usually a percentage. E.g. you have completed 70% of today's target of 10000 steps.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setRangedValue(float value) {
- putFloatField(FIELD_VALUE, value);
- return this;
- }
-
- /**
- * Sets the <i>value type</i> field which provides meta data about the value. This is
- * optional for the {@link #TYPE_RANGED_VALUE} type.
- */
- @NonNull
- public Builder setRangedValueType(int valueType) {
- putIntField(FIELD_VALUE_TYPE, valueType);
- return this;
- }
-
- /**
- * Sets the <i>min value</i> field. This is required for the {@link #TYPE_RANGED_VALUE}
- * type, and is not valid for any other type. A {@link #TYPE_RANGED_VALUE} complication
- * visually presents a single value, which is usually a percentage. E.g. you have
- * completed 70% of today's target of 10000 steps.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setRangedMinValue(float minValue) {
- putFloatField(FIELD_MIN_VALUE, minValue);
- return this;
- }
-
- /**
- * Sets the <i>max value</i> field. This is required for the {@link #TYPE_RANGED_VALUE}
- * type, and is not valid for any other type.A {@link #TYPE_RANGED_VALUE} complication
- * visually presents a single value, which is usually a percentage. E.g. you have
- * completed 70% of today's target of 10000 steps.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setRangedMaxValue(float maxValue) {
- putFloatField(FIELD_MAX_VALUE, maxValue);
- return this;
- }
-
- /**
- * Sets the <i>targetValue</i> field. This is required for the
- * {@link #TYPE_GOAL_PROGRESS} type, and is not valid for any other type. A
- * {@link #TYPE_GOAL_PROGRESS} complication visually presents a single value, which
- * is usually a percentage. E.g. you have completed 70% of today's target of 10000 steps.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setTargetValue(float targetValue) {
- putFloatField(FIELD_TARGET_VALUE, targetValue);
- return this;
- }
-
- /**
- * Sets the <i>long title</i> field. This is optional for the {@link #TYPE_LONG_TEXT} type,
- * and is not valid for any other type.
- *
- * <p>The value must be provided as a {@link ComplicationText} object, so that
- * time-dependent values may be included.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setLongTitle(@Nullable ComplicationText longTitle) {
- putOrRemoveField(FIELD_LONG_TITLE, longTitle);
- return this;
- }
-
- /**
- * Sets the <i>long text</i> field. This is required for the {@link #TYPE_LONG_TEXT} type,
- * and is not valid for any other type.
- *
- * <p>The value must be provided as a {@link ComplicationText} object, so that
- * time-dependent values may be included.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setLongText(@Nullable ComplicationText longText) {
- putOrRemoveField(FIELD_LONG_TEXT, longText);
- return this;
- }
-
- /**
- * Sets the <i>short title</i> field. This is valid for the {@link #TYPE_SHORT_TEXT}, {@link
- * #TYPE_RANGED_VALUE}, and {@link #TYPE_NO_PERMISSION} types, and is not valid for any
- * other type.
- *
- * <p>The value must be provided as a {@link ComplicationText} object, so that
- * time-dependent values may be included.
- *
- * <p>The length of the text, including any time-dependent values, should not exceed seven
- * characters. If it does, the text may be truncated by the watch face or might not fit in
- * the complication.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setShortTitle(@Nullable ComplicationText shortTitle) {
- putOrRemoveField(FIELD_SHORT_TITLE, shortTitle);
- return this;
- }
-
- /**
- * Sets the <i>short text</i> field. This is required for the {@link #TYPE_SHORT_TEXT} type,
- * is optional for the {@link #TYPE_RANGED_VALUE} and {@link #TYPE_NO_PERMISSION} types, and
- * is not valid for any other type.
- *
- * <p>The value must be provided as a {@link ComplicationText} object, so that
- * time-dependent values may be included.
- *
- * <p>The length of the text, including any time-dependent values, should not exceed seven
- * characters. If it does, the text may be truncated by the watch face or might not fit in
- * the complication.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setShortText(@Nullable ComplicationText shortText) {
- putOrRemoveField(FIELD_SHORT_TEXT, shortText);
- return this;
- }
-
- /**
- * Sets the <i>icon</i> field. This is required for the {@link #TYPE_ICON} type, and is
- * optional for the {@link #TYPE_SHORT_TEXT}, {@link #TYPE_LONG_TEXT}, {@link
- * #TYPE_RANGED_VALUE}, and {@link #TYPE_NO_PERMISSION} types.
- *
- * <p>The provided image must be single-color, so that watch faces can tint it as required.
- *
- * <p>If the icon provided here is not suitable for display in ambient mode with burn-in
- * protection (e.g. if it includes solid blocks of pixels), then a burn-in safe version of
- * the icon must be provided via {@link #setBurnInProtectionIcon}.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setIcon(@Nullable Icon icon) {
- putOrRemoveField(FIELD_ICON, icon);
- return this;
- }
-
- /**
- * Sets the burn-in protection version of the <i>icon</i> field. This should be provided if
- * the <i>icon</i> field is provided, unless the main icon is already safe for use with
- * burn-in protection. This icon should have fewer lit pixels, and should use darker
- * colors to prevent LCD burn in issues.
- *
- * <p>The provided image must be single-color, so that watch faces can tint it as required.
- *
- * <p>The provided image must not contain solid blocks of pixels - it should instead be
- * composed of outlines or lines only.
- *
- * <p>If this field is set, the <i>icon</i> field must also be set.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setBurnInProtectionIcon(@Nullable Icon icon) {
- putOrRemoveField(FIELD_ICON_BURN_IN_PROTECTION, icon);
- return this;
- }
-
- /**
- * Sets the <i>small image</i> field. This is required for the {@link #TYPE_SMALL_IMAGE}
- * type, and is optional for the {@link #TYPE_LONG_TEXT} type.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setSmallImage(@Nullable Icon smallImage) {
- putOrRemoveField(FIELD_SMALL_IMAGE, smallImage);
- return this;
- }
-
- /**
- * Sets the burn-in protection version of the <i>small image</i> field. This should be
- * provided if the <i>small image</i> field is provided, unless the main small image is
- * already safe for use with burn-in protection.
- *
- * <p>The provided image must not contain solid blocks of pixels - it should instead be
- * composed of outlines or lines only.
- *
- * <p>If this field is set, the <i>small image</i> field must also be set.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setBurnInProtectionSmallImage(@Nullable Icon smallImage) {
- putOrRemoveField(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, smallImage);
- return this;
- }
-
- /**
- * Sets the display style for this complication data. This is valid only for types that
- * contain small images, i.e. {@link #TYPE_SMALL_IMAGE} and {@link #TYPE_LONG_TEXT}.
- *
- * <p>This affects how watch faces will draw the image in the complication.
- *
- * <p>If not specified, the default is {@link #IMAGE_STYLE_PHOTO}.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- * @see #IMAGE_STYLE_PHOTO which can be cropped but not recolored.
- * @see #IMAGE_STYLE_ICON which can be recolored but not cropped.
- */
- @NonNull
- public Builder setSmallImageStyle(@ImageStyle int imageStyle) {
- putIntField(FIELD_IMAGE_STYLE, imageStyle);
- return this;
- }
-
- /**
- * Sets the <i>large image</i> field. This is required for the {@link #TYPE_LARGE_IMAGE}
- * type, and is not valid for any other type.
- *
- * <p>The provided image should be suitably sized to fill the screen of the watch.
- *
- * <p>Returns this Builder to allow chaining.
- *
- * @throws IllegalStateException if this field is not valid for the complication type
- */
- @NonNull
- public Builder setLargeImage(@Nullable Icon largeImage) {
- putOrRemoveField(FIELD_LARGE_IMAGE, largeImage);
- return this;
- }
-
- /**
- * Sets the list style hint
- *
- * <p>Valid only if the type of this complication data is {@link #EXP_TYPE_LIST}. Otherwise
- * returns
- * zero.
- */
- @NonNull
- public Builder setListStyleHint(int listStyleHint) {
- putIntField(EXP_FIELD_LIST_STYLE_HINT, listStyleHint);
- return this;
- }
-
- /**
- * Sets the <i>tap action</i> field. This is optional for any non-empty type.
- *
- * <p>The provided {@link PendingIntent} may be fired if the complication is tapped on. Note
- * that some complications might not be tappable, in which case this field will be ignored.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setTapAction(@Nullable PendingIntent pendingIntent) {
- putOrRemoveField(FIELD_TAP_ACTION, pendingIntent);
- return this;
- }
-
- /**
- * Sets the <i>content description</i> field for accessibility. This is optional for any
- * non-empty type. It is recommended to provide a content description whenever the
- * data includes an image.
- *
- * <p>The provided text will be read aloud by a Text-to-speech converter for users who may
- * be vision-impaired. It will be read aloud in addition to any long, short, or range text
- * in the complication.
- *
- * <p>If using to describe an image/icon that is purely stylistic and doesn't convey any
- * information to the user, you may set the image content description to an empty string
- * ("").
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setContentDescription(@Nullable ComplicationText description) {
- putOrRemoveField(FIELD_CONTENT_DESCRIPTION, description);
- return this;
- }
-
- /**
- * Sets whether or not this ComplicationData has been serialized.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setTapActionLostDueToSerialization(boolean tapActionLostDueToSerialization) {
- if (tapActionLostDueToSerialization) {
- mFields.putBoolean(FIELD_TAP_ACTION_LOST, tapActionLostDueToSerialization);
- }
- return this;
- }
-
- /**
- * Sets the placeholder.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @SuppressLint("SyntheticAccessor")
- @NonNull
- public Builder setPlaceholder(@Nullable ComplicationData placeholder) {
- if (placeholder == null) {
- mFields.remove(FIELD_PLACEHOLDER_FIELDS);
- mFields.remove(FIELD_PLACEHOLDER_TYPE);
- } else {
- ComplicationData.checkFieldValidForType(FIELD_PLACEHOLDER_FIELDS, mType);
- mFields.putBundle(FIELD_PLACEHOLDER_FIELDS, placeholder.mFields);
- putIntField(FIELD_PLACEHOLDER_TYPE, placeholder.mType);
- }
- return this;
- }
-
- /**
- * Sets the {@link ComponentName} of the ComplicationDataSourceService that provided this
- * ComplicationData. Generally this field should be set and is only nullable for backwards
- * compatibility.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setDataSource(@Nullable ComponentName provider) {
- putOrRemoveField(FIELD_DATA_SOURCE, provider);
- return this;
- }
-
- /**
- * Sets the ambient proto layout associated with this complication.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setAmbientLayout(@NonNull byte[] ambientProtoLayout) {
- putByteArrayField(EXP_FIELD_PROTO_LAYOUT_AMBIENT, ambientProtoLayout);
- return this;
- }
-
- /**
- * Sets the proto layout associated with this complication.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setInteractiveLayout(@NonNull byte[] protoLayout) {
- putByteArrayField(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, protoLayout);
- return this;
- }
-
- /**
- * Sets the proto layout resources associated with this complication.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setLayoutResources(@NonNull byte[] resources) {
- putByteArrayField(EXP_FIELD_PROTO_LAYOUT_RESOURCES, resources);
- return this;
- }
-
- /**
- * Optional. Sets the color the color ramp should be drawn with.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setColorRamp(@Nullable int[] colorRamp) {
- putOrRemoveField(FIELD_COLOR_RAMP, colorRamp);
- return this;
- }
-
- /**
- * Optional. Sets whether or not the color ramp should be smootly shaded or drawn with
- * steps.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setColorRampIsSmoothShaded(@Nullable Boolean isSmoothShaded) {
- putOrRemoveField(FIELD_COLOR_RAMP_INTERPOLATED, isSmoothShaded);
- return this;
- }
-
- /**
- * Sets the list of {@link ComplicationData} timeline entries.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setListEntryCollection(
- @Nullable Collection<ComplicationData> timelineEntries) {
- if (timelineEntries == null) {
- mFields.remove(EXP_FIELD_LIST_ENTRIES);
- } else {
- mFields.putParcelableArray(
- EXP_FIELD_LIST_ENTRIES,
- timelineEntries.stream()
- .map(
- e -> {
- e.mFields.putInt(EXP_FIELD_LIST_ENTRY_TYPE, e.mType);
- return e.mFields;
- })
- .toArray(Parcelable[]::new));
- }
- return this;
- }
-
- /**
- * Sets the element weights for this complication.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setElementWeights(@Nullable float[] elementWeights) {
- putOrRemoveField(FIELD_ELEMENT_WEIGHTS, elementWeights);
- return this;
- }
-
- /**
- * Sets the element colors for this complication.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setElementColors(@Nullable int[] elementColors) {
- putOrRemoveField(FIELD_ELEMENT_COLORS, elementColors);
- return this;
- }
-
- /**
- * Sets the background color to use between elements for this complication.
- *
- * <p>Returns this Builder to allow chaining.
- */
- @NonNull
- public Builder setElementBackgroundColor(@ColorInt int elementBackgroundColor) {
- putOrRemoveField(FIELD_ELEMENT_BACKGROUND_COLOR, elementBackgroundColor);
- return this;
- }
-
- /**
- * Constructs and returns {@link ComplicationData} with the provided fields. All required
- * fields must be populated before this method is called.
- *
- * @throws IllegalStateException if the required fields have not been populated
- */
- @NonNull
- @SuppressLint("SyntheticAccessor")
- public ComplicationData build() {
- // Validate.
- for (String requiredField : Objects.requireNonNull(REQUIRED_FIELDS.get(mType))) {
- if (!mFields.containsKey(requiredField)) {
- throw new IllegalStateException(
- "Field " + requiredField + " is required for type " + mType);
- }
-
- if (mFields.containsKey(FIELD_ICON_BURN_IN_PROTECTION)
- && !mFields.containsKey(FIELD_ICON)) {
- throw new IllegalStateException(
- "Field ICON must be provided when field ICON_BURN_IN_PROTECTION is"
- + " provided.");
- }
-
- if (mFields.containsKey(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION)
- && !mFields.containsKey(FIELD_SMALL_IMAGE)) {
- throw new IllegalStateException(
- "Field SMALL_IMAGE must be provided when field"
- + " SMALL_IMAGE_BURN_IN_PROTECTION is provided.");
- }
- }
-
- return new ComplicationData(this);
- }
-
- @SuppressLint("SyntheticAccessor")
- private void putIntField(@NonNull String field, int value) {
- ComplicationData.checkFieldValidForType(field, mType);
- mFields.putInt(field, value);
- }
-
- @SuppressLint("SyntheticAccessor")
- private void putFloatField(@NonNull String field, float value) {
- ComplicationData.checkFieldValidForType(field, mType);
- mFields.putFloat(field, value);
- }
-
- @SuppressLint("SyntheticAccessor")
- private void putByteArrayField(@NonNull String field, @NonNull byte[] value) {
- ComplicationData.checkFieldValidForType(field, mType);
- mFields.putByteArray(field, value);
- }
-
- /** Sets the field with obj or removes it if null. */
- @SuppressLint("SyntheticAccessor")
- private void putOrRemoveField(@NonNull String field, @Nullable Object obj) {
- ComplicationData.checkFieldValidForType(field, mType);
- if (obj == null) {
- mFields.remove(field);
- return;
- }
- if (obj instanceof Boolean) {
- mFields.putBoolean(field, (Boolean) obj);
- } else if (obj instanceof Integer) {
- mFields.putInt(field, (Integer) obj);
- } else if (obj instanceof String) {
- mFields.putString(field, (String) obj);
- } else if (obj instanceof Parcelable) {
- mFields.putParcelable(field, (Parcelable) obj);
- } else if (obj instanceof int[]) {
- mFields.putIntArray(field, (int[]) obj);
- } else if (obj instanceof float[]) {
- mFields.putFloatArray(field, (float[]) obj);
- } else {
- throw new IllegalArgumentException("Unexpected object type: " + obj.getClass());
- }
- }
- }
-
- /** Returns whether or not we should redact complication data in toString(). */
- public static boolean shouldRedact() {
- return !Log.isLoggable(TAG, Log.DEBUG);
- }
-
- @NonNull
- static String maybeRedact(@Nullable CharSequence unredacted) {
- if (unredacted == null) {
- return "(null)";
- }
- return maybeRedact(unredacted.toString());
- }
-
- @NonNull
- static String maybeRedact(@NonNull String unredacted) {
- if (!shouldRedact() || unredacted.equals(PLACEHOLDER_STRING)) {
- return unredacted;
- }
- return "REDACTED";
- }
-}
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
new file mode 100644
index 0000000..02dc741
--- /dev/null
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.kt
@@ -0,0 +1,2235 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.wearable.complications
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.os.BadParcelableException
+import android.os.Build
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+import android.util.Log
+import androidx.annotation.ColorInt
+import androidx.annotation.IntDef
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.wear.watchface.complications.data.ComplicationDisplayPolicies
+import androidx.wear.watchface.complications.data.ComplicationDisplayPolicy
+import androidx.wear.watchface.complications.data.ComplicationPersistencePolicies
+import androidx.wear.watchface.complications.data.ComplicationPersistencePolicy
+import java.io.IOException
+import java.io.InvalidObjectException
+import java.io.ObjectInputStream
+import java.io.ObjectOutputStream
+import java.io.Serializable
+
+/**
+ * Container for complication data of all types.
+ *
+ * A [androidx.wear.watchface.complications.ComplicationProviderService] should create
+ * instances of
+ * this class using [ComplicationData.Builder] and send them to the complication system in
+ * response to
+ * [androidx.wear.watchface.complications.ComplicationProviderService.onComplicationRequest].
+ * Depending on the type of complication data, some fields will be required and some will be
+ * optional - see the documentation for each type, and for the builder's set methods, for details.
+ *
+ * A watch face will receive instances of this class as long as providers are configured.
+ *
+ * When rendering the complication data for a given time, the watch face should first call
+ * [isActiveAt] to determine whether the data is valid at that time. See the documentation for each
+ * of the complication types below for details of which fields are expected to be displayed.
+ *
+ * @hide
+ */
+@SuppressLint("BanParcelableUsage")
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class ComplicationData : Parcelable, Serializable {
+ /** @hide */
+ @IntDef(
+ TYPE_EMPTY,
+ TYPE_NOT_CONFIGURED,
+ TYPE_SHORT_TEXT,
+ TYPE_LONG_TEXT,
+ TYPE_RANGED_VALUE,
+ TYPE_ICON,
+ TYPE_SMALL_IMAGE,
+ TYPE_LARGE_IMAGE,
+ TYPE_NO_PERMISSION,
+ TYPE_NO_DATA,
+ TYPE_GOAL_PROGRESS,
+ TYPE_WEIGHTED_ELEMENTS,
+ EXP_TYPE_PROTO_LAYOUT,
+ EXP_TYPE_LIST
+ )
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class ComplicationType
+
+ /** @hide */
+ @IntDef(IMAGE_STYLE_PHOTO, IMAGE_STYLE_ICON)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class ImageStyle
+
+ /** Returns the type of this complication data. */
+ @ComplicationType
+ val type: Int
+
+ private val fields: Bundle
+
+ internal constructor(builder: Builder) {
+ type = builder.type
+ fields = builder.fields
+ }
+
+ internal constructor(type: Int, fields: Bundle) {
+ this.type = type
+ this.fields = fields
+ this.fields.classLoader = javaClass.classLoader
+ }
+
+ internal constructor(input: Parcel) {
+ type = input.readInt()
+ fields = input.readBundle(javaClass.classLoader) ?: run {
+ Log.w(TAG, "ComplicationData parcel input has null bundle.")
+ Bundle()
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ private class SerializedForm
+ @JvmOverloads
+ constructor(
+ private var complicationData: ComplicationData? = null
+ ) : Serializable {
+
+ @Throws(IOException::class)
+ private fun writeObject(oos: ObjectOutputStream) {
+ // Get a copy because it could technically change to null in another thread.
+ val complicationData = this.complicationData!!
+ oos.writeInt(VERSION_NUMBER)
+ val type = complicationData.type
+ oos.writeInt(type)
+ oos.writeInt(complicationData.persistencePolicy)
+ oos.writeInt(complicationData.displayPolicy)
+ if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
+ oos.writeObject(complicationData.longText)
+ }
+ if (isFieldValidForType(FIELD_LONG_TITLE, type)) {
+ oos.writeObject(complicationData.longTitle)
+ }
+ if (isFieldValidForType(FIELD_SHORT_TEXT, type)) {
+ oos.writeObject(complicationData.shortText)
+ }
+ if (isFieldValidForType(FIELD_SHORT_TITLE, type)) {
+ oos.writeObject(complicationData.shortTitle)
+ }
+ if (isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type)) {
+ oos.writeObject(complicationData.contentDescription)
+ }
+ if (isFieldValidForType(FIELD_ICON, type)) {
+ oos.writeObject(IconSerializableHelper.create(complicationData.icon))
+ }
+ if (isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type)) {
+ oos.writeObject(
+ IconSerializableHelper.create(complicationData.burnInProtectionIcon)
+ )
+ }
+ if (isFieldValidForType(FIELD_SMALL_IMAGE, type)) {
+ oos.writeObject(IconSerializableHelper.create(complicationData.smallImage))
+ }
+ if (isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type)) {
+ oos.writeObject(
+ IconSerializableHelper.create(
+ complicationData.burnInProtectionSmallImage
+ )
+ )
+ }
+ if (isFieldValidForType(FIELD_IMAGE_STYLE, type)) {
+ oos.writeInt(complicationData.smallImageStyle)
+ }
+ if (isFieldValidForType(FIELD_LARGE_IMAGE, type)) {
+ oos.writeObject(IconSerializableHelper.create(complicationData.largeImage))
+ }
+ if (isFieldValidForType(FIELD_VALUE, type)) {
+ oos.writeFloat(complicationData.rangedValue)
+ }
+ if (isFieldValidForType(FIELD_VALUE_TYPE, type)) {
+ oos.writeInt(complicationData.rangedValueType)
+ }
+ if (isFieldValidForType(FIELD_MIN_VALUE, type)) {
+ oos.writeFloat(complicationData.rangedMinValue)
+ }
+ if (isFieldValidForType(FIELD_MAX_VALUE, type)) {
+ oos.writeFloat(complicationData.rangedMaxValue)
+ }
+ if (isFieldValidForType(FIELD_TARGET_VALUE, type)) {
+ oos.writeFloat(complicationData.targetValue)
+ }
+ if (isFieldValidForType(FIELD_COLOR_RAMP, type)) {
+ val colors = complicationData.colorRamp
+ if (colors != null) {
+ oos.writeBoolean(true)
+ oos.writeInt(colors.size)
+ for (color in colors) {
+ oos.writeInt(color)
+ }
+ } else {
+ oos.writeBoolean(false)
+ }
+ }
+ if (isFieldValidForType(FIELD_COLOR_RAMP_INTERPOLATED, type)) {
+ val isColorRampSmoothShaded = complicationData.isColorRampInterpolated
+ if (isColorRampSmoothShaded != null) {
+ oos.writeBoolean(true)
+ oos.writeBoolean(isColorRampSmoothShaded)
+ } else {
+ oos.writeBoolean(false)
+ }
+ }
+ if (isFieldValidForType(FIELD_ELEMENT_WEIGHTS, type)) {
+ val weights = complicationData.elementWeights
+ if (weights != null) {
+ oos.writeBoolean(true)
+ oos.writeInt(weights.size)
+ for (weight in weights) {
+ oos.writeFloat(weight)
+ }
+ } else {
+ oos.writeBoolean(false)
+ }
+ }
+ if (isFieldValidForType(FIELD_ELEMENT_COLORS, type)) {
+ val colors = complicationData.elementColors
+ if (colors != null) {
+ oos.writeBoolean(true)
+ oos.writeInt(colors.size)
+ for (color in colors) {
+ oos.writeInt(color)
+ }
+ } else {
+ oos.writeBoolean(false)
+ }
+ }
+ if (isFieldValidForType(FIELD_ELEMENT_BACKGROUND_COLOR, type)) {
+ oos.writeInt(complicationData.elementBackgroundColor)
+ }
+ if (isFieldValidForType(FIELD_START_TIME, type)) {
+ oos.writeLong(complicationData.startDateTimeMillis)
+ }
+ if (isFieldValidForType(FIELD_END_TIME, type)) {
+ oos.writeLong(complicationData.endDateTimeMillis)
+ }
+ oos.writeInt(complicationData.fields.getInt(EXP_FIELD_LIST_ENTRY_TYPE))
+ if (isFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)) {
+ oos.writeInt(complicationData.listStyleHint)
+ }
+ if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, type)) {
+ val bytes = complicationData.interactiveLayout
+ if (bytes == null) {
+ oos.writeInt(0)
+ } else {
+ oos.writeInt(bytes.size)
+ oos.write(bytes)
+ }
+ }
+ if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_AMBIENT, type)) {
+ val bytes = complicationData.ambientLayout
+ if (bytes == null) {
+ oos.writeInt(0)
+ } else {
+ oos.writeInt(bytes.size)
+ oos.write(bytes)
+ }
+ }
+ if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_RESOURCES, type)) {
+ val bytes = complicationData.layoutResources
+ if (bytes == null) {
+ oos.writeInt(0)
+ } else {
+ oos.writeInt(bytes.size)
+ oos.write(bytes)
+ }
+ }
+ if (isFieldValidForType(FIELD_DATA_SOURCE, type)) {
+ val componentName = complicationData.dataSource
+ if (componentName == null) {
+ oos.writeUTF("")
+ } else {
+ oos.writeUTF(componentName.flattenToString())
+ }
+ }
+
+ // TapAction unfortunately can't be serialized, instead we record if we've lost it.
+ oos.writeBoolean(
+ complicationData.hasTapAction() || complicationData.tapActionLostDueToSerialization
+ )
+ val start = complicationData.fields.getLong(FIELD_TIMELINE_START_TIME, -1)
+ oos.writeLong(start)
+ val end = complicationData.fields.getLong(FIELD_TIMELINE_END_TIME, -1)
+ oos.writeLong(end)
+ oos.writeInt(complicationData.fields.getInt(FIELD_TIMELINE_ENTRY_TYPE))
+ val listEntries = complicationData.listEntries
+ val listEntriesLength = listEntries?.size ?: 0
+ oos.writeInt(listEntriesLength)
+ if (listEntries != null) {
+ for (data in listEntries) {
+ SerializedForm(data).writeObject(oos)
+ }
+ }
+ if (isFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)) {
+ val placeholder = complicationData.placeholder
+ if (placeholder == null) {
+ oos.writeBoolean(false)
+ } else {
+ oos.writeBoolean(true)
+ SerializedForm(placeholder).writeObject(oos)
+ }
+ }
+
+ // This has to be last, since it's recursive.
+ val timeline = complicationData.timelineEntries
+ val timelineLength = timeline?.size ?: 0
+ oos.writeInt(timelineLength)
+ if (timeline != null) {
+ for (data in timeline) {
+ SerializedForm(data).writeObject(oos)
+ }
+ }
+ }
+
+ @Throws(IOException::class, ClassNotFoundException::class)
+ private fun readObject(ois: ObjectInputStream) {
+ val versionNumber = ois.readInt()
+ if (versionNumber != VERSION_NUMBER) {
+ // Give up if there's a version skew.
+ throw IOException("Unsupported serialization version number $versionNumber")
+ }
+ val type = ois.readInt()
+ val fields = Bundle()
+ fields.putInt(FIELD_PERSISTENCE_POLICY, ois.readInt())
+ fields.putInt(FIELD_DISPLAY_POLICY, ois.readInt())
+ if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
+ putIfNotNull(fields, FIELD_LONG_TEXT, ois.readObject() as ComplicationText?)
+ }
+ if (isFieldValidForType(FIELD_LONG_TITLE, type)) {
+ putIfNotNull(fields, FIELD_LONG_TITLE, ois.readObject() as ComplicationText?)
+ }
+ if (isFieldValidForType(FIELD_SHORT_TEXT, type)) {
+ putIfNotNull(fields, FIELD_SHORT_TEXT, ois.readObject() as ComplicationText?)
+ }
+ if (isFieldValidForType(FIELD_SHORT_TITLE, type)) {
+ putIfNotNull(fields, FIELD_SHORT_TITLE, ois.readObject() as ComplicationText?)
+ }
+ if (isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type)) {
+ putIfNotNull(
+ fields, FIELD_CONTENT_DESCRIPTION,
+ ois.readObject() as ComplicationText?
+ )
+ }
+ if (isFieldValidForType(FIELD_ICON, type)) {
+ putIfNotNull(fields, FIELD_ICON, IconSerializableHelper.read(ois))
+ }
+ if (isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type)) {
+ putIfNotNull(
+ fields, FIELD_ICON_BURN_IN_PROTECTION,
+ IconSerializableHelper.read(ois)
+ )
+ }
+ if (isFieldValidForType(FIELD_SMALL_IMAGE, type)) {
+ putIfNotNull(fields, FIELD_SMALL_IMAGE, IconSerializableHelper.read(ois))
+ }
+ if (isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type)) {
+ putIfNotNull(
+ fields,
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, IconSerializableHelper.read(ois)
+ )
+ }
+ if (isFieldValidForType(FIELD_IMAGE_STYLE, type)) {
+ fields.putInt(FIELD_IMAGE_STYLE, ois.readInt())
+ }
+ if (isFieldValidForType(FIELD_LARGE_IMAGE, type)) {
+ fields.putParcelable(FIELD_LARGE_IMAGE, IconSerializableHelper.read(ois))
+ }
+ if (isFieldValidForType(FIELD_VALUE, type)) {
+ fields.putFloat(FIELD_VALUE, ois.readFloat())
+ }
+ if (isFieldValidForType(FIELD_VALUE_TYPE, type)) {
+ fields.putInt(FIELD_VALUE_TYPE, ois.readInt())
+ }
+ if (isFieldValidForType(FIELD_MIN_VALUE, type)) {
+ fields.putFloat(FIELD_MIN_VALUE, ois.readFloat())
+ }
+ if (isFieldValidForType(FIELD_MAX_VALUE, type)) {
+ fields.putFloat(FIELD_MAX_VALUE, ois.readFloat())
+ }
+ if (isFieldValidForType(FIELD_TARGET_VALUE, type)) {
+ fields.putFloat(FIELD_TARGET_VALUE, ois.readFloat())
+ }
+ if (isFieldValidForType(FIELD_COLOR_RAMP, type) && ois.readBoolean()) {
+ val numColors = ois.readInt()
+ val colors = IntArray(numColors)
+ for (i in 0 until numColors) {
+ colors[i] = ois.readInt()
+ }
+ fields.putIntArray(FIELD_COLOR_RAMP, colors)
+ }
+ if (isFieldValidForType(FIELD_COLOR_RAMP_INTERPOLATED, type) && ois.readBoolean()) {
+ fields.putBoolean(FIELD_COLOR_RAMP_INTERPOLATED, ois.readBoolean())
+ }
+ if (isFieldValidForType(FIELD_ELEMENT_WEIGHTS, type) && ois.readBoolean()) {
+ val numWeights = ois.readInt()
+ val weights = FloatArray(numWeights)
+ for (i in 0 until numWeights) {
+ weights[i] = ois.readFloat()
+ }
+ fields.putFloatArray(FIELD_ELEMENT_WEIGHTS, weights)
+ }
+ if (isFieldValidForType(FIELD_ELEMENT_COLORS, type) && ois.readBoolean()) {
+ val numColors = ois.readInt()
+ val colors = IntArray(numColors)
+ for (i in 0 until numColors) {
+ colors[i] = ois.readInt()
+ }
+ fields.putIntArray(FIELD_ELEMENT_COLORS, colors)
+ }
+ if (isFieldValidForType(FIELD_ELEMENT_BACKGROUND_COLOR, type)) {
+ fields.putInt(FIELD_ELEMENT_BACKGROUND_COLOR, ois.readInt())
+ }
+ if (isFieldValidForType(FIELD_START_TIME, type)) {
+ fields.putLong(FIELD_START_TIME, ois.readLong())
+ }
+ if (isFieldValidForType(FIELD_END_TIME, type)) {
+ fields.putLong(FIELD_END_TIME, ois.readLong())
+ }
+ val listEntryType = ois.readInt()
+ if (listEntryType != 0) {
+ fields.putInt(EXP_FIELD_LIST_ENTRY_TYPE, listEntryType)
+ }
+ if (isFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)) {
+ fields.putInt(EXP_FIELD_LIST_STYLE_HINT, ois.readInt())
+ }
+ if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, type)) {
+ val length = ois.readInt()
+ if (length > 0) {
+ val protoLayout = ByteArray(length)
+ ois.readFully(protoLayout)
+ fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, protoLayout)
+ }
+ }
+ if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_AMBIENT, type)) {
+ val length = ois.readInt()
+ if (length > 0) {
+ val ambientProtoLayout = ByteArray(length)
+ ois.readFully(ambientProtoLayout)
+ fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_AMBIENT, ambientProtoLayout)
+ }
+ }
+ if (isFieldValidForType(EXP_FIELD_PROTO_LAYOUT_RESOURCES, type)) {
+ val length = ois.readInt()
+ if (length > 0) {
+ val protoLayoutResources = ByteArray(length)
+ ois.readFully(protoLayoutResources)
+ fields.putByteArray(EXP_FIELD_PROTO_LAYOUT_RESOURCES, protoLayoutResources)
+ }
+ }
+ if (isFieldValidForType(FIELD_DATA_SOURCE, type)) {
+ val componentName = ois.readUTF()
+ if (componentName.isEmpty()) {
+ fields.remove(FIELD_DATA_SOURCE)
+ } else {
+ fields.putParcelable(
+ FIELD_DATA_SOURCE, ComponentName.unflattenFromString(componentName)
+ )
+ }
+ }
+ if (ois.readBoolean()) {
+ fields.putBoolean(FIELD_TAP_ACTION_LOST, true)
+ }
+ val start = ois.readLong()
+ if (start != -1L) {
+ fields.putLong(FIELD_TIMELINE_START_TIME, start)
+ }
+ val end = ois.readLong()
+ if (end != -1L) {
+ fields.putLong(FIELD_TIMELINE_END_TIME, end)
+ }
+ val timelineEntryType = ois.readInt()
+ if (timelineEntryType != 0) {
+ fields.putInt(FIELD_TIMELINE_ENTRY_TYPE, timelineEntryType)
+ }
+ val listEntriesLength = ois.readInt()
+ if (listEntriesLength != 0) {
+ val parcels = arrayOfNulls<Parcelable>(listEntriesLength)
+ for (i in 0 until listEntriesLength) {
+ val entry = SerializedForm()
+ entry.readObject(ois)
+ parcels[i] = entry.complicationData!!.fields
+ }
+ fields.putParcelableArray(EXP_FIELD_LIST_ENTRIES, parcels)
+ }
+ if (isFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)) {
+ if (ois.readBoolean()) {
+ val serializedPlaceholder = SerializedForm()
+ serializedPlaceholder.readObject(ois)
+ fields.putInt(
+ FIELD_PLACEHOLDER_TYPE,
+ serializedPlaceholder.complicationData!!.type
+ )
+ fields.putBundle(
+ FIELD_PLACEHOLDER_FIELDS,
+ serializedPlaceholder.complicationData!!.fields
+ )
+ }
+ }
+ val timelineLength = ois.readInt()
+ if (timelineLength != 0) {
+ val parcels = arrayOfNulls<Parcelable>(timelineLength)
+ for (i in 0 until timelineLength) {
+ val entry = SerializedForm()
+ entry.readObject(ois)
+ parcels[i] = entry.complicationData!!.fields
+ }
+ fields.putParcelableArray(FIELD_TIMELINE_ENTRIES, parcels)
+ }
+ complicationData = ComplicationData(type, fields)
+ }
+
+ fun readResolve(): Any = complicationData!!
+
+ companion object {
+ private const val VERSION_NUMBER = 19
+ internal fun putIfNotNull(fields: Bundle, field: String, value: Parcelable?) {
+ if (value != null) {
+ fields.putParcelable(field, value)
+ }
+ }
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ fun writeReplace(): Any = SerializedForm(this)
+
+ @Throws(InvalidObjectException::class)
+ private fun readObject(@Suppress("UNUSED_PARAMETER") stream: ObjectInputStream) {
+ throw InvalidObjectException("Use SerializedForm")
+ }
+
+ override fun describeContents() = 0
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ dest.writeInt(type)
+ dest.writeBundle(fields)
+ }
+
+ /**
+ * Returns true if the complication is active and should be displayed at the given time. If this
+ * returns false, the complication should not be displayed.
+ *
+ * This must be checked for any time for which the complication will be displayed.
+ */
+ fun isActiveAt(dateTimeMillis: Long) =
+ (dateTimeMillis >= fields.getLong(FIELD_START_TIME, 0) &&
+ dateTimeMillis <= fields.getLong(FIELD_END_TIME, Long.MAX_VALUE))
+
+ /**
+ * TapAction unfortunately can't be serialized. Returns true if tapAction has been lost due to
+ * serialization (e.g. due to being read from the local cache). The next complication update
+ * from the system would replace this with one with a tapAction.
+ */
+ val tapActionLostDueToSerialization: Boolean
+ get() = fields.getBoolean(FIELD_TAP_ACTION_LOST)
+
+ /**
+ * For timeline entries. The epoch second at which this timeline entry becomes * valid or `null`
+ * if it's not set.
+ */
+ var timelineStartEpochSecond: Long?
+ get() {
+ val expiresAt = fields.getLong(FIELD_TIMELINE_START_TIME, -1)
+ return if (expiresAt == -1L) {
+ null
+ } else {
+ expiresAt
+ }
+ }
+ set(epochSecond) {
+ if (epochSecond == null) {
+ fields.remove(FIELD_TIMELINE_START_TIME)
+ } else {
+ fields.putLong(FIELD_TIMELINE_START_TIME, epochSecond)
+ }
+ }
+
+ /**
+ * For timeline entries. The epoch second at which this timeline entry becomes invalid or `null`
+ * if it's not set.
+ */
+ var timelineEndEpochSecond: Long?
+ get() {
+ val expiresAt = fields.getLong(FIELD_TIMELINE_END_TIME, -1)
+ return if (expiresAt == -1L) {
+ null
+ } else {
+ expiresAt
+ }
+ }
+ set(epochSecond) {
+ if (epochSecond == null) {
+ fields.remove(FIELD_TIMELINE_END_TIME)
+ } else {
+ fields.putLong(FIELD_TIMELINE_END_TIME, epochSecond)
+ }
+ }
+
+ /** The list of [ComplicationData] timeline entries. */
+ val timelineEntries: List<ComplicationData>?
+ @Suppress("DEPRECATION")
+ get() = fields.getParcelableArray(FIELD_TIMELINE_ENTRIES)
+ ?.map { parcelable ->
+ val bundle = parcelable as Bundle
+ bundle.classLoader = javaClass.classLoader
+ // Use the serialized FIELD_TIMELINE_ENTRY_TYPE or the outer type if it's not there.
+ // Usually the timeline entry type will be the same as the outer type, unless an
+ // entry contains NoDataComplicationData.
+ val type = bundle.getInt(FIELD_TIMELINE_ENTRY_TYPE, type)
+ ComplicationData(type, parcelable)
+ }
+
+ /** Sets the list of [ComplicationData] timeline entries. */
+ fun setTimelineEntryCollection(timelineEntries: Collection<ComplicationData>?) {
+ if (timelineEntries == null) {
+ fields.remove(FIELD_TIMELINE_ENTRIES)
+ } else {
+ fields.putParcelableArray(
+ FIELD_TIMELINE_ENTRIES,
+ timelineEntries.map {
+ // This supports timeline entry of NoDataComplicationData.
+ it.fields.putInt(FIELD_TIMELINE_ENTRY_TYPE, it.type)
+ it.fields
+ }.toTypedArray()
+ )
+ }
+ }
+
+ /** The list of [ComplicationData] entries for a ListComplicationData. */
+ val listEntries: List<ComplicationData>?
+ @Suppress("deprecation")
+ get() = fields.getParcelableArray(EXP_FIELD_LIST_ENTRIES)
+ ?.map { parcelable ->
+ val bundle = parcelable as Bundle
+ bundle.classLoader = javaClass.classLoader
+ ComplicationData(bundle.getInt(EXP_FIELD_LIST_ENTRY_TYPE), bundle)
+ }
+
+ /**
+ * The [ComponentName] of the ComplicationDataSourceService that provided this ComplicationData.
+ */
+ var dataSource: ComponentName?
+ // The safer alternative is not available on Wear OS yet.
+ get() = getParcelableField(FIELD_DATA_SOURCE)
+ set(provider) {
+ fields.putParcelable(FIELD_DATA_SOURCE, provider)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a ranged max value. I.e. if [rangedValue] can
+ * succeed.
+ */
+ fun hasRangedValue(): Boolean = isFieldValidForType(FIELD_VALUE, type)
+
+ /**
+ * Returns the *value* field for this complication.
+ *
+ * Valid only if the type of this complication data is [TYPE_RANGED_VALUE], otherwise returns
+ * zero.
+ */
+ val rangedValue: Float
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_VALUE, type)
+ return fields.getFloat(FIELD_VALUE)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a ranged max type. I.e. if [rangedValueType]
+ * can succeed.
+ */
+ fun hasRangedValueType(): Boolean = isFieldValidForType(FIELD_VALUE_TYPE, type)
+
+ /**
+ * Returns the *value* field for this complication.
+ *
+ * Valid only if the type of this complication data is [TYPE_RANGED_VALUE], otherwise returns
+ * zero.
+ */
+ val rangedValueType: Int
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_VALUE_TYPE, type)
+ return fields.getInt(FIELD_VALUE_TYPE)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a ranged max value. I.e. if [rangedMinValue]
+ * can succeed.
+ */
+ fun hasRangedMinValue(): Boolean = isFieldValidForType(FIELD_MIN_VALUE, type)
+
+ /**
+ * Returns the *min value* field for this complication.
+ *
+ * Valid only if the type of this complication data is [TYPE_RANGED_VALUE], otherwise returns
+ * zero.
+ */
+ val rangedMinValue: Float
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_MIN_VALUE, type)
+ return fields.getFloat(FIELD_MIN_VALUE)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a ranged max value. I.e. if [rangedMaxValue]
+ * can succeed.
+ */
+ fun hasRangedMaxValue(): Boolean = isFieldValidForType(FIELD_MAX_VALUE, type)
+
+ /**
+ * Returns the *max value* field for this complication.
+ *
+ * Valid only if the type of this complication data is [TYPE_RANGED_VALUE], otherwise returns
+ * zero.
+ */
+ val rangedMaxValue: Float
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_MAX_VALUE, type)
+ return fields.getFloat(FIELD_MAX_VALUE)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a ranged max value. I.e. if [targetValue] can
+ * succeed.
+ */
+ fun hasTargetValue(): Boolean = isFieldValidForType(FIELD_TARGET_VALUE, type)
+
+ /**
+ * Returns the *value* field for this complication.
+ *
+ * Valid only if the type of this complication data is [TYPE_GOAL_PROGRESS], otherwise returns
+ * zero.
+ */
+ val targetValue: Float
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_TARGET_VALUE, type)
+ return fields.getFloat(FIELD_TARGET_VALUE)
+ }
+
+ /**
+ * Returns the colors for the progress bar.
+ *
+ * Valid only if the type of this complication data is [TYPE_RANGED_VALUE] or
+ * [TYPE_GOAL_PROGRESS].
+ */
+ @get:ColorInt
+ val colorRamp: IntArray?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_COLOR_RAMP, type)
+ return if (fields.containsKey(FIELD_COLOR_RAMP)) {
+ fields.getIntArray(FIELD_COLOR_RAMP)
+ } else {
+ null
+ }
+ }
+
+ /**
+ * Returns either a boolean where: true means the color ramp colors should be smoothly
+ * interpolated; false means the color ramp should be rendered in equal sized blocks of
+ * solid color; null means this value wasn't set, i.e. the complication is not of type
+ * [TYPE_RANGED_VALUE] or [TYPE_GOAL_PROGRESS].
+ *
+ * Valid only if the type of this complication data is [TYPE_RANGED_VALUE] or
+ * [TYPE_GOAL_PROGRESS].
+ */
+ val isColorRampInterpolated: Boolean?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_COLOR_RAMP_INTERPOLATED, type)
+ return if (fields.containsKey(FIELD_COLOR_RAMP_INTERPOLATED)) {
+ fields.getBoolean(FIELD_COLOR_RAMP_INTERPOLATED)
+ } else {
+ null
+ }
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a short title. I.e. if [shortTitle]
+ * can succeed.
+ */
+ fun hasShortTitle(): Boolean =
+ isFieldValidForType(FIELD_SHORT_TITLE, type) && hasParcelableField(FIELD_SHORT_TITLE)
+
+ /**
+ * Returns the *short title* field for this complication, or `null` if no value was
+ * provided for the field.
+ *
+ * The value is provided as a [ComplicationText] object, from which the text to display
+ * can be obtained for a given point in time.
+ *
+ * The length of the text, including any time-dependent values at any valid time, is expected
+ * to not exceed seven characters. When using this text, the watch face should be able to
+ * display any string of up to seven characters (reducing the text size appropriately if the
+ * string is very wide). Although not expected, it is possible that strings of more than seven
+ * characters might be seen, in which case they may be truncated.
+ *
+ * Valid only if the type of this complication data is [TYPE_SHORT_TEXT], [TYPE_RANGED_VALUE],
+ * or [TYPE_NO_PERMISSION], otherwise returns null.
+ */
+ val shortTitle: ComplicationText?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_SHORT_TITLE, type)
+ return getParcelableFieldOrWarn<ComplicationText>(FIELD_SHORT_TITLE)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains short text. I.e. if [shortText] can succeed.
+ */
+ fun hasShortText(): Boolean =
+ isFieldValidForType(FIELD_SHORT_TEXT, type) && hasParcelableField(FIELD_SHORT_TEXT)
+
+ /**
+ * Returns the *short text* field for this complication, or `null` if no value was
+ * provided for the field.
+ *
+ * The value is provided as a [ComplicationText] object, from which the text to display
+ * can be obtained for a given point in time.
+ *
+ * The length of the text, including any time-dependent values at any valid time, is expected
+ * to not exceed seven characters. When using this text, the watch face should be able to
+ * display any string of up to seven characters (reducing the text size appropriately if the
+ * string is very wide). Although not expected, it is possible that strings of more than seven
+ * characters might be seen, in which case they may be truncated.
+ *
+ * Valid only if the type of this complication data is [TYPE_SHORT_TEXT], [TYPE_RANGED_VALUE],
+ * or [TYPE_NO_PERMISSION], otherwise returns null.
+ */
+ val shortText: ComplicationText?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_SHORT_TEXT, type)
+ return getParcelableFieldOrWarn<ComplicationText>(FIELD_SHORT_TEXT)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a long title. I.e. if [longTitle]
+ * can succeed.
+ */
+ fun hasLongTitle(): Boolean =
+ isFieldValidForType(FIELD_LONG_TITLE, type) && hasParcelableField(FIELD_LONG_TITLE)
+
+ /**
+ * Returns the *long title* field for this complication, or `null` if no value was
+ * provided for the field.
+ *
+ * The value is provided as a [ComplicationText] object, from which the text to display
+ * can be obtained for a given point in time.
+ *
+ * Valid only if the type of this complication data is [TYPE_LONG_TEXT], otherwise returns null.
+ */
+ val longTitle: ComplicationText?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_LONG_TITLE, type)
+ return getParcelableFieldOrWarn<ComplicationText>(FIELD_LONG_TITLE)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains long text. I.e. if [longText] can
+ * succeed.
+ */
+ fun hasLongText(): Boolean =
+ isFieldValidForType(FIELD_LONG_TEXT, type) && hasParcelableField(FIELD_LONG_TEXT)
+
+ /**
+ * Returns the *long text* field for this complication.
+ *
+ * The value is provided as a [ComplicationText] object, from which the text to display
+ * can be obtained for a given point in time.
+ *
+ * Valid only if the type of this complication data is [TYPE_LONG_TEXT], otherwise returns null.
+ */
+ val longText: ComplicationText?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_LONG_TEXT, type)
+ return getParcelableFieldOrWarn<ComplicationText>(FIELD_LONG_TEXT)
+ }
+
+ /** Returns true if the ComplicationData contains an Icon. I.e. if [icon] can succeed. */
+ fun hasIcon(): Boolean =
+ isFieldValidForType(FIELD_ICON, type) && hasParcelableField(FIELD_ICON)
+
+ /**
+ * Returns the *icon* field for this complication, or `null` if no value was provided
+ * for the field. The image returned is expected to be single-color and so may be tinted to
+ * whatever color the watch face requires (but note that
+ * [android.graphics.drawable.Drawable.mutate] should be called before drawables are tinted).
+ *
+ * If the device is in ambient mode, and utilises burn-in protection, then the result of
+ * [burnInProtectionIcon] must be used instead of this.
+ *
+ * Valid for the types [TYPE_SHORT_TEXT], [TYPE_LONG_TEXT], [TYPE_RANGED_VALUE], [TYPE_ICON], or
+ * [TYPE_NO_PERMISSION], otherwise returns null.
+ */
+ val icon: Icon?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_ICON, type)
+ return getParcelableFieldOrWarn<Icon>(FIELD_ICON)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a burn in protection Icon. I.e. if
+ * [burnInProtectionIcon] can succeed.
+ */
+ fun hasBurnInProtectionIcon(): Boolean =
+ isFieldValidForType(FIELD_ICON_BURN_IN_PROTECTION, type) &&
+ hasParcelableField(FIELD_ICON_BURN_IN_PROTECTION)
+
+ /**
+ * Returns the burn-in protection version of the *icon* field for this complication, or
+ * `null` if no such icon was provided. The image returned is expected to be an outline
+ * image suitable for use in ambient mode on screens with burn-in protection. The image is also
+ * expected to be single-color and so may be tinted to whatever color the watch face requires
+ * (but note that [android.graphics.drawable.Drawable.mutate] should be called before drawables
+ * are tinted, and that the color used should be suitable for ambient mode with burn-in
+ * protection).
+ *
+ * If the device is in ambient mode, and utilises burn-in protection, then the result of this
+ * method must be used instead of the result of [icon].
+ *
+ * Valid for the types [TYPE_SHORT_TEXT], [TYPE_LONG_TEXT], [TYPE_RANGED_VALUE], [TYPE_ICON], or
+ * [TYPE_NO_PERMISSION], otherwise returns null.
+ */
+ val burnInProtectionIcon: Icon?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_ICON_BURN_IN_PROTECTION, type)
+ return getParcelableFieldOrWarn<Icon>(FIELD_ICON_BURN_IN_PROTECTION)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a small image. I.e. if [smallImage]
+ * can succeed.
+ */
+ fun hasSmallImage(): Boolean =
+ isFieldValidForType(FIELD_SMALL_IMAGE, type) && hasParcelableField(FIELD_SMALL_IMAGE)
+
+ /**
+ * Returns the *small image* field for this complication, or `null` if no value was
+ * provided for the field.
+ *
+ * This may be either a [IMAGE_STYLE_PHOTO] image, which is expected to
+ * fill the space available, or an [IMAGE_STYLE_ICON] image, which should be
+ * drawn entirely within the space available. Use [smallImageStyle] to determine which
+ * of these applies.
+ *
+ * As this may be any image, it is unlikely to be suitable for display in ambient mode when
+ * burn-in protection is enabled, or in low-bit ambient mode, and should not be rendered under
+ * these circumstances.
+ *
+ * Valid for the types [TYPE_LONG_TEXT] and [TYPE_SMALL_IMAGE].
+ * Otherwise returns null.
+ */
+ val smallImage: Icon?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_SMALL_IMAGE, type)
+ return getParcelableFieldOrWarn<Icon>(FIELD_SMALL_IMAGE)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a burn in protection small image. I.e. if
+ * [burnInProtectionSmallImage] can succeed.
+ *
+ * @throws IllegalStateException for invalid types
+ */
+ fun hasBurnInProtectionSmallImage(): Boolean =
+ isFieldValidForType(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, type) &&
+ hasParcelableField(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION)
+
+ /**
+ * Returns the burn-in protection version of the *small image* field for this complication,
+ * or `null` if no such icon was provided. The image returned is expected to be an outline
+ * image suitable for use in ambient mode on screens with burn-in protection. The image is also
+ * expected to be single-color and so may be tinted to whatever color the watch face requires
+ * (but note that [android.graphics.drawable.Drawable.mutate] should be called before drawables
+ * are tinted, and that the color used should be suitable for ambient mode with burn-in
+ * protection).
+ *
+ * If the device is in ambient mode, and utilises burn-in protection, then the result of this
+ * method must be used instead of the result of [smallImage].
+ *
+ * Valid for the types [TYPE_LONG_TEXT] and [TYPE_SMALL_IMAGE].
+ * Otherwise returns null.
+ */
+ val burnInProtectionSmallImage: Icon?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+ type
+ )
+ return getParcelableFieldOrWarn<Icon>(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION)
+ }
+
+ /**
+ * Returns the *small image style* field for this complication.
+ *
+ * The result of this method should be taken in to account when drawing a small image
+ * complication.
+ *
+ * Valid only for types that contain small images, i.e. [TYPE_SMALL_IMAGE] and [TYPE_LONG_TEXT].
+ * Otherwise returns zero.
+ *
+ * @see IMAGE_STYLE_PHOTO which can be cropped but not recolored.
+ * @see IMAGE_STYLE_ICON which can be recolored but not cropped.
+ */
+ @ImageStyle
+ val smallImageStyle: Int
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_IMAGE_STYLE, type)
+ return fields.getInt(FIELD_IMAGE_STYLE)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a large image. I.e. if [largeImage]
+ * can succeed.
+ */
+ fun hasLargeImage(): Boolean =
+ isFieldValidForType(FIELD_LARGE_IMAGE, type) && hasParcelableField(FIELD_LARGE_IMAGE)
+
+ /**
+ * Returns the *large image* field for this complication. This image is expected to be of a
+ * suitable size to fill the screen of the watch.
+ *
+ * As this may be any image, it is unlikely to be suitable for display in ambient mode when
+ * burn-in protection is enabled, or in low-bit ambient mode, and should not be rendered under
+ * these circumstances.
+ *
+ * Valid only if the type of this complication data is [TYPE_LARGE_IMAGE].
+ * Otherwise returns null.
+ */
+ val largeImage: Icon?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_LARGE_IMAGE, type)
+ return getParcelableFieldOrWarn<Icon>(FIELD_LARGE_IMAGE)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a tap action. I.e. if [tapAction]
+ * can succeed.
+ */
+ fun hasTapAction(): Boolean =
+ isFieldValidForType(FIELD_TAP_ACTION, type) && hasParcelableField(FIELD_TAP_ACTION)
+
+ /**
+ * Returns the *tap action* field for this complication. The result is a [PendingIntent] that
+ * should be fired if the complication is tapped on, assuming the complication is tappable, or
+ * `null` if no tap action has been specified.
+ *
+ * Valid for all non-empty types, otherwise returns null.
+ */
+ val tapAction: PendingIntent?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_TAP_ACTION, type)
+ return getParcelableFieldOrWarn<PendingIntent>(FIELD_TAP_ACTION)
+ }
+
+ /**
+ * Returns true if the ComplicationData contains a content description. I.e. if
+ * [contentDescription] can succeed.
+ */
+ fun hasContentDescription(): Boolean =
+ isFieldValidForType(FIELD_CONTENT_DESCRIPTION, type) &&
+ hasParcelableField(FIELD_CONTENT_DESCRIPTION)
+
+ /**
+ * Returns the *content description * field for this complication, for screen readers. This
+ * usually describes the image, but may also describe the overall complication.
+ *
+ * Valid for all non-empty types.
+ */
+ val contentDescription: ComplicationText?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_CONTENT_DESCRIPTION, type)
+ return getParcelableFieldOrWarn<ComplicationText>(FIELD_CONTENT_DESCRIPTION)
+ }
+
+ /**
+ * Returns the element weights for this complication.
+ *
+ * Valid only if the type of this complication data is [TYPE_WEIGHTED_ELEMENTS].
+ * Otherwise returns null.
+ */
+ val elementWeights: FloatArray?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_WEIGHTS, type)
+ return fields.getFloatArray(FIELD_ELEMENT_WEIGHTS)
+ }
+
+ /**
+ * Returns the element colors for this complication.
+ *
+ * Valid only if the type of this complication data is [TYPE_WEIGHTED_ELEMENTS].
+ * Otherwise returns null.
+ */
+ val elementColors: IntArray?
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_COLORS, type)
+ return fields.getIntArray(FIELD_ELEMENT_COLORS)
+ }
+
+ /**
+ * Returns the background color to use between elements for this complication.
+ *
+ * Valid only if the type of this complication data is [TYPE_WEIGHTED_ELEMENTS].
+ * Otherwise returns 0.
+ */
+ @get:ColorInt
+ val elementBackgroundColor: Int
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_ELEMENT_BACKGROUND_COLOR, type)
+ return fields.getInt(FIELD_ELEMENT_BACKGROUND_COLOR)
+ }
+
+ /** Returns the placeholder ComplicationData if there is one or `null`. */
+ val placeholder: ComplicationData?
+ get() {
+ checkFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)
+ checkFieldValidForType(FIELD_PLACEHOLDER_TYPE, type)
+ return if (
+ !fields.containsKey(FIELD_PLACEHOLDER_FIELDS) ||
+ !fields.containsKey(FIELD_PLACEHOLDER_TYPE)
+ ) {
+ null
+ } else ComplicationData(
+ fields.getInt(FIELD_PLACEHOLDER_TYPE),
+ fields.getBundle(FIELD_PLACEHOLDER_FIELDS)!!
+ )
+ }
+
+ /** Returns the bytes of the proto layout. */
+ val interactiveLayout: ByteArray?
+ get() = fields.getByteArray(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE)
+
+ /**
+ * Returns the list style hint.
+ *
+ * Valid only if the type of this complication data is [EXP_TYPE_LIST]. Otherwise
+ * returns zero.
+ */
+ val listStyleHint: Int
+ get() {
+ checkFieldValidForType(EXP_FIELD_LIST_STYLE_HINT, type)
+ return fields.getInt(EXP_FIELD_LIST_STYLE_HINT)
+ }
+
+ /** Returns the bytes of the ambient proto layout. */
+ val ambientLayout: ByteArray?
+ get() = fields.getByteArray(EXP_FIELD_PROTO_LAYOUT_AMBIENT)
+
+ /** Returns the bytes of the proto layout resources. */
+ val layoutResources: ByteArray?
+ get() = fields.getByteArray(EXP_FIELD_PROTO_LAYOUT_RESOURCES)
+
+ /** Return's the complication's [ComplicationPersistencePolicies]. */
+ @ComplicationPersistencePolicy
+ val persistencePolicy: Int
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_PERSISTENCE_POLICY, type)
+ return fields.getInt(
+ FIELD_PERSISTENCE_POLICY, ComplicationPersistencePolicies.CACHING_ALLOWED
+ )
+ }
+
+ /** Return's the complication's [ComplicationDisplayPolicy]. */
+ @ComplicationDisplayPolicy
+ val displayPolicy: Int
+ get() {
+ checkFieldValidForTypeWithoutThrowingException(FIELD_DISPLAY_POLICY, type)
+ return fields.getInt(FIELD_DISPLAY_POLICY, ComplicationDisplayPolicies.ALWAYS_DISPLAY)
+ }
+
+ /**
+ * Returns the start time for this complication data (i.e. the first time at which it should
+ * be considered active and displayed), this may be 0. See also [isActiveAt].
+ */
+ val startDateTimeMillis: Long
+ get() = fields.getLong(FIELD_START_TIME, 0)
+
+ /**
+ * Returns the end time for this complication data (i.e. the last time at which it should be
+ * considered active and displayed), this may be [Long.MAX_VALUE]. See also [isActiveAt].
+ */
+ val endDateTimeMillis: Long
+ get() = fields.getLong(FIELD_END_TIME, Long.MAX_VALUE)
+
+ /**
+ * Returns true if the complication data contains at least one text field with a value that may
+ * change based on the current time.
+ */
+ val isTimeDependent: Boolean
+ get() = isTimeDependentField(FIELD_SHORT_TEXT) ||
+ isTimeDependentField(FIELD_SHORT_TITLE) ||
+ isTimeDependentField(FIELD_LONG_TEXT) ||
+ isTimeDependentField(FIELD_LONG_TITLE)
+
+ private fun isTimeDependentField(field: String): Boolean {
+ val text = getParcelableFieldOrWarn<ComplicationText>(field)
+ return text != null && text.isTimeDependent
+ }
+
+ private fun <T : Parcelable?> getParcelableField(field: String): T? =
+ try {
+ @Suppress("deprecation")
+ fields.getParcelable<T>(field)
+ } catch (e: BadParcelableException) {
+ null
+ }
+
+ private fun hasParcelableField(field: String) = getParcelableField<Parcelable>(field) != null
+
+ private fun <T : Parcelable?> getParcelableFieldOrWarn(field: String): T? =
+ try {
+ @Suppress("deprecation")
+ fields.getParcelable<T>(field)
+ } catch (e: BadParcelableException) {
+ Log.w(
+ TAG,
+ "Could not unparcel ComplicationData. Provider apps must exclude wearable " +
+ "support complication classes from proguard.",
+ e
+ )
+ null
+ }
+
+ override fun toString() =
+ if (shouldRedact()) {
+ "ComplicationData{mType=$type, mFields=REDACTED}"
+ } else {
+ toStringNoRedaction()
+ }
+
+ /** @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ fun toStringNoRedaction() = "ComplicationData{mType=$type, mFields=$fields}"
+
+ /** Builder class for [ComplicationData]. */
+ class Builder {
+ @ComplicationType
+ internal val type: Int
+
+ internal val fields: Bundle
+
+ /** Creates a builder from given [ComplicationData], copying its type and data. */
+ constructor(data: ComplicationData) {
+ type = data.type
+ fields = data.fields.clone() as Bundle
+ }
+
+ constructor(@ComplicationType type: Int) {
+ this.type = type
+ fields = Bundle()
+ if (type == TYPE_SMALL_IMAGE || type == TYPE_LONG_TEXT) {
+ setSmallImageStyle(IMAGE_STYLE_PHOTO)
+ }
+ }
+
+ /** Sets the complication's [ComplicationPersistencePolicy]. */
+ fun setPersistencePolicy(@ComplicationPersistencePolicy cachePolicy: Int) =
+ apply { fields.putInt(FIELD_PERSISTENCE_POLICY, cachePolicy) }
+
+ /** Sets the complication's [ComplicationDisplayPolicy]. */
+ fun setDisplayPolicy(@ComplicationDisplayPolicy displayPolicy: Int) =
+ apply { fields.putInt(FIELD_DISPLAY_POLICY, displayPolicy) }
+
+ /**
+ * Sets the start time for this complication data. This is optional for any type.
+ *
+ * The complication data will be considered inactive (i.e. should not be displayed) if
+ * the current time is less than the start time. If not specified, the data is considered
+ * active for all time up to the end time (or always active if end time is also not
+ * specified).
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setStartDateTimeMillis(startDateTimeMillis: Long) =
+ apply { fields.putLong(FIELD_START_TIME, startDateTimeMillis) }
+
+ /**
+ * Removes the start time for this complication data.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun clearStartDateTime() = apply { fields.remove(FIELD_START_TIME) }
+
+ /**
+ * Sets the end time for this complication data. This is optional for any type.
+ *
+ * The complication data will be considered inactive (i.e. should not be displayed) if
+ * the current time is greater than the end time. If not specified, the data is considered
+ * active for all time after the start time (or always active if start time is also not
+ * specified).
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setEndDateTimeMillis(endDateTimeMillis: Long) =
+ apply { fields.putLong(FIELD_END_TIME, endDateTimeMillis) }
+
+ /**
+ * Removes the end time for this complication data.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun clearEndDateTime() = apply { fields.remove(FIELD_END_TIME) }
+
+ /**
+ * Sets the *value* field. This is required for the [TYPE_RANGED_VALUE] type,
+ * and the [TYPE_GOAL_PROGRESS] type. For [TYPE_RANGED_VALUE] value must
+ * be in the range [min .. max] for [TYPE_GOAL_PROGRESS] value must be >= and may
+ * be greater than target value.
+ *
+ * Both the [TYPE_RANGED_VALUE] complication and the
+ * [TYPE_GOAL_PROGRESS] complication visually present a single value, which is
+ * usually a percentage. E.g. you have completed 70% of today's target of 10000 steps.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setRangedValue(value: Float) = apply { putFloatField(FIELD_VALUE, value) }
+
+ /**
+ * Sets the *value type* field which provides meta data about the value. This is
+ * optional for the [TYPE_RANGED_VALUE] type.
+ */
+ fun setRangedValueType(valueType: Int) = apply { putIntField(FIELD_VALUE_TYPE, valueType) }
+
+ /**
+ * Sets the *min value* field. This is required for the [TYPE_RANGED_VALUE]
+ * type, and is not valid for any other type. A [TYPE_RANGED_VALUE] complication
+ * visually presents a single value, which is usually a percentage. E.g. you have
+ * completed 70% of today's target of 10000 steps.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setRangedMinValue(minValue: Float) = apply { putFloatField(FIELD_MIN_VALUE, minValue) }
+
+ /**
+ * Sets the *max value* field. This is required for the [TYPE_RANGED_VALUE]
+ * type, and is not valid for any other type. A [TYPE_RANGED_VALUE] complication
+ * visually presents a single value, which is usually a percentage. E.g. you have
+ * completed 70% of today's target of 10000 steps.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setRangedMaxValue(maxValue: Float) = apply { putFloatField(FIELD_MAX_VALUE, maxValue) }
+
+ /**
+ * Sets the *targetValue* field. This is required for the
+ * [TYPE_GOAL_PROGRESS] type, and is not valid for any other type. A
+ * [TYPE_GOAL_PROGRESS] complication visually presents a single value, which
+ * is usually a percentage. E.g. you have completed 70% of today's target of 10000 steps.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setTargetValue(targetValue: Float) =
+ apply { putFloatField(FIELD_TARGET_VALUE, targetValue) }
+
+ /**
+ * Sets the *long title* field. This is optional for the [TYPE_LONG_TEXT] type,
+ * and is not valid for any other type.
+ *
+ * The value must be provided as a [ComplicationText] object, so that
+ * time-dependent values may be included.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setLongTitle(longTitle: ComplicationText?) =
+ apply { putOrRemoveField(FIELD_LONG_TITLE, longTitle) }
+
+ /**
+ * Sets the *long text* field. This is required for the [TYPE_LONG_TEXT] type,
+ * and is not valid for any other type.
+ *
+ * The value must be provided as a [ComplicationText] object, so that
+ * time-dependent values may be included.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setLongText(longText: ComplicationText?) =
+ apply { putOrRemoveField(FIELD_LONG_TEXT, longText) }
+
+ /**
+ * Sets the *short title* field. This is valid for the [TYPE_SHORT_TEXT],
+ * [TYPE_RANGED_VALUE], and [TYPE_NO_PERMISSION] types, and is not valid for any other type.
+ *
+ * The value must be provided as a [ComplicationText] object, so that
+ * time-dependent values may be included.
+ *
+ * The length of the text, including any time-dependent values, should not exceed seven
+ * characters. If it does, the text may be truncated by the watch face or might not fit in
+ * the complication.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setShortTitle(shortTitle: ComplicationText?) =
+ apply { putOrRemoveField(FIELD_SHORT_TITLE, shortTitle) }
+
+ /**
+ * Sets the *short text* field. This is required for the [TYPE_SHORT_TEXT] type,
+ * is optional for the [TYPE_RANGED_VALUE] and [TYPE_NO_PERMISSION] types, and
+ * is not valid for any other type.
+ *
+ * The value must be provided as a [ComplicationText] object, so that
+ * time-dependent values may be included.
+ *
+ * The length of the text, including any time-dependent values, should not exceed seven
+ * characters. If it does, the text may be truncated by the watch face or might not fit in
+ * the complication.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setShortText(shortText: ComplicationText?) =
+ apply { putOrRemoveField(FIELD_SHORT_TEXT, shortText) }
+
+ /**
+ * Sets the *icon* field. This is required for the [TYPE_ICON] type, and is
+ * optional for the [TYPE_SHORT_TEXT], [TYPE_LONG_TEXT], [TYPE_RANGED_VALUE], and
+ * [TYPE_NO_PERMISSION] types.
+ *
+ * The provided image must be single-color, so that watch faces can tint it as required.
+ *
+ * If the icon provided here is not suitable for display in ambient mode with burn-in
+ * protection (e.g. if it includes solid blocks of pixels), then a burn-in safe version of
+ * the icon must be provided via [setBurnInProtectionIcon].
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setIcon(icon: Icon?) = apply { putOrRemoveField(FIELD_ICON, icon) }
+
+ /**
+ * Sets the burn-in protection version of the *icon* field. This should be provided if
+ * the *icon* field is provided, unless the main icon is already safe for use with
+ * burn-in protection. This icon should have fewer lit pixels, and should use darker
+ * colors to prevent LCD burn in issues.
+ *
+ * The provided image must be single-color, so that watch faces can tint it as required.
+ *
+ * The provided image must not contain solid blocks of pixels - it should instead be
+ * composed of outlines or lines only.
+ *
+ * If this field is set, the *icon* field must also be set.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setBurnInProtectionIcon(icon: Icon?) =
+ apply { putOrRemoveField(FIELD_ICON_BURN_IN_PROTECTION, icon) }
+
+ /**
+ * Sets the *small image* field. This is required for the [TYPE_SMALL_IMAGE]
+ * type, and is optional for the [TYPE_LONG_TEXT] type.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setSmallImage(smallImage: Icon?) =
+ apply { putOrRemoveField(FIELD_SMALL_IMAGE, smallImage) }
+
+ /**
+ * Sets the burn-in protection version of the *small image* field. This should be
+ * provided if the *small image* field is provided, unless the main small image is
+ * already safe for use with burn-in protection.
+ *
+ * The provided image must not contain solid blocks of pixels - it should instead be
+ * composed of outlines or lines only.
+ *
+ * If this field is set, the *small image* field must also be set.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setBurnInProtectionSmallImage(smallImage: Icon?) =
+ apply { putOrRemoveField(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION, smallImage) }
+
+ /**
+ * Sets the display style for this complication data. This is valid only for types that
+ * contain small images, i.e. [TYPE_SMALL_IMAGE] and [TYPE_LONG_TEXT].
+ *
+ * This affects how watch faces will draw the image in the complication.
+ *
+ * If not specified, the default is [IMAGE_STYLE_PHOTO].
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ * @see .IMAGE_STYLE_PHOTO which can be cropped but not recolored.
+ *
+ * @see .IMAGE_STYLE_ICON which can be recolored but not cropped.
+ */
+ fun setSmallImageStyle(@ImageStyle imageStyle: Int) =
+ apply { putIntField(FIELD_IMAGE_STYLE, imageStyle) }
+
+ /**
+ * Sets the *large image* field. This is required for the [TYPE_LARGE_IMAGE]
+ * type, and is not valid for any other type.
+ *
+ * The provided image should be suitably sized to fill the screen of the watch.
+ *
+ * Returns this Builder to allow chaining.
+ *
+ * @throws IllegalStateException if this field is not valid for the complication type
+ */
+ fun setLargeImage(largeImage: Icon?) =
+ apply { putOrRemoveField(FIELD_LARGE_IMAGE, largeImage) }
+
+ /**
+ * Sets the list style hint
+ *
+ * Valid only if the type of this complication data is [EXP_TYPE_LIST]. Otherwise
+ * returns
+ * zero.
+ */
+ fun setListStyleHint(listStyleHint: Int) =
+ apply { putIntField(EXP_FIELD_LIST_STYLE_HINT, listStyleHint) }
+
+ /**
+ * Sets the *tap action* field. This is optional for any non-empty type.
+ *
+ * The provided [PendingIntent] may be fired if the complication is tapped on. Note
+ * that some complications might not be tappable, in which case this field will be ignored.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setTapAction(pendingIntent: PendingIntent?) =
+ apply { putOrRemoveField(FIELD_TAP_ACTION, pendingIntent) }
+
+ /**
+ * Sets the *content description* field for accessibility. This is optional for any
+ * non-empty type. It is recommended to provide a content description whenever the
+ * data includes an image.
+ *
+ * The provided text will be read aloud by a Text-to-speech converter for users who may
+ * be vision-impaired. It will be read aloud in addition to any long, short, or range text
+ * in the complication.
+ *
+ * If using to describe an image/icon that is purely stylistic and doesn't convey any
+ * information to the user, you may set the image content description to an empty string
+ * ("").
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setContentDescription(description: ComplicationText?) =
+ apply { putOrRemoveField(FIELD_CONTENT_DESCRIPTION, description) }
+
+ /**
+ * Sets whether or not this ComplicationData has been serialized.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setTapActionLostDueToSerialization(tapActionLostDueToSerialization: Boolean) = apply {
+ if (tapActionLostDueToSerialization) {
+ fields.putBoolean(FIELD_TAP_ACTION_LOST, tapActionLostDueToSerialization)
+ }
+ }
+
+ /**
+ * Sets the placeholder.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setPlaceholder(placeholder: ComplicationData?) = apply {
+ if (placeholder == null) {
+ fields.remove(FIELD_PLACEHOLDER_FIELDS)
+ fields.remove(FIELD_PLACEHOLDER_TYPE)
+ } else {
+ checkFieldValidForType(FIELD_PLACEHOLDER_FIELDS, type)
+ fields.putBundle(FIELD_PLACEHOLDER_FIELDS, placeholder.fields)
+ putIntField(FIELD_PLACEHOLDER_TYPE, placeholder.type)
+ }
+ }
+
+ /**
+ * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
+ * ComplicationData. Generally this field should be set and is only nullable for backwards
+ * compatibility.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setDataSource(provider: ComponentName?) =
+ apply { putOrRemoveField(FIELD_DATA_SOURCE, provider) }
+
+ /**
+ * Sets the ambient proto layout associated with this complication.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setAmbientLayout(ambientProtoLayout: ByteArray) =
+ apply { putByteArrayField(EXP_FIELD_PROTO_LAYOUT_AMBIENT, ambientProtoLayout) }
+
+ /**
+ * Sets the proto layout associated with this complication.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setInteractiveLayout(protoLayout: ByteArray) =
+ apply { putByteArrayField(EXP_FIELD_PROTO_LAYOUT_INTERACTIVE, protoLayout) }
+
+ /**
+ * Sets the proto layout resources associated with this complication.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setLayoutResources(resources: ByteArray) =
+ apply { putByteArrayField(EXP_FIELD_PROTO_LAYOUT_RESOURCES, resources) }
+
+ /**
+ * Optional. Sets the color the color ramp should be drawn with.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setColorRamp(colorRamp: IntArray?) =
+ apply { putOrRemoveField(FIELD_COLOR_RAMP, colorRamp) }
+
+ /**
+ * Optional. Sets whether or not the color ramp should be smoothly shaded or drawn with
+ * steps.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setColorRampIsSmoothShaded(isSmoothShaded: Boolean?) =
+ apply { putOrRemoveField(FIELD_COLOR_RAMP_INTERPOLATED, isSmoothShaded) }
+
+ /**
+ * Sets the list of [ComplicationData] timeline entries.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setListEntryCollection(
+ timelineEntries: Collection<ComplicationData>?
+ ) = apply {
+ if (timelineEntries == null) {
+ fields.remove(EXP_FIELD_LIST_ENTRIES)
+ } else {
+ fields.putParcelableArray(
+ EXP_FIELD_LIST_ENTRIES,
+ timelineEntries.map { data ->
+ data.fields.putInt(EXP_FIELD_LIST_ENTRY_TYPE, data.type)
+ data.fields
+ }.toTypedArray()
+ )
+ }
+ }
+
+ /**
+ * Sets the element weights for this complication.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setElementWeights(elementWeights: FloatArray?) =
+ apply { putOrRemoveField(FIELD_ELEMENT_WEIGHTS, elementWeights) }
+
+ /**
+ * Sets the element colors for this complication.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setElementColors(elementColors: IntArray?) =
+ apply { putOrRemoveField(FIELD_ELEMENT_COLORS, elementColors) }
+
+ /**
+ * Sets the background color to use between elements for this complication.
+ *
+ * Returns this Builder to allow chaining.
+ */
+ fun setElementBackgroundColor(@ColorInt elementBackgroundColor: Int) =
+ apply { putOrRemoveField(FIELD_ELEMENT_BACKGROUND_COLOR, elementBackgroundColor) }
+
+ /**
+ * Constructs and returns [ComplicationData] with the provided fields. All required
+ * fields must be populated before this method is called.
+ *
+ * @throws IllegalStateException if the required fields have not been populated
+ */
+ fun build(): ComplicationData {
+ // Validate.
+ for (requiredField in REQUIRED_FIELDS[type]!!) {
+ check(fields.containsKey(requiredField)) {
+ "Field $requiredField is required for type $type"
+ }
+ check(
+ !(fields.containsKey(FIELD_ICON_BURN_IN_PROTECTION) &&
+ !fields.containsKey(FIELD_ICON))
+ ) {
+ "Field ICON must be provided when field ICON_BURN_IN_PROTECTION is provided."
+ }
+ check(
+ !(fields.containsKey(FIELD_SMALL_IMAGE_BURN_IN_PROTECTION) &&
+ !fields.containsKey(FIELD_SMALL_IMAGE))
+ ) {
+ "Field SMALL_IMAGE must be provided when field SMALL_IMAGE_BURN_IN_PROTECTION" +
+ " is provided."
+ }
+ }
+ return ComplicationData(this)
+ }
+
+ private fun putIntField(field: String, value: Int) {
+ checkFieldValidForType(field, type)
+ fields.putInt(field, value)
+ }
+
+ private fun putFloatField(field: String, value: Float) {
+ checkFieldValidForType(field, type)
+ fields.putFloat(field, value)
+ }
+
+ private fun putByteArrayField(field: String, value: ByteArray) {
+ checkFieldValidForType(field, type)
+ fields.putByteArray(field, value)
+ }
+
+ /** Sets the field with obj or removes it if null. */
+ private fun putOrRemoveField(field: String, obj: Any?) {
+ checkFieldValidForType(field, type)
+ if (obj == null) {
+ fields.remove(field)
+ return
+ }
+ when (obj) {
+ is Boolean -> fields.putBoolean(field, obj)
+ is Int -> fields.putInt(field, obj)
+ is String -> fields.putString(field, obj)
+ is Parcelable -> fields.putParcelable(field, obj)
+ is IntArray -> fields.putIntArray(field, obj)
+ is FloatArray -> fields.putFloatArray(field, obj)
+ else -> throw IllegalArgumentException("Unexpected object type: " + obj.javaClass)
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "ComplicationData"
+ const val PLACEHOLDER_STRING = "__placeholder__"
+
+ /**
+ * Type sent when a complication does not have a provider configured. The system will send
+ * data of this type to watch faces when the user has not chosen a provider for an active
+ * complication, and the watch face has not set a default provider. Providers cannot send
+ * data of this type.
+ *
+ * No fields may be populated for complication data of this type.
+ */
+ const val TYPE_NOT_CONFIGURED = 1
+
+ /**
+ * Type sent when the user has specified that an active complication should have no
+ * provider, i.e. when the user has chosen "Empty" in the provider chooser. Providers cannot
+ * send data of this type.
+ *
+ * No fields may be populated for complication data of this type.
+ */
+ const val TYPE_EMPTY = 2
+
+ /**
+ * Type that can be sent by any provider, regardless of the configured type, when the
+ * provider has no data to be displayed. Watch faces may choose whether to render this in
+ * some way or leave the slot empty.
+ *
+ * No fields may be populated for complication data of this type.
+ */
+ const val TYPE_NO_DATA = 10
+
+ /**
+ * Type used for complications where the primary piece of data is a short piece of text
+ * (expected to be no more than seven characters in length). The short text may be
+ * accompanied by an icon or a short title (or both, but if both are provided then a watch
+ * face may choose to display only one).
+ *
+ * The *short text* field is required for this type, and is expected to always be
+ * displayed.
+ *
+ * The *icon* (and *burnInProtectionIcon*) and *short title* fields are
+ * optional for this type. If only one of these is provided, it is expected that it will be
+ * displayed. If both are provided, it is expected that one of these will be displayed.
+ */
+ const val TYPE_SHORT_TEXT = 3
+
+ /**
+ * Type used for complications where the primary piece of data is a piece of text. The text
+ * may be accompanied by an icon and/or a title.
+ *
+ * The *long text* field is required for this type, and is expected to always be
+ * displayed.
+ *
+ * The *long title* field is optional for this type. If provided, it is expected that
+ * this field will be displayed.
+ *
+ * The *icon* (and *burnInProtectionIcon*) and *small image* fields are also
+ * optional for this type. If provided, at least one of these should be displayed.
+ */
+ const val TYPE_LONG_TEXT = 4
+
+ /**
+ * Type used for complications including a numerical value within a range, such as a
+ * percentage. The value may be accompanied by an icon and/or short text and title.
+ *
+ * The *value*, *min value*, and *max value* fields are required for this
+ * type, and the value within the range is expected to always be displayed.
+ *
+ * The *icon* (and *burnInProtectionIcon*), *short title*, and *short
+ * text* fields are optional for this type, but at least one must be defined. The watch face
+ * may choose which of these fields to display, if any.
+ */
+ const val TYPE_RANGED_VALUE = 5
+
+ /**
+ * Type used for complications which consist only of a tintable icon.
+ *
+ * The *icon* field is required for this type, and is expected to always be displayed,
+ * unless the device is in ambient mode with burn-in protection enabled, in which case the
+ * *burnInProtectionIcon* field should be used instead.
+ *
+ * The contentDescription field is recommended for this type. Use it to describe what data
+ * the icon represents. If the icon is purely stylistic, and does not convey any information
+ * to the user, then enter the empty string as the contentDescription.
+ *
+ * No other fields are valid for this type.
+ */
+ const val TYPE_ICON = 6
+
+ /**
+ * Type used for complications which consist only of a small image.
+ *
+ * The *small image* field is required for this type, and is expected to always be
+ * displayed, unless the device is in ambient mode, in which case either nothing or the
+ * *burnInProtectionSmallImage* field may be used instead.
+ *
+ * The contentDescription field is recommended for this type. Use it to describe what data
+ * the image represents. If the image is purely stylistic, and does not convey any
+ * information to the user, then enter the empty string as the contentDescription.
+ *
+ * No other fields are valid for this type.
+ */
+ const val TYPE_SMALL_IMAGE = 7
+
+ /**
+ * Type used for complications which consist only of a large image. A large image here is
+ * one that could be used to fill the watch face, for example as the background.
+ *
+ * The *large image* field is required for this type, and is expected to always be
+ * displayed, unless the device is in ambient mode.
+ *
+ * The contentDescription field is recommended for this type. Use it to describe what data
+ * the image represents. If the image is purely stylistic, and does not convey any
+ * information to the user, then enter the empty string as the contentDescription.
+ *
+ * No other fields are valid for this type.
+ */
+ const val TYPE_LARGE_IMAGE = 8
+
+ /**
+ * Type sent by the system when the watch face does not have permission to receive
+ * complication data.
+ *
+ * Fields will be populated to allow the data to be rendered as if it were of
+ * [TYPE_SHORT_TEXT] or [TYPE_ICON] for consistency and convenience, but watch faces may
+ * render this as they see fit.
+ *
+ * It is recommended that, where possible, tapping on the complication when in this state
+ * should trigger a permission request.
+ */
+ const val TYPE_NO_PERMISSION = 9
+
+ /**
+ * Type used for complications which indicate progress towards a goal. The value may be
+ * accompanied by an icon and/or short text and title.
+ *
+ * The *value*, and *target value* fields are required for this type, and the
+ * value is expected to always be displayed. The value must be >= 0 and may be > target
+ * value. E.g. 15000 out of a target of 10000 steps.
+ *
+ * The *icon* (and *burnInProtectionIcon*), *short title*, and *short
+ * text* fields are optional for this type, but at least one must be defined. The watch face
+ * may choose which of these fields to display, if any.
+ */
+ const val TYPE_GOAL_PROGRESS = 13
+
+ /**
+ * Type used for complications to display a series of weighted values e.g. in a pie chart.
+ * The weighted values may be accompanied by an icon and/or short text and title.
+ *
+ * The *element weights* and *element colors* fields are required for this type,
+ * and the value within the range is expected to always be displayed.
+ *
+ * The *icon* (and *burnInProtectionIcon*), *short title*, and *short
+ * text* fields are optional for this type, but at least one must be defined. The watch face
+ * may choose which of these fields to display, if any.
+ */
+ const val TYPE_WEIGHTED_ELEMENTS = 14
+
+ // The following types are experimental, and they have negative IDs.
+ /** Type that specifies a proto layout based complication. */
+ const val EXP_TYPE_PROTO_LAYOUT = -11
+
+ /** Type that specifies a list of complication values. E.g. to support linear 3. */
+ const val EXP_TYPE_LIST = -12
+
+ /**
+ * Style for small images which are photos that are expected to fill the space available.
+ * Images of this style may be cropped to fit the shape of the complication - in particular,
+ * the image may be cropped to a circle. Photos my not be recolored.
+ *
+ * This is the default value.
+ */
+ const val IMAGE_STYLE_PHOTO = 1
+
+ /**
+ * Style for small images that have a transparent background and are expected to be drawn
+ * entirely within the space available, such as a launcher icon. Watch faces may add padding
+ * when drawing these images, but should never crop these images. Icons may be recolored to
+ * fit the complication style.
+ */
+ const val IMAGE_STYLE_ICON = 2
+ private const val FIELD_COLOR_RAMP = "COLOR_RAMP"
+ private const val FIELD_COLOR_RAMP_INTERPOLATED = "COLOR_RAMP_INTERPOLATED"
+ private const val FIELD_DATA_SOURCE = "FIELD_DATA_SOURCE"
+ private const val FIELD_DISPLAY_POLICY = "DISPLAY_POLICY"
+ private const val FIELD_ELEMENT_BACKGROUND_COLOR = "ELEMENT_BACKGROUND_COLOR"
+ private const val FIELD_ELEMENT_COLORS = "ELEMENT_COLORS"
+ private const val FIELD_ELEMENT_WEIGHTS = "ELEMENT_WEIGHTS"
+ private const val FIELD_END_TIME = "END_TIME"
+ private const val FIELD_ICON = "ICON"
+ private const val FIELD_ICON_BURN_IN_PROTECTION = "ICON_BURN_IN_PROTECTION"
+ private const val FIELD_IMAGE_STYLE = "IMAGE_STYLE"
+ private const val FIELD_LARGE_IMAGE = "LARGE_IMAGE"
+ private const val FIELD_LONG_TITLE = "LONG_TITLE"
+ private const val FIELD_LONG_TEXT = "LONG_TEXT"
+ private const val FIELD_MAX_VALUE = "MAX_VALUE"
+ private const val FIELD_MIN_VALUE = "MIN_VALUE"
+ private const val FIELD_PERSISTENCE_POLICY = "PERSISTENCE_POLICY"
+ private const val FIELD_PLACEHOLDER_FIELDS = "PLACEHOLDER_FIELDS"
+ private const val FIELD_PLACEHOLDER_TYPE = "PLACEHOLDER_TYPE"
+ private const val FIELD_SMALL_IMAGE = "SMALL_IMAGE"
+ private const val FIELD_SMALL_IMAGE_BURN_IN_PROTECTION = "SMALL_IMAGE_BURN_IN_PROTECTION"
+ private const val FIELD_SHORT_TITLE = "SHORT_TITLE"
+ private const val FIELD_SHORT_TEXT = "SHORT_TEXT"
+ private const val FIELD_START_TIME = "START_TIME"
+ private const val FIELD_TAP_ACTION = "TAP_ACTION"
+ private const val FIELD_TAP_ACTION_LOST = "FIELD_TAP_ACTION_LOST"
+ private const val FIELD_TARGET_VALUE = "TARGET_VALUE"
+ private const val FIELD_TIMELINE_START_TIME = "TIMELINE_START_TIME"
+ private const val FIELD_TIMELINE_END_TIME = "TIMELINE_END_TIME"
+ private const val FIELD_TIMELINE_ENTRIES = "TIMELINE"
+ private const val FIELD_TIMELINE_ENTRY_TYPE = "TIMELINE_ENTRY_TYPE"
+ private const val FIELD_VALUE = "VALUE"
+ private const val FIELD_VALUE_TYPE = "VALUE_TYPE"
+
+ // Experimental fields, these are subject to change without notice.
+ private const val EXP_FIELD_LIST_ENTRIES = "EXP_LIST_ENTRIES"
+ private const val EXP_FIELD_LIST_ENTRY_TYPE = "EXP_LIST_ENTRY_TYPE"
+ private const val EXP_FIELD_LIST_STYLE_HINT = "EXP_LIST_STYLE_HINT"
+ private const val EXP_FIELD_PROTO_LAYOUT_AMBIENT = "EXP_FIELD_PROTO_LAYOUT_AMBIENT"
+ private const val EXP_FIELD_PROTO_LAYOUT_INTERACTIVE = "EXP_FIELD_PROTO_LAYOUT_INTERACTIVE"
+ private const val EXP_FIELD_PROTO_LAYOUT_RESOURCES = "EXP_FIELD_PROTO_LAYOUT_RESOURCES"
+
+ // Originally it was planned to support both content and image content descriptions.
+ private const val FIELD_CONTENT_DESCRIPTION = "IMAGE_CONTENT_DESCRIPTION"
+
+ // The set of valid types.
+ private val VALID_TYPES: Set<Int> = setOf(
+ TYPE_NOT_CONFIGURED,
+ TYPE_EMPTY,
+ TYPE_SHORT_TEXT,
+ TYPE_LONG_TEXT,
+ TYPE_RANGED_VALUE,
+ TYPE_ICON,
+ TYPE_SMALL_IMAGE,
+ TYPE_LARGE_IMAGE,
+ TYPE_NO_PERMISSION,
+ TYPE_NO_DATA,
+ EXP_TYPE_PROTO_LAYOUT,
+ EXP_TYPE_LIST,
+ TYPE_GOAL_PROGRESS,
+ TYPE_WEIGHTED_ELEMENTS,
+ )
+
+ // Used for validation. REQUIRED_FIELDS[i] is a list containing all the fields which must be
+ // populated for @ComplicationType i.
+ private val REQUIRED_FIELDS: Map<Int, Set<String>> = mapOf(
+ TYPE_NOT_CONFIGURED to setOf(),
+ TYPE_EMPTY to setOf(),
+ TYPE_SHORT_TEXT to setOf(FIELD_SHORT_TEXT),
+ TYPE_LONG_TEXT to setOf(FIELD_LONG_TEXT),
+ TYPE_RANGED_VALUE to setOf(FIELD_VALUE, FIELD_MIN_VALUE, FIELD_MAX_VALUE),
+ TYPE_ICON to setOf(FIELD_ICON),
+ TYPE_SMALL_IMAGE to setOf(FIELD_SMALL_IMAGE, FIELD_IMAGE_STYLE),
+ TYPE_LARGE_IMAGE to setOf(FIELD_LARGE_IMAGE),
+ TYPE_NO_PERMISSION to setOf(),
+ TYPE_NO_DATA to setOf(),
+ EXP_TYPE_PROTO_LAYOUT to setOf(
+ EXP_FIELD_PROTO_LAYOUT_AMBIENT,
+ EXP_FIELD_PROTO_LAYOUT_INTERACTIVE,
+ EXP_FIELD_PROTO_LAYOUT_RESOURCES
+ ),
+ EXP_TYPE_LIST to setOf(EXP_FIELD_LIST_ENTRIES),
+ TYPE_GOAL_PROGRESS to setOf(FIELD_VALUE, FIELD_TARGET_VALUE),
+ TYPE_WEIGHTED_ELEMENTS to setOf(
+ FIELD_ELEMENT_WEIGHTS,
+ FIELD_ELEMENT_COLORS,
+ FIELD_ELEMENT_BACKGROUND_COLOR
+ ),
+ )
+
+ // Used for validation. OPTIONAL_FIELDS[i] is an array containing all the fields which are
+ // valid but not required for type i.
+ private val OPTIONAL_FIELDS: Map<Int, Set<String>> = mapOf(
+ TYPE_NOT_CONFIGURED to setOf(),
+ TYPE_EMPTY to setOf(),
+ TYPE_SHORT_TEXT to setOf(
+ FIELD_SHORT_TITLE,
+ FIELD_ICON,
+ FIELD_ICON_BURN_IN_PROTECTION,
+ FIELD_SMALL_IMAGE,
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+ FIELD_IMAGE_STYLE,
+ FIELD_TAP_ACTION,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ TYPE_LONG_TEXT to setOf(
+ FIELD_LONG_TITLE,
+ FIELD_ICON,
+ FIELD_ICON_BURN_IN_PROTECTION,
+ FIELD_SMALL_IMAGE,
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+ FIELD_IMAGE_STYLE,
+ FIELD_TAP_ACTION,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ TYPE_RANGED_VALUE to setOf(
+ FIELD_SHORT_TEXT,
+ FIELD_SHORT_TITLE,
+ FIELD_ICON,
+ FIELD_ICON_BURN_IN_PROTECTION,
+ FIELD_SMALL_IMAGE,
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+ FIELD_IMAGE_STYLE,
+ FIELD_TAP_ACTION,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_COLOR_RAMP,
+ FIELD_COLOR_RAMP_INTERPOLATED,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY,
+ FIELD_VALUE_TYPE
+ ),
+ TYPE_ICON to setOf(
+ FIELD_TAP_ACTION,
+ FIELD_ICON_BURN_IN_PROTECTION,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ TYPE_SMALL_IMAGE to setOf(
+ FIELD_TAP_ACTION,
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ TYPE_LARGE_IMAGE to setOf(
+ FIELD_TAP_ACTION,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ TYPE_NO_PERMISSION to setOf(
+ FIELD_SHORT_TEXT,
+ FIELD_SHORT_TITLE,
+ FIELD_ICON,
+ FIELD_ICON_BURN_IN_PROTECTION,
+ FIELD_SMALL_IMAGE,
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+ FIELD_IMAGE_STYLE,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ TYPE_NO_DATA to setOf(
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_ICON,
+ FIELD_ICON_BURN_IN_PROTECTION,
+ FIELD_IMAGE_STYLE,
+ FIELD_LARGE_IMAGE,
+ FIELD_LONG_TEXT,
+ FIELD_LONG_TITLE,
+ FIELD_MAX_VALUE,
+ FIELD_MIN_VALUE,
+ FIELD_PLACEHOLDER_FIELDS,
+ FIELD_PLACEHOLDER_TYPE,
+ FIELD_SHORT_TEXT,
+ FIELD_SHORT_TITLE,
+ FIELD_SMALL_IMAGE,
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+ FIELD_TAP_ACTION,
+ FIELD_VALUE,
+ FIELD_VALUE_TYPE,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ EXP_TYPE_PROTO_LAYOUT to setOf(
+ FIELD_TAP_ACTION,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ EXP_TYPE_LIST to setOf(
+ FIELD_TAP_ACTION,
+ EXP_FIELD_LIST_STYLE_HINT,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ TYPE_GOAL_PROGRESS to setOf(
+ FIELD_SHORT_TEXT,
+ FIELD_SHORT_TITLE,
+ FIELD_ICON,
+ FIELD_ICON_BURN_IN_PROTECTION,
+ FIELD_SMALL_IMAGE,
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+ FIELD_IMAGE_STYLE,
+ FIELD_TAP_ACTION,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_COLOR_RAMP,
+ FIELD_COLOR_RAMP_INTERPOLATED,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ TYPE_WEIGHTED_ELEMENTS to setOf(
+ FIELD_SHORT_TEXT,
+ FIELD_SHORT_TITLE,
+ FIELD_ICON,
+ FIELD_ICON_BURN_IN_PROTECTION,
+ FIELD_SMALL_IMAGE,
+ FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
+ FIELD_IMAGE_STYLE,
+ FIELD_TAP_ACTION,
+ FIELD_CONTENT_DESCRIPTION,
+ FIELD_DATA_SOURCE,
+ FIELD_PERSISTENCE_POLICY,
+ FIELD_DISPLAY_POLICY
+ ),
+ )
+
+ @JvmField
+ val CREATOR = object : Parcelable.Creator<ComplicationData> {
+ override fun createFromParcel(source: Parcel) = ComplicationData(source)
+
+ override fun newArray(size: Int): Array<ComplicationData?> = Array(size) { null }
+ }
+
+ fun isFieldValidForType(field: String, @ComplicationType type: Int): Boolean {
+ val requiredFields = REQUIRED_FIELDS[type] ?: return false
+ for (requiredField in requiredFields) {
+ if (requiredField == field) {
+ return true
+ }
+ }
+ for (optionalField in OPTIONAL_FIELDS[type]!!) {
+ if (optionalField == field) {
+ return true
+ }
+ }
+ return false
+ }
+
+ private fun isTypeSupported(type: Int) = type in VALID_TYPES
+
+ /** The unparceling logic needs to remain backward compatible. */
+ internal fun checkFieldValidForTypeWithoutThrowingException(
+ field: String,
+ @ComplicationType type: Int,
+ ) {
+ if (!isTypeSupported(type)) {
+ Log.w(TAG, "Type $type can not be recognized")
+ return
+ }
+ if (!isFieldValidForType(field, type)) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Field $field is not supported for type $type")
+ }
+ }
+ }
+
+ internal fun checkFieldValidForType(field: String, @ComplicationType type: Int) {
+ check(isTypeSupported(type)) { "Type $type can not be recognized" }
+ check(isFieldValidForType(field, type)) {
+ "Field $field is not supported for type $type"
+ }
+ }
+
+ /** Returns whether or not we should redact complication data in toString(). */
+ @JvmStatic
+ fun shouldRedact() = !Log.isLoggable(TAG, Log.DEBUG)
+
+ @JvmStatic
+ fun maybeRedact(unredacted: CharSequence?): String =
+ if (unredacted == null) "(null)"
+ else maybeRedact(unredacted.toString())
+
+ @JvmSynthetic
+ private fun maybeRedact(unredacted: String): String =
+ if (!shouldRedact() || unredacted == PLACEHOLDER_STRING) unredacted
+ else "REDACTED"
+ }
+}
\ No newline at end of file
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Image.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Image.kt
index c5463bf..5558fca 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Image.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Image.kt
@@ -194,7 +194,7 @@
internal fun addToWireComplicationData(builder: WireComplicationDataBuilder) = builder.apply {
setSmallImage(image)
setSmallImageStyle(
- when (type) {
+ when (this@SmallImage.type) {
SmallImageType.ICON -> WireComplicationData.IMAGE_STYLE_ICON
SmallImageType.PHOTO -> WireComplicationData.IMAGE_STYLE_PHOTO
}
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/PlaceholderTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/PlaceholderTest.kt
index c70e7a6..513af6b 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/PlaceholderTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/PlaceholderTest.kt
@@ -21,7 +21,7 @@
import android.graphics.Color
import android.graphics.drawable.Icon
import android.support.wearable.complications.ComplicationData
-import android.support.wearable.complications.ComplicationData.IMAGE_STYLE_ICON
+import android.support.wearable.complications.ComplicationData.Companion.IMAGE_STYLE_ICON
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import java.time.Instant