Merge "Use fully qualified class names in class verification failure autofix" into androidx-main
diff --git a/asynclayoutinflater/asynclayoutinflater/api/current.txt b/asynclayoutinflater/asynclayoutinflater/api/current.txt
index 32c043c..46e1c0e 100644
--- a/asynclayoutinflater/asynclayoutinflater/api/current.txt
+++ b/asynclayoutinflater/asynclayoutinflater/api/current.txt
@@ -1,16 +1,9 @@
// Signature format: 4.0
package androidx.asynclayoutinflater.view {
- public class AppCompatFactory2 implements android.view.LayoutInflater.Factory2 {
- ctor public AppCompatFactory2();
- method public android.view.View? onCreateView(android.view.View?, String, android.content.Context, android.util.AttributeSet);
- method public android.view.View? onCreateView(String, android.content.Context, android.util.AttributeSet);
- }
-
public final class AsyncLayoutInflater {
ctor public AsyncLayoutInflater(android.content.Context);
- method @Deprecated @UiThread public void inflate(@LayoutRes int, android.view.ViewGroup?, androidx.asynclayoutinflater.view.AsyncLayoutInflater.OnInflateFinishedListener);
- method @UiThread public void inflateWithOriginalFactory(@LayoutRes int, android.view.ViewGroup?, java.util.concurrent.Executor?, androidx.asynclayoutinflater.view.AsyncLayoutInflater.OnInflateFinishedListener);
+ method @UiThread public void inflate(@LayoutRes int, android.view.ViewGroup?, androidx.asynclayoutinflater.view.AsyncLayoutInflater.OnInflateFinishedListener);
}
public static interface AsyncLayoutInflater.OnInflateFinishedListener {
diff --git a/asynclayoutinflater/asynclayoutinflater/api/public_plus_experimental_current.txt b/asynclayoutinflater/asynclayoutinflater/api/public_plus_experimental_current.txt
index 32c043c..46e1c0e 100644
--- a/asynclayoutinflater/asynclayoutinflater/api/public_plus_experimental_current.txt
+++ b/asynclayoutinflater/asynclayoutinflater/api/public_plus_experimental_current.txt
@@ -1,16 +1,9 @@
// Signature format: 4.0
package androidx.asynclayoutinflater.view {
- public class AppCompatFactory2 implements android.view.LayoutInflater.Factory2 {
- ctor public AppCompatFactory2();
- method public android.view.View? onCreateView(android.view.View?, String, android.content.Context, android.util.AttributeSet);
- method public android.view.View? onCreateView(String, android.content.Context, android.util.AttributeSet);
- }
-
public final class AsyncLayoutInflater {
ctor public AsyncLayoutInflater(android.content.Context);
- method @Deprecated @UiThread public void inflate(@LayoutRes int, android.view.ViewGroup?, androidx.asynclayoutinflater.view.AsyncLayoutInflater.OnInflateFinishedListener);
- method @UiThread public void inflateWithOriginalFactory(@LayoutRes int, android.view.ViewGroup?, java.util.concurrent.Executor?, androidx.asynclayoutinflater.view.AsyncLayoutInflater.OnInflateFinishedListener);
+ method @UiThread public void inflate(@LayoutRes int, android.view.ViewGroup?, androidx.asynclayoutinflater.view.AsyncLayoutInflater.OnInflateFinishedListener);
}
public static interface AsyncLayoutInflater.OnInflateFinishedListener {
diff --git a/asynclayoutinflater/asynclayoutinflater/api/restricted_current.txt b/asynclayoutinflater/asynclayoutinflater/api/restricted_current.txt
index 32c043c..46e1c0e 100644
--- a/asynclayoutinflater/asynclayoutinflater/api/restricted_current.txt
+++ b/asynclayoutinflater/asynclayoutinflater/api/restricted_current.txt
@@ -1,16 +1,9 @@
// Signature format: 4.0
package androidx.asynclayoutinflater.view {
- public class AppCompatFactory2 implements android.view.LayoutInflater.Factory2 {
- ctor public AppCompatFactory2();
- method public android.view.View? onCreateView(android.view.View?, String, android.content.Context, android.util.AttributeSet);
- method public android.view.View? onCreateView(String, android.content.Context, android.util.AttributeSet);
- }
-
public final class AsyncLayoutInflater {
ctor public AsyncLayoutInflater(android.content.Context);
- method @Deprecated @UiThread public void inflate(@LayoutRes int, android.view.ViewGroup?, androidx.asynclayoutinflater.view.AsyncLayoutInflater.OnInflateFinishedListener);
- method @UiThread public void inflateWithOriginalFactory(@LayoutRes int, android.view.ViewGroup?, java.util.concurrent.Executor?, androidx.asynclayoutinflater.view.AsyncLayoutInflater.OnInflateFinishedListener);
+ method @UiThread public void inflate(@LayoutRes int, android.view.ViewGroup?, androidx.asynclayoutinflater.view.AsyncLayoutInflater.OnInflateFinishedListener);
}
public static interface AsyncLayoutInflater.OnInflateFinishedListener {
diff --git a/asynclayoutinflater/asynclayoutinflater/build.gradle b/asynclayoutinflater/asynclayoutinflater/build.gradle
index a559503..f11e241 100644
--- a/asynclayoutinflater/asynclayoutinflater/build.gradle
+++ b/asynclayoutinflater/asynclayoutinflater/build.gradle
@@ -8,11 +8,6 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
api("androidx.core:core:1.1.0")
- constraints {
- implementation(project(":appcompat:appcompat")) // Optional dependency via compile-only
- }
- compileOnly(project(":appcompat:appcompat"))
-
androidTestImplementation(project(":appcompat:appcompat"))
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRunner)
@@ -31,8 +26,5 @@
}
android {
- buildTypes.all {
- consumerProguardFiles("proguard-rules.pro")
- }
namespace "androidx.asynclayoutinflater"
}
diff --git a/asynclayoutinflater/asynclayoutinflater/proguard-rules.pro b/asynclayoutinflater/asynclayoutinflater/proguard-rules.pro
deleted file mode 100644
index 9928140..0000000
--- a/asynclayoutinflater/asynclayoutinflater/proguard-rules.pro
+++ /dev/null
@@ -1,11 +0,0 @@
-# Ignore missing classes from optional AppCompat dependency.
--dontwarn androidx.appcompat.app.AppCompatActivity
--dontwarn androidx.appcompat.app.AppCompatCallback
--dontwarn androidx.appcompat.app.AppCompatDelegate
--dontwarn androidx.appcompat.app.AppCompatViewInflater
--dontwarn androidx.appcompat.widget.VectorEnabledTintResources
--dontwarn androidx.appcompat.R
--dontwarn android.support.v7.app.AppCompatActivity
--dontwarn android.support.v7.app.AppCompatViewInflater
--dontwarn android.support.v7.appcompat.R$styleable
--dontwarn android.support.v7.widget.VectorEnabledTintResources
\ No newline at end of file
diff --git a/asynclayoutinflater/asynclayoutinflater/src/androidTest/java/androidx/asynclayoutinflater/view/AsyncLayoutInflaterTest.java b/asynclayoutinflater/asynclayoutinflater/src/androidTest/java/androidx/asynclayoutinflater/view/AsyncLayoutInflaterTest.java
index 9adfa28..7f2a54d 100644
--- a/asynclayoutinflater/asynclayoutinflater/src/androidTest/java/androidx/asynclayoutinflater/view/AsyncLayoutInflaterTest.java
+++ b/asynclayoutinflater/asynclayoutinflater/src/androidTest/java/androidx/asynclayoutinflater/view/AsyncLayoutInflaterTest.java
@@ -15,8 +15,6 @@
*/
package androidx.asynclayoutinflater.view;
-import android.os.Build;
-import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
@@ -24,7 +22,6 @@
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
-import androidx.test.filters.SdkSuppress;
import com.google.common.util.concurrent.SettableFuture;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -37,7 +34,6 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicReference;
@MediumTest
@RunWith(AndroidJUnit4.class)
@@ -75,72 +71,4 @@
false);
Assert.assertNotSame(asyncInflatedView.getClass(), inflatedView.getClass());
}
-
- @Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
- public void correctAsyncInflaterView_forButton() throws Exception {
- SettableFuture<View> asyncInflatedViewFuture = SettableFuture.create();
- mAsyncLayoutInflater.inflateWithOriginalFactory(R.layout.test_button, null,
- /* callbackExecutor= */ null,
- (view, resId, parent) -> {
- asyncInflatedViewFuture.set(view);
- });
-
- View asyncInflatedView = asyncInflatedViewFuture.get();
- View inflatedView = LayoutInflater.from(mActivity).inflate(R.layout.test_button, null,
- false);
-
- Assert.assertSame(asyncInflatedView.getClass(), inflatedView.getClass());
- }
-
- @Test
- public void inflateCallback_onMainThread() throws Exception {
- AtomicReference<Thread> callbackThread = new AtomicReference<>(null);
- SettableFuture<View> asyncInflatedViewFuture = SettableFuture.create();
- mAsyncLayoutInflater.inflateWithOriginalFactory(R.layout.test_button, null,
- /* callbackExecutor= */ null,
- (view, resId, parent) -> {
- callbackThread.set(Thread.currentThread());
- asyncInflatedViewFuture.set(view);
- });
-
- asyncInflatedViewFuture.get();
-
- Assert.assertSame(callbackThread.get(), Looper.getMainLooper().getThread());
- }
-
- @Test
- public void inflateCallback_onBackgroundThread() throws Exception {
- AtomicReference<Thread> callbackThread = new AtomicReference<>(null);
- SettableFuture<View> asyncInflatedViewFuture = SettableFuture.create();
- mAsyncLayoutInflater.inflateWithOriginalFactory(R.layout.test_button, null,
- mBackgroundExecutor,
- (view, resId, parent) -> {
- callbackThread.set(Thread.currentThread());
- asyncInflatedViewFuture.set(view);
- });
-
- asyncInflatedViewFuture.get();
-
- // Not called on main thread.
- Assert.assertNotSame(callbackThread.get(), Looper.getMainLooper().getThread());
- Assert.assertEquals(callbackThread.get().getName(), BG_THREAD_NAME);
- }
-
- @Test
- public void inflateCallback_onBackgroundThread_onAsyncInflationFailure() throws Exception {
- AtomicReference<Thread> callbackThread = new AtomicReference<>(null);
- SettableFuture<View> asyncInflatedViewFuture = SettableFuture.create();
- mAsyncLayoutInflater.inflateWithOriginalFactory(R.layout.fail_async_text_view, null,
- mBackgroundExecutor,
- (view, resId, parent) -> {
- callbackThread.set(Thread.currentThread());
- asyncInflatedViewFuture.set(view);
- });
-
- asyncInflatedViewFuture.get();
-
- Assert.assertNotSame(callbackThread.get(), Looper.getMainLooper().getThread());
- Assert.assertEquals(callbackThread.get().getName(), BG_THREAD_NAME);
- }
}
diff --git a/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AppCompatFactory2.java b/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AppCompatFactory2.java
deleted file mode 100644
index 03c6586..0000000
--- a/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AppCompatFactory2.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.asynclayoutinflater.view;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.R;
-import androidx.appcompat.app.AppCompatViewInflater;
-import androidx.appcompat.widget.VectorEnabledTintResources;
-/**
- * Factory for inflating views in AppCompat activity. This is used when Async inflater is created
- * with AppCompat context.
- */
-public class AppCompatFactory2 implements LayoutInflater.Factory2 {
- private static final String TAG = "AppCompatFactory2";
- private AppCompatViewInflater mAppCompatViewInflater;
- /**
- * Creates view using {@link AppCompatViewInflater}.
- */
- @Nullable
- @Override
- public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context,
- @NonNull AttributeSet attrs) {
- return createView(parent, name, context, attrs);
- }
- /**
- * Creates view using {@link AppCompatViewInflater}.
- */
- @Nullable
- @Override
- public View onCreateView(@NonNull String name, @NonNull Context context,
- @NonNull AttributeSet attrs) {
- return createView(/* parent= */ null, name, context, attrs);
- }
- View createView(View parent, final String name, @NonNull Context context,
- @NonNull AttributeSet attrs) {
- if (mAppCompatViewInflater == null) {
- TypedArray a = context.obtainStyledAttributes(R.styleable.AppCompatTheme);
- String viewInflaterClassName = a.getString(
- R.styleable.AppCompatTheme_viewInflaterClass);
- if (viewInflaterClassName == null) {
- // Set to null (the default in all AppCompat themes). Create the base inflater
- // (no reflection)
- mAppCompatViewInflater = new AppCompatViewInflater();
- } else {
- try {
- Class<?> viewInflaterClass = context.getClassLoader().loadClass(
- viewInflaterClassName);
- mAppCompatViewInflater =
- (AppCompatViewInflater) viewInflaterClass
- .getDeclaredConstructor()
- .newInstance();
- } catch (Throwable t) {
- Log.i(TAG, "Failed to instantiate custom view inflater " + viewInflaterClassName
- + ". Falling back to default.", t);
- mAppCompatViewInflater = new AppCompatViewInflater();
- }
- }
- }
- return mAppCompatViewInflater.createView(parent, name, context, attrs,
- /* inheritContext= */ false,
- /* isPreLollipop= */ false,
- true, /* Read read app:theme as a fallback at all times for legacy reasons */
- VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if
- enabled */);
- }
-}
diff --git a/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AsyncLayoutInflater.java b/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AsyncLayoutInflater.java
index 5d2d9be..3d3d8b2 100644
--- a/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AsyncLayoutInflater.java
+++ b/asynclayoutinflater/asynclayoutinflater/src/main/java/androidx/asynclayoutinflater/view/AsyncLayoutInflater.java
@@ -17,7 +17,6 @@
package androidx.asynclayoutinflater.view;
import android.content.Context;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -31,7 +30,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import androidx.appcompat.app.AppCompatActivity;
import androidx.core.util.Pools.SynchronizedPool;
import java.util.concurrent.ArrayBlockingQueue;
@@ -69,54 +67,23 @@
*/
public final class AsyncLayoutInflater {
private static final String TAG = "AsyncLayoutInflater";
- private static final boolean IS_POST_LOLLIPOP = Build.VERSION.SDK_INT >= 21;
- private static boolean sAppCompatPresent;
- static {
- try {
- Class.forName("androidx.appcompat.app.AppCompatActivity");
- sAppCompatPresent = true;
- } catch (ClassNotFoundException ex) {
- sAppCompatPresent = false;
- }
- }
-
LayoutInflater mInflater;
- LayoutInflater mInflaterDeprecated;
Handler mHandler;
InflateThread mInflateThread;
public AsyncLayoutInflater(@NonNull Context context) {
- mInflaterDeprecated = new BasicInflater(context);
mInflater = new BasicInflater(context);
mHandler = new Handler(Looper.myLooper(), mHandlerCallback);
mInflateThread = InflateThread.getInstance();
- if (sAppCompatPresent && context instanceof AppCompatActivity && IS_POST_LOLLIPOP) {
- mInflater.setFactory2(new AppCompatFactory2());
- }
}
/**
* Triggers view inflation on background thread.
- *
- * @deprecated may initialize incorrect class for AppCompat library. Use
- * {@link #inflateWithOriginalFactory} instead.
*/
@UiThread
- @Deprecated
public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent,
@NonNull OnInflateFinishedListener callback) {
- inflateInternal(resid, parent, callback, mInflaterDeprecated, /* callbackExecutor= */ null);
- }
-
- /**
- * Triggers inflation on a background thread. It uses an underlying
- * inflater with same {@link LayoutInflater.Factory2} as the inflater for
- * {@link AppCompatActivity}.
- */
- @UiThread
- public void inflateWithOriginalFactory(@LayoutRes int resid, @Nullable ViewGroup parent,
- @Nullable Executor callbackExecutor, @NonNull OnInflateFinishedListener callback) {
- inflateInternal(resid, parent, callback, mInflater, callbackExecutor);
+ inflateInternal(resid, parent, callback, mInflater, /* callbackExecutor= */ null);
}
private void inflateInternal(@LayoutRes int resid, @Nullable ViewGroup parent,
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
index d107dd0b..ec67850 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkScopeTest.kt
@@ -78,7 +78,10 @@
baselineProfileMode = BaselineProfileMode.Disable,
warmupIterations = iterations
)
- compilation.resetAndCompile(Packages.TARGET) {
+ compilation.resetAndCompile(
+ Packages.TARGET,
+ killProcessBlock = scope::killProcess
+ ) {
executions += 1
scope.pressHome()
scope.startActivityAndWait()
@@ -88,8 +91,12 @@
@Test
fun compile_full() {
+ val scope = MacrobenchmarkScope(Packages.TARGET, launchWithClearTask = true)
val compilation = CompilationMode.Full()
- compilation.resetAndCompile(Packages.TARGET) {
+ compilation.resetAndCompile(
+ Packages.TARGET,
+ killProcessBlock = scope::killProcess
+ ) {
fail("Should never be called for $compilation")
}
}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
index 697fc15..82d069e 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
@@ -63,7 +63,13 @@
try {
userspaceTrace("compile $packageName") {
compilationMode.resetAndCompile(
- packageName = packageName
+ packageName = packageName,
+ killProcessBlock = {
+ // When generating baseline profiles we want to default to using
+ // killProcess if the session is rooted. This is so we can collect
+ // baseline profiles for System Apps.
+ scope.killProcess(useKillAll = Shell.isSessionRooted())
+ }
) {
profileBlock(scope)
}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
index 9a51aea..69c07cd 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
@@ -72,6 +72,7 @@
internal fun resetAndCompile(
packageName: String,
usePackageReset: () -> Boolean = Shell::isSessionRooted,
+ killProcessBlock: () -> Unit,
warmupBlock: () -> Unit
) {
if (Build.VERSION.SDK_INT >= 24) {
@@ -105,8 +106,8 @@
}
}
// Write skip file to stop profile installer from interfering with the benchmark
- writeProfileInstallerSkipFile(packageName)
- compileImpl(packageName, warmupBlock)
+ writeProfileInstallerSkipFile(packageName, killProcessBlock = killProcessBlock)
+ compileImpl(packageName, killProcessBlock, warmupBlock)
} else {
Log.d(TAG, "Compilation is disabled, skipping compilation of $packageName")
}
@@ -157,7 +158,7 @@
* Writes a skip file via a [ProfileInstallReceiver] broadcast, so profile installation
* does not interfere with benchmarks.
*/
- private fun writeProfileInstallerSkipFile(packageName: String) {
+ private fun writeProfileInstallerSkipFile(packageName: String, killProcessBlock: () -> Unit) {
val result = profileInstallerSkipFileOperation(packageName, "WRITE_SKIP_FILE")
if (result != null) {
Log.w(
@@ -169,7 +170,7 @@
)
}
Log.d(TAG, "Killing process $packageName")
- Shell.executeCommand("am force-stop $packageName")
+ killProcessBlock()
}
/**
@@ -228,7 +229,11 @@
}
@RequiresApi(24)
- internal abstract fun compileImpl(packageName: String, warmupBlock: () -> Unit)
+ internal abstract fun compileImpl(
+ packageName: String,
+ killProcessBlock: () -> Unit,
+ warmupBlock: () -> Unit
+ )
@RequiresApi(24)
internal abstract fun shouldReset(): Boolean
@@ -245,7 +250,11 @@
class None : CompilationMode() {
override fun toString(): String = "None"
- override fun compileImpl(packageName: String, warmupBlock: () -> Unit) {
+ override fun compileImpl(
+ packageName: String,
+ killProcessBlock: () -> Unit,
+ warmupBlock: () -> Unit
+ ) {
// nothing to do!
}
@@ -262,7 +271,11 @@
class Ignore : CompilationMode() {
override fun toString(): String = "Ignore"
- override fun compileImpl(packageName: String, warmupBlock: () -> Unit) {
+ override fun compileImpl(
+ packageName: String,
+ killProcessBlock: () -> Unit,
+ warmupBlock: () -> Unit
+ ) {
// Do nothing.
}
@@ -381,14 +394,18 @@
}
}
- override fun compileImpl(packageName: String, warmupBlock: () -> Unit) {
+ override fun compileImpl(
+ packageName: String,
+ killProcessBlock: () -> Unit,
+ warmupBlock: () -> Unit
+ ) {
if (baselineProfileMode != BaselineProfileMode.Disable) {
// Ignores the presence of a skip file.
val installErrorString = broadcastBaselineProfileInstall(packageName)
if (installErrorString == null) {
// baseline profile install success, kill process before compiling
Log.d(TAG, "Killing process $packageName")
- Shell.executeCommand("am force-stop $packageName")
+ killProcessBlock()
cmdPackageCompile(packageName, "speed-profile")
} else {
if (baselineProfileMode == BaselineProfileMode.Require) {
@@ -430,7 +447,11 @@
class Full : CompilationMode() {
override fun toString(): String = "Full"
- override fun compileImpl(packageName: String, warmupBlock: () -> Unit) {
+ override fun compileImpl(
+ packageName: String,
+ killProcessBlock: () -> Unit,
+ warmupBlock: () -> Unit
+ ) {
if (Build.VERSION.SDK_INT >= 24) {
cmdPackageCompile(packageName, "speed")
}
@@ -454,7 +475,11 @@
object Interpreted : CompilationMode() {
override fun toString(): String = "Interpreted"
- override fun compileImpl(packageName: String, warmupBlock: () -> Unit) {
+ override fun compileImpl(
+ packageName: String,
+ killProcessBlock: () -> Unit,
+ warmupBlock: () -> Unit
+ ) {
// Nothing to do - handled externally
}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index 15fd52b..fbb5d40 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -136,7 +136,7 @@
scope.killProcess()
userspaceTrace("compile $packageName") {
- compilationMode.resetAndCompile(packageName) {
+ compilationMode.resetAndCompile(packageName, killProcessBlock = scope::killProcess) {
setupBlock(scope)
measureBlock(scope)
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dackka/GenerateMetadataTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/dackka/GenerateMetadataTask.kt
index be98b33..99964bf 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dackka/GenerateMetadataTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dackka/GenerateMetadataTask.kt
@@ -16,7 +16,8 @@
package androidx.build.dackka
-import java.io.File
+import com.google.gson.Gson
+import java.io.FileWriter
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.component.ComponentArtifactIdentifier
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
@@ -27,7 +28,6 @@
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
-import org.json.JSONArray
@CacheableTask
abstract class GenerateMetadataTask : DefaultTask() {
@@ -67,10 +67,9 @@
}
val jsonMapping = generateJsonMapping(entries)
- val json = JSONArray(jsonMapping)
-
- val outputFile = File(destinationFile.get().toString())
- outputFile.writeText(json.toString(2))
+ val writer = FileWriter(destinationFile.get().toString())
+ Gson().toJson(jsonMapping, writer)
+ writer.close()
}
/**
diff --git a/buildSrc/shared.gradle b/buildSrc/shared.gradle
index a253d3d..7e4b0de 100644
--- a/buildSrc/shared.gradle
+++ b/buildSrc/shared.gradle
@@ -1,5 +1,3 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
apply plugin: "kotlin"
apply from: "../kotlin-dsl-dependency.gradle"
apply plugin: "java-gradle-plugin"
@@ -13,146 +11,49 @@
}
}
-configurations {
- // Dependencies added to these configurations get copied into the corresponding configuration
- // (cacheableApi gets copied into api, etc).
- // Because we cache the resolutions of these configurations, performance is faster when
- // artifacts are put into these configurations than when those artifacts are put into their
- // corresponding configuration.
- cacheableApi {
- canBeConsumed = false // not directly used to satisfy a project dependency
- attributes {
- attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.class, Usage.JAVA_RUNTIME));
- attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.class, Category.LIBRARY));
- attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.class, LibraryElements.JAR));
- attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, Integer.parseInt(JavaVersion.current().getMajorVersion()));
- attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, project.objects.named(GradlePluginApiVersion.class, GradleVersion.current().getVersion()));
- }
- }
- cacheableImplementation {
- canBeConsumed = false // not directly used to satisfy a project dependency
- attributes {
- attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.class, Usage.JAVA_RUNTIME));
- attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.class, Category.LIBRARY));
- attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.class, LibraryElements.JAR));
- attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, Integer.parseInt(JavaVersion.current().getMajorVersion()));
- attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, project.objects.named(GradlePluginApiVersion.class, GradleVersion.current().getVersion()));
- }
- extendsFrom(project.configurations.cacheableApi)
- }
- cacheableRuntimeOnly {
- canBeConsumed = false // not directly used to satisfy a project dependency
- }
-}
-
dependencies {
- cacheableApi(libs.toml)
- cacheableImplementation(libs.gson)
- cacheableImplementation(libs.dom4j) {
+ implementation(project(":jetpad-integration"))
+
+ // Gradle APIs
+ implementation(gradleApi())
+ compileOnly(findGradleKotlinDsl())
+
+ // Plugins we use and configure
+ implementation(libs.androidGradlePluginz)
+ implementation(libs.androidToolsCommon) // for com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
+ implementation(libs.kotlinGradlePluginz)
+ implementation(libs.dokkaGradlePluginz)
+
+ // variety of json parsers
+ implementation(libs.gson)
+ implementation(libs.json) // b/241475613
+ implementation(libs.jsonSimple)
+
+ // XML parsers used in MavenUploadHelper.kt
+ implementation(libs.dom4j) {
// Optional dependency where Ivy fails to parse the POM file.
exclude(group:"net.java.dev.msv", module:"xsdlib")
}
- cacheableApi(libs.androidGradlePluginz)
- // AGP has removes dependency on json-simple in http://b/239967984, so add explicitly
- cacheableImplementation(libs.jsonSimple)
- cacheableImplementation(libs.dexMemberList)
- cacheableApi(libs.kotlinGradlePluginz)
- cacheableImplementation(gradleApi())
- cacheableApi(libs.dokkaGradlePluginz)
- // needed by inspection plugin
- cacheableImplementation(libs.protobufGradlePluginz)
- cacheableImplementation(libs.wireGradlePluginz)
- cacheableImplementation(libs.shadow)
+ implementation(libs.xerces)
+
+ implementation(libs.shadow) // used by BundleInsideHelper.kt
+ implementation(libs.apacheAnt) // used in AarManifestTransformerTask.kt for unziping
+ implementation(libs.toml)
+ implementation(libs.apacheCommonIo) // used in CheckApiEquivalenceTask.kt
+ implementation(libs.dexMemberList) // used in ReportLibraryMetricsTask.kt
+
+ implementation(libs.protobufGradlePluginz) // needed to compile inspection plugin
+ implementation(libs.kotlinPoet) // needed to compile material-icon-generator
+ implementation(libs.xmlpull) // needed to compile material-icon-generator
+
// dependencies that aren't used by buildSrc directly but that we resolve here so that the
// root project doesn't need to re-resolve them and their dependencies on every build
- cacheableRuntimeOnly(libs.hiltAndroidGradlePluginz)
- // Needed for hiltAndroidGradlePluginz to workaround https://github.com/google/dagger/issues/3068
- cacheableApi(libs.javapoet)
- // room kotlintestapp uses the ksp plugin but it does not publish a plugin marker yet
- cacheableApi(libs.kspGradlePluginz)
- // dependencies whose resolutions we don't need to cache
- compileOnly(findGradleKotlinDsl()) // Only one file in this configuration, no need to cache it
- implementation(project(":jetpad-integration")) // Doesn't have a .pom, so not slow to load
+ runtimeOnly(libs.hiltAndroidGradlePluginz)
+ runtimeOnly(libs.javapoet) // for hiltAndroidGradlePluginz to workaround https://github.com/google/dagger/issues/3068
+ runtimeOnly(libs.kspGradlePluginz)
+ runtimeOnly(libs.wireGradlePluginz)
}
-// Saves configuration into destFile
-// Each line of destFile will be the absolute filepath of one of the files in configuration
-def saveConfigurationResolution(configuration, destFile) {
- def resolvedConfiguration = configuration.resolvedConfiguration
- def files = resolvedConfiguration.files
- def paths = files.collect { f -> f.toString() }
- def serialized = paths.join("\n")
- destFile.text = serialized
-}
-
-// Parses a file into a list of Dependency objects representing a ResolvedConfiguration
-def parseConfigurationResolution(savedFile, throwOnError) {
- def savedText = savedFile.text
- def filenames = savedText.split("\n")
- def valid = true
- def dependencies = filenames.collect { filename ->
- if (!project.file(filename).exists()) {
- if (throwOnError) {
- throw new GradleException("\nFile " + filename + " listed as a resolved dependency in " + savedFile + " does not exist!\n\nFor more information, see b/187075069")
- } else {
- valid = false
- }
- }
- project.dependencies.create(project.files(filename))
- }
- if (!valid) {
- return null
- }
- return dependencies
-}
-
-// Resolves a Configuration into a list of Dependency objects
-def resolveConfiguration(configuration) {
- def resolvedName = configuration.name
- def cacheDir = new File(project.rootProject.buildDir, "/" + resolvedName)
- def inputsFile = new File(cacheDir, "/deps")
- def outputsFile = new File(cacheDir, "/result")
-
- def inputText = fingerprintConfiguration(configuration)
- def parsed = null
- if (inputsFile.exists() && inputsFile.text == inputText) {
- // Try to parse the previously resolved configuration, but don't give up if it mentions a
- // nonexistent file. If something has since deleted one of the referenced files, we will
- // try to reresolve that file later
- parsed = parseConfigurationResolution(outputsFile, false)
- }
- // If the configuration has changed or if any of its files have been deleted, reresolve it
- if (parsed == null) {
- cacheDir.mkdirs()
- saveConfigurationResolution(configuration, outputsFile)
- inputsFile.text = inputText
- // confirm that the resolved configuration parses successfully
- parsed = parseConfigurationResolution(outputsFile, true)
- }
- return parsed
-}
-
-// Computes a unique string from a Configuration based on its dependencies
-// This is used for up-to-date checks
-def fingerprintConfiguration(configuration) {
- def dependencies = configuration.allDependencies
- def dependencyTexts = dependencies.collect { dep -> dep.group + ":" + dep.name + ":" + dep.version }
- return dependencyTexts.join("\n")
-}
-
-// Imports the contents of fromConf into toConf
-// Uses caching to often short-circuit the resolution of fromConf
-def loadConfigurationQuicklyInto(fromConf, toConf) {
- def resolved = resolveConfiguration(fromConf)
- resolved.each { dep ->
- project.dependencies.add(toConf.name, dep)
- }
-}
-
-loadConfigurationQuicklyInto(configurations.cacheableApi, configurations.api)
-loadConfigurationQuicklyInto(configurations.cacheableImplementation, configurations.implementation)
-loadConfigurationQuicklyInto(configurations.cacheableRuntimeOnly, configurations.runtimeOnly)
-
project.tasks.withType(Jar) { task ->
task.reproducibleFileOrder = true
task.preserveFileTimestamps = false
diff --git a/camera/camera-core/src/main/cpp/opengl_renderer_jni.cpp b/camera/camera-core/src/main/cpp/opengl_renderer_jni.cpp
index 4e73b47..f12082d 100644
--- a/camera/camera-core/src/main/cpp/opengl_renderer_jni.cpp
+++ b/camera/camera-core/src/main/cpp/opengl_renderer_jni.cpp
@@ -409,6 +409,15 @@
fragmentShaderSrc = env->GetStringUTFChars(jcustomFragmentShader, nullptr);
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Custom fragment shader = %s",
fragmentShaderSrc);
+ // A simple check to workaround custom shader doesn't contain required variable.
+ // See b/241193761.
+ if (!strstr(fragmentShaderSrc, VAR_NAMES[0].c_str())) {
+ ThrowException(env, "java/lang/IllegalArgumentException", std::string(
+ "Missing required variable '" + VAR_NAMES[0] +
+ "' in the custom fragment shader").c_str());
+ env->ReleaseStringUTFChars(jcustomFragmentShader, fragmentShaderSrc);
+ return 0;
+ }
} else {
fragmentShaderSrc = FRAGMENT_SHADER_SRC.c_str();
}
diff --git a/camera/integration-tests/uiwidgetstestapp/build.gradle b/camera/integration-tests/uiwidgetstestapp/build.gradle
index 2631c7c..4c8c5d1 100644
--- a/camera/integration-tests/uiwidgetstestapp/build.gradle
+++ b/camera/integration-tests/uiwidgetstestapp/build.gradle
@@ -111,4 +111,10 @@
debugImplementation("androidx.fragment:fragment-testing:1.2.5")
// Testing resource dependency for manifest
debugImplementation(project(":camera:camera-testing"))
+
+ // Testing for Compose
+ // Test rules and transitive dependencies:
+ androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.1.1")
+ // Needed for createComposeRule, but not createAndroidComposeRule:
+ debugImplementation("androidx.compose.ui:ui-test-manifest:1.1.1")
}
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraAppTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraAppTest.kt
new file mode 100644
index 0000000..1c5b3c1
--- /dev/null
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraAppTest.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.uiwidgets.compose
+
+import android.os.Build
+import androidx.camera.integration.uiwidgets.compose.ui.navigation.ComposeCameraScreen
+import androidx.camera.view.PreviewView
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assertIsSelected
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ActivityScenario
+import androidx.test.filters.LargeTest
+import androidx.test.rule.GrantPermissionRule
+import androidx.testutils.RepeatRule
+import com.google.common.truth.Truth
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.runBlocking
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+@LargeTest
+class ComposeCameraAppTest {
+ // Provide permissions to app via ComposeCameraActivity
+ @get:Rule
+ val permissionRule: GrantPermissionRule =
+ GrantPermissionRule.grant(
+ *ComposeCameraActivity.REQUIRED_PERMISSIONS
+ )
+
+ @get: Rule
+ val androidComposeTestRule = createAndroidComposeRule<ComposeCameraActivity>()
+
+ @get: Rule
+ val repeatRule = RepeatRule()
+
+ @Before
+ fun setup() {
+ // Skip test for b/168175357
+ Assume.assumeFalse(
+ "Cuttlefish has MediaCodec dequeInput/Output buffer fails issue. Unable to test.",
+ Build.MODEL.contains("Cuttlefish") && Build.VERSION.SDK_INT == 29
+ )
+
+ // Recreate the activity as it might terminate in other tests
+ androidComposeTestRule.activityRule.scenario.recreate()
+ }
+
+ // Activity launch will render ImageCaptureScreen
+ // Ensure that ImageCapture screen's PreviewView is streaming properly
+ @Test
+ @RepeatRule.Repeat(times = 10)
+ fun testPreviewViewStreamStateOnActivityLaunch() {
+ assertStreamState(
+ ComposeCameraScreen.ImageCapture,
+ PreviewView.StreamState.STREAMING,
+ androidComposeTestRule.activityRule.scenario
+ )
+ }
+
+ // Navigating from ImageCapture to VideoCapture screen
+ // Ensure that VideoCapture screen's PreviewView is streaming properly
+ @Test
+ @RepeatRule.Repeat(times = 10)
+ fun testPreviewViewStreamStateOnNavigation() {
+
+ // Get VideoCapture Navigation Tab (Node)
+ val node = androidComposeTestRule.onNode(
+ SemanticsMatcher.expectValue(
+ SemanticsProperties.Role, Role.Tab,
+ ).and(
+ SemanticsMatcher.expectValue(
+ SemanticsProperties.ContentDescription,
+ listOf("VideoCapture")
+ )
+ )
+ )
+
+ // Ensure that Tab is selected after we click on it
+ node.performClick().assertIsSelected()
+
+ // Assert VideoCapture's PreviewView is streaming
+ assertStreamState(
+ ComposeCameraScreen.VideoCapture,
+ PreviewView.StreamState.STREAMING,
+ androidComposeTestRule.activityRule.scenario
+ )
+ }
+
+ // Asserts that the StreamState in the ComposeCameraScreen reaches
+ // expectedState within a reasonable timeout
+ private fun assertStreamState(
+ expectedScreen: ComposeCameraScreen,
+ expectedState: PreviewView.StreamState,
+ scenario: ActivityScenario<ComposeCameraActivity>,
+ ) = runBlocking<Unit> {
+ lateinit var result: Deferred<Boolean>
+
+ scenario.onActivity { activity ->
+ // Make async Coroutine to wait the result, not block the test thread.
+ result = async {
+ activity.waitForStreamState(
+ expectedScreen = expectedScreen,
+ expectedState = expectedState
+ )
+ }
+ }
+
+ Truth.assertThat(result.await()).isTrue()
+ }
+
+ companion object {
+ private const val TAG = "ComposeCameraAppTest"
+ }
+}
\ No newline at end of file
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraActivity.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraActivity.kt
index 3837653..3b500f3 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraActivity.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ComposeCameraActivity.kt
@@ -20,13 +20,24 @@
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
+import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.camera.integration.uiwidgets.compose.ui.ComposeCameraApp
import androidx.camera.integration.uiwidgets.compose.ui.PermissionsUI
+import androidx.camera.integration.uiwidgets.compose.ui.navigation.ComposeCameraScreen
+import androidx.camera.view.PreviewView.StreamState
import androidx.core.content.ContextCompat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
class ComposeCameraActivity : ComponentActivity() {
+
+ // Variables for testing StreamState changes in PreviewView
+ private var expectedScreen: ComposeCameraScreen = ComposeCameraScreen.ImageCapture
+ private var expectedStreamState: StreamState = StreamState.STREAMING
+ private var latchForState: CountDownLatch = CountDownLatch(0)
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
@@ -36,7 +47,7 @@
checkAllPermissionsGranted(it)
}
) {
- ComposeCameraApp()
+ ComposeCameraApp(onStreamStateChange = this::onStreamStateChange)
}
}
}
@@ -47,7 +58,49 @@
}
}
+ // Saves the expected ComposeCameraScreen and StreamState for testing PreviewView
+ // Once saved, this method waits to be notified of StreamState changes
+ // Used to assert that PreviewView is streaming within reasonable timeout
+ fun waitForStreamState(
+ expectedScreen: ComposeCameraScreen,
+ expectedState: StreamState
+ ): Boolean {
+ this.expectedScreen = expectedScreen
+ expectedStreamState = expectedState
+ latchForState = CountDownLatch(1)
+ return latchForState.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)
+ }
+
+ // Callback to observe changes in PreviewView.StreamState happening in some ComposeCameraScreen
+ // Used to observe changes in StreamState when Composables render (during testing)
+ private fun onStreamStateChange(screen: ComposeCameraScreen, streamState: StreamState) {
+ // StreamState change not coming from expected screen
+ if (screen != expectedScreen) {
+ return
+ }
+
+ when (streamState) {
+ StreamState.STREAMING -> {
+ Log.d(TAG, "PreviewView.StreamState.STREAMING from ${screen.name}")
+ if (expectedStreamState == StreamState.STREAMING) {
+ latchForState.countDown()
+ }
+ }
+ StreamState.IDLE -> {
+ Log.d(TAG, "PreviewView.StreamState.IDLE in ${screen.name}")
+ if (expectedStreamState == StreamState.IDLE) {
+ latchForState.countDown()
+ }
+ }
+ else -> {
+ Log.e(TAG, "Wrong PreviewView.StreamState in ${screen.name}! Return IDLE")
+ }
+ }
+ }
+
companion object {
+ private const val TAG = "ComposeCameraActivity"
+ private const val LATCH_TIMEOUT: Long = 5000
val REQUIRED_PERMISSIONS = mutableListOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/ComposeCameraApp.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/ComposeCameraApp.kt
index 026ee4d..326bf36 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/ComposeCameraApp.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/ComposeCameraApp.kt
@@ -19,6 +19,7 @@
import androidx.camera.integration.uiwidgets.compose.ui.navigation.ComposeCameraNavHost
import androidx.camera.integration.uiwidgets.compose.ui.navigation.ComposeCameraScreen
import androidx.camera.integration.uiwidgets.compose.ui.screen.components.ComposeCameraScreenTabRow
+import androidx.camera.view.PreviewView
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
@@ -27,8 +28,11 @@
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
+// Provides callback when StreamState changes in a ComposeCameraScreen
@Composable
-fun ComposeCameraApp() {
+fun ComposeCameraApp(
+ onStreamStateChange: (ComposeCameraScreen, PreviewView.StreamState) -> Unit = { _, _ -> }
+) {
MaterialTheme {
val allScreens = ComposeCameraScreen.values().toList()
val navController = rememberNavController()
@@ -51,7 +55,8 @@
) { innerPadding ->
ComposeCameraNavHost(
navController = navController,
- modifier = Modifier.padding(innerPadding)
+ modifier = Modifier.padding(innerPadding),
+ onStreamStateChange = onStreamStateChange
)
}
}
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraNavHost.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraNavHost.kt
index 0f0927f..d91fc70 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraNavHost.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/navigation/ComposeCameraNavHost.kt
@@ -18,16 +18,20 @@
import androidx.camera.integration.uiwidgets.compose.ui.screen.imagecapture.ImageCaptureScreen
import androidx.camera.integration.uiwidgets.compose.ui.screen.videocapture.VideoCaptureScreen
+import androidx.camera.view.PreviewView
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
+// Provides the ComposeCameraScreen needed for onStreamStateChange
+// The Screen-level Composable will provide the StreamState changes
@Composable
fun ComposeCameraNavHost(
navController: NavHostController,
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
+ onStreamStateChange: (ComposeCameraScreen, PreviewView.StreamState) -> Unit = { _, _ -> }
) {
NavHost(
navController = navController,
@@ -35,11 +39,25 @@
modifier = modifier
) {
composable(ComposeCameraScreen.ImageCapture.name) {
- ImageCaptureScreen()
+ ImageCaptureScreen(
+ onStreamStateChange = { state ->
+ onStreamStateChange(
+ ComposeCameraScreen.ImageCapture,
+ state
+ )
+ }
+ )
}
composable(ComposeCameraScreen.VideoCapture.name) {
- VideoCaptureScreen()
+ VideoCaptureScreen(
+ onStreamStateChange = { state ->
+ onStreamStateChange(
+ ComposeCameraScreen.VideoCapture,
+ state
+ )
+ }
+ )
}
}
}
\ No newline at end of file
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/imagecapture/ImageCaptureScreen.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/imagecapture/ImageCaptureScreen.kt
index 4cf4fe8..4b4ed18 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/imagecapture/ImageCaptureScreen.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/imagecapture/ImageCaptureScreen.kt
@@ -17,6 +17,7 @@
package androidx.camera.integration.uiwidgets.compose.ui.screen.imagecapture
import android.graphics.Rect
+import android.util.Log
import android.view.ViewGroup
import androidx.camera.core.MeteringPoint
import androidx.camera.core.Preview.SurfaceProvider
@@ -57,6 +58,9 @@
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.Observer
+
+private const val TAG = "ImageCaptureScreen"
// ImageCaptureScreen with QR-Code Overlay (Supports Preview + ImageCapture + ImageAnalysis)
// Screen provides ImageCapture functionality and draws a bounding box around a detected QR Code
@@ -66,7 +70,8 @@
@Composable
fun ImageCaptureScreen(
modifier: Modifier = Modifier,
- state: ImageCaptureScreenState = rememberImageCaptureScreenState()
+ state: ImageCaptureScreenState = rememberImageCaptureScreenState(),
+ onStreamStateChange: (PreviewView.StreamState) -> Unit = {}
) {
val lifecycleOwner = LocalLifecycleOwner.current
val localContext = LocalContext.current
@@ -75,13 +80,6 @@
state.startCamera(context = localContext, lifecycleOwner = lifecycleOwner)
}
- // Release resources when the Composable is removed from the Composition
- DisposableEffect(Unit) {
- onDispose {
- state.releaseResources()
- }
- }
-
ImageCaptureScreen(
modifier = modifier,
zoomRatio = state.zoomRatio,
@@ -97,7 +95,9 @@
},
onSurfaceProviderReady = state::setSurfaceProvider,
onOutputTransformReady = state::setOutputTransform,
- onTouch = state::startTapToFocus
+ onTouch = state::startTapToFocus,
+ onStreamStateChange = onStreamStateChange,
+ onDispose = state::releaseResources
) {
// Uses overlay to draw detected QRCode in the image stream
QRCodeOverlay(qrCodeBoundingBox = state.qrCodeBoundingBox)
@@ -121,10 +121,19 @@
onSurfaceProviderReady: (SurfaceProvider) -> Unit,
onOutputTransformReady: (OutputTransform) -> Unit,
onTouch: (MeteringPoint) -> Unit,
+ onStreamStateChange: (PreviewView.StreamState) -> Unit = {},
+ onDispose: () -> Unit = {},
content: @Composable () -> Unit = {} // overlay to display something above PreviewView
) {
+ val lifecycleOwner = LocalLifecycleOwner.current
val localContext = LocalContext.current
+ val streamStateObserver = remember {
+ Observer<PreviewView.StreamState> { state ->
+ onStreamStateChange(state)
+ }
+ }
+
// Saving an instance of PreviewView outside of AndroidView
// This allows us to access properties of PreviewView (e.g. ViewPort and OutputTransform)
// Allows us to support functionalities such as UseCaseGroup in bindToLifecycle()
@@ -151,6 +160,24 @@
}
}
+ // Attach StreamState Observer when the screen first renders
+ DisposableEffect(key1 = Unit) {
+ Log.d(TAG, "[DisposableEffect] Detaching StreamState Observers from PreviewView")
+ previewView.previewStreamState.removeObservers(lifecycleOwner)
+
+ Log.d(TAG, "[DisposableEffect] Attaching StreamState Observer to PreviewView")
+ previewView.previewStreamState.observe(lifecycleOwner, streamStateObserver)
+
+ // Detach observer when the screen is removed from the Composition
+ onDispose {
+ Log.d(TAG, "[onDispose] Detaching current StreamState Observer from PreviewView")
+ previewView.previewStreamState.removeObservers(lifecycleOwner)
+
+ // Clean up resources when PreviewView is removed from the composition
+ onDispose()
+ }
+ }
+
// Provides OutputTransform when PreviewView is attached on the screen
LaunchedEffect(key1 = previewView.outputTransform) {
if (previewView.outputTransform != null) {
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/imagecapture/ImageCaptureScreenState.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/imagecapture/ImageCaptureScreenState.kt
index 4f77685..cd0aa98 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/imagecapture/ImageCaptureScreenState.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/imagecapture/ImageCaptureScreenState.kt
@@ -295,7 +295,9 @@
}
// Method to release resources when the Screen is removed from the Composition
+ // We will release the ImageAnalyzer from ImageAnalysis use case and close the analyzer
fun releaseResources() {
+ imageAnalysis.clearAnalyzer()
barcodeScanner.close()
}
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/videocapture/VideoCaptureScreen.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/videocapture/VideoCaptureScreen.kt
index acc60e6..5bb0aae 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/videocapture/VideoCaptureScreen.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/videocapture/VideoCaptureScreen.kt
@@ -16,6 +16,7 @@
package androidx.camera.integration.uiwidgets.compose.ui.screen.videocapture
+import android.util.Log
import android.view.ViewGroup
import androidx.camera.core.MeteringPoint
import androidx.camera.core.Preview.SurfaceProvider
@@ -39,6 +40,7 @@
import androidx.compose.material.icons.sharp.FlipCameraAndroid
import androidx.compose.material.icons.sharp.Lens
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -48,11 +50,15 @@
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.Observer
+
+private const val TAG = "VideoCaptureScreen"
@Composable
fun VideoCaptureScreen(
modifier: Modifier = Modifier,
- state: VideoCaptureScreenState = rememberVideoCaptureScreenState()
+ state: VideoCaptureScreenState = rememberVideoCaptureScreenState(),
+ onStreamStateChange: (PreviewView.StreamState) -> Unit = {}
) {
val lifecycleOwner = LocalLifecycleOwner.current
val localContext = LocalContext.current
@@ -74,7 +80,8 @@
state.captureVideo(localContext)
},
onSurfaceProviderReady = state::setSurfaceProvider,
- onTouch = state::startTapToFocus
+ onTouch = state::startTapToFocus,
+ onStreamStateChange = onStreamStateChange
)
}
@@ -90,10 +97,18 @@
onFlipCameraIconClicked: () -> Unit,
onVideoCaptureIconClicked: () -> Unit,
onSurfaceProviderReady: (SurfaceProvider) -> Unit,
- onTouch: (MeteringPoint) -> Unit
+ onTouch: (MeteringPoint) -> Unit,
+ onStreamStateChange: (PreviewView.StreamState) -> Unit = {}
) {
+ val lifecycleOwner = LocalLifecycleOwner.current
val localContext = LocalContext.current
+ val streamStateObserver = remember {
+ Observer<PreviewView.StreamState> { state ->
+ onStreamStateChange(state)
+ }
+ }
+
val previewView = remember {
PreviewView(localContext).apply {
layoutParams = ViewGroup.LayoutParams(
@@ -113,6 +128,21 @@
}
}
+ // Attach StreamState Observer when the screen first renders
+ DisposableEffect(key1 = Unit) {
+ Log.d(TAG, "[DisposableEffect] Detaching StreamState Observers from PreviewView")
+ previewView.previewStreamState.removeObservers(lifecycleOwner)
+
+ Log.d(TAG, "[DisposableEffect] Attaching StreamState Observer to PreviewView")
+ previewView.previewStreamState.observe(lifecycleOwner, streamStateObserver)
+
+ // Detach observer when the screen is removed from the Composition
+ onDispose {
+ Log.d(TAG, "[onDispose] Detaching current StreamState Observer from PreviewView")
+ previewView.previewStreamState.removeObservers(lifecycleOwner)
+ }
+ }
+
Box(modifier = modifier.fillMaxSize()) {
AndroidView(
factory = { previewView }
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 8f2297c..34cfdbc 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -33,11 +33,11 @@
method public final androidx.car.app.HostInfo? getHostInfo();
method public final androidx.car.app.Session? getSession(androidx.car.app.SessionInfo);
method @CallSuper public final android.os.IBinder onBind(android.content.Intent);
- method @Deprecated public androidx.car.app.Session onCreateSession();
- method public androidx.car.app.Session onCreateSession(androidx.car.app.SessionInfo);
+ method public androidx.car.app.Session onCreateSession();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.Session onCreateSession(androidx.car.app.SessionInfo);
method public final boolean onUnbind(android.content.Intent);
field @Deprecated public static final String CATEGORY_CHARGING_APP = "androidx.car.app.category.CHARGING";
- field public static final String CATEGORY_FEATURE_CLUSTER = "androidx.car.app.category.FEATURE_CLUSTER";
+ field @androidx.car.app.annotations.RequiresCarApi(6) public static final String CATEGORY_FEATURE_CLUSTER = "androidx.car.app.category.FEATURE_CLUSTER";
field public static final String CATEGORY_NAVIGATION_APP = "androidx.car.app.category.NAVIGATION";
field @Deprecated public static final String CATEGORY_PARKING_APP = "androidx.car.app.category.PARKING";
field public static final String CATEGORY_POI_APP = "androidx.car.app.category.POI";
@@ -158,7 +158,7 @@
method public void onNewIntent(android.content.Intent);
}
- @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class SessionInfo {
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class SessionInfo {
ctor public SessionInfo(int, String);
method public int getDisplayType();
method public String getSessionId();
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 77984e8..2062816 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -33,11 +33,11 @@
method public final androidx.car.app.HostInfo? getHostInfo();
method public final androidx.car.app.Session? getSession(androidx.car.app.SessionInfo);
method @CallSuper public final android.os.IBinder onBind(android.content.Intent);
- method @Deprecated public androidx.car.app.Session onCreateSession();
- method public androidx.car.app.Session onCreateSession(androidx.car.app.SessionInfo);
+ method public androidx.car.app.Session onCreateSession();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.Session onCreateSession(androidx.car.app.SessionInfo);
method public final boolean onUnbind(android.content.Intent);
field @Deprecated public static final String CATEGORY_CHARGING_APP = "androidx.car.app.category.CHARGING";
- field public static final String CATEGORY_FEATURE_CLUSTER = "androidx.car.app.category.FEATURE_CLUSTER";
+ field @androidx.car.app.annotations.RequiresCarApi(6) public static final String CATEGORY_FEATURE_CLUSTER = "androidx.car.app.category.FEATURE_CLUSTER";
field public static final String CATEGORY_NAVIGATION_APP = "androidx.car.app.category.NAVIGATION";
field @Deprecated public static final String CATEGORY_PARKING_APP = "androidx.car.app.category.PARKING";
field public static final String CATEGORY_POI_APP = "androidx.car.app.category.POI";
@@ -159,7 +159,7 @@
method public void onNewIntent(android.content.Intent);
}
- @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class SessionInfo {
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class SessionInfo {
ctor public SessionInfo(int, String);
method public int getDisplayType();
method public String getSessionId();
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 8f2297c..34cfdbc 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -33,11 +33,11 @@
method public final androidx.car.app.HostInfo? getHostInfo();
method public final androidx.car.app.Session? getSession(androidx.car.app.SessionInfo);
method @CallSuper public final android.os.IBinder onBind(android.content.Intent);
- method @Deprecated public androidx.car.app.Session onCreateSession();
- method public androidx.car.app.Session onCreateSession(androidx.car.app.SessionInfo);
+ method public androidx.car.app.Session onCreateSession();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.Session onCreateSession(androidx.car.app.SessionInfo);
method public final boolean onUnbind(android.content.Intent);
field @Deprecated public static final String CATEGORY_CHARGING_APP = "androidx.car.app.category.CHARGING";
- field public static final String CATEGORY_FEATURE_CLUSTER = "androidx.car.app.category.FEATURE_CLUSTER";
+ field @androidx.car.app.annotations.RequiresCarApi(6) public static final String CATEGORY_FEATURE_CLUSTER = "androidx.car.app.category.FEATURE_CLUSTER";
field public static final String CATEGORY_NAVIGATION_APP = "androidx.car.app.category.NAVIGATION";
field @Deprecated public static final String CATEGORY_PARKING_APP = "androidx.car.app.category.PARKING";
field public static final String CATEGORY_POI_APP = "androidx.car.app.category.POI";
@@ -158,7 +158,7 @@
method public void onNewIntent(android.content.Intent);
}
- @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class SessionInfo {
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class SessionInfo {
ctor public SessionInfo(int, String);
method public int getDisplayType();
method public String getSessionId();
diff --git a/car/app/app/src/main/java/androidx/car/app/CarAppService.java b/car/app/app/src/main/java/androidx/car/app/CarAppService.java
index a55db0b..3da6251 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarAppService.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarAppService.java
@@ -31,6 +31,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.car.app.annotations.ExperimentalCarApi;
+import androidx.car.app.annotations.RequiresCarApi;
import androidx.car.app.validation.HostValidator;
import java.io.FileDescriptor;
@@ -90,6 +91,7 @@
/**
* Used to declare that this app supports cluster in the manifest.
*/
+ @RequiresCarApi(6)
public static final String CATEGORY_FEATURE_CLUSTER =
"androidx.car.app.category.FEATURE_CLUSTER";
@@ -253,12 +255,8 @@
* <p>Called by the system, do not call this method directly.
*
* @see CarContext#startCarApp(Intent)
- * @deprecated this method continues to exist for backwards compatibility; however, is
- * succeeded by {@link #onCreateSession(SessionInfo)}. Prefer to implement {@link
- * #onCreateSession(SessionInfo)}.
*/
@NonNull
- @Deprecated
public Session onCreateSession() {
throw new RuntimeException(
"Please override and implement CarAppService#onCreateSession(SessionInfo).");
@@ -280,6 +278,7 @@
*/
@NonNull
@SuppressWarnings("deprecation")
+ @RequiresCarApi(6)
public Session onCreateSession(@NonNull SessionInfo sessionInfo) {
return onCreateSession();
}
diff --git a/car/app/app/src/main/java/androidx/car/app/SessionInfo.java b/car/app/app/src/main/java/androidx/car/app/SessionInfo.java
index 3176923..ce2f647 100644
--- a/car/app/app/src/main/java/androidx/car/app/SessionInfo.java
+++ b/car/app/app/src/main/java/androidx/car/app/SessionInfo.java
@@ -36,7 +36,7 @@
import java.util.Set;
/** Information about a {@link Session}, such as the physical display and the session ID. */
-@RequiresCarApi(5)
+@RequiresCarApi(6)
@CarProtocol
public class SessionInfo {
private static final char DIVIDER = '/';
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index 74fa853..9982e90 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -208,13 +208,13 @@
}
public final class ChipKt {
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void AssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.material3.ChipColors colors);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedAssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.material3.ChipColors colors);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedFilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.material3.SelectableChipColors colors);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedSuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.material3.ChipColors colors);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void FilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.material3.SelectableChipColors colors);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void InputChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? avatar, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.material3.SelectableChipColors colors);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.material3.ChipColors colors);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void AssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedAssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedFilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipColors colors, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedSuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void FilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipColors colors, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void InputChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? avatar, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipColors colors, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
@androidx.compose.runtime.Stable public final class ColorScheme {
@@ -601,12 +601,12 @@
}
public final class NavigationDrawerKt {
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DismissibleDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DismissibleDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DismissibleNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void NavigationDrawerItem(kotlin.jvm.functions.Function0<kotlin.Unit> label, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.NavigationDrawerItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void PermanentDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void PermanentDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void PermanentNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.DrawerState rememberDrawerState(androidx.compose.material3.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
index 71ae1a7..a66ff79 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
@@ -87,19 +87,19 @@
* services.
* @param leadingIcon optional icon at the start of the chip, preceding the [label] text
* @param trailingIcon optional icon at the end of the chip
- * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
- * for this chip. You can create and pass in your own `remember`ed instance to observe
- * [Interaction]s and customize the appearance / behavior of this chip in different states.
+ * @param shape defines the shape of this chip's container, border (when [border] is not null), and
+ * shadow (when using [elevation])
+ * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
+ * different states. See [AssistChipDefaults.assistChipColors].
* @param elevation [ChipElevation] used to resolve the elevation for this chip in different states.
* This controls the size of the shadow below the chip. Additionally, when the container color is
* [ColorScheme.surface], this controls the amount of primary color applied as an overlay. See
* [AssistChipDefaults.assistChipElevation].
- * @param shape defines the shape of this chip's container, border (when [border] is not null), and
- * shadow (when using [elevation])
* @param border the border to draw around the container of this chip. Pass `null` for no border.
* See [AssistChipDefaults.assistChipBorder].
- * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
- * different states. See [AssistChipDefaults.assistChipColors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this chip. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this chip in different states.
*/
@ExperimentalMaterial3Api
@Composable
@@ -110,11 +110,11 @@
enabled: Boolean = true,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- elevation: ChipElevation? = AssistChipDefaults.assistChipElevation(),
shape: Shape = AssistChipDefaults.shape,
+ colors: ChipColors = AssistChipDefaults.assistChipColors(),
+ elevation: ChipElevation? = AssistChipDefaults.assistChipElevation(),
border: ChipBorder? = AssistChipDefaults.assistChipBorder(),
- colors: ChipColors = AssistChipDefaults.assistChipColors()
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) = Chip(
modifier = modifier,
onClick = onClick,
@@ -124,12 +124,12 @@
labelColor = colors.labelColor(enabled).value,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
- elevation = elevation,
+ shape = shape,
colors = colors,
+ elevation = elevation,
+ border = border?.borderStroke(enabled)?.value,
minHeight = AssistChipDefaults.Height,
paddingValues = AssistChipPadding,
- shape = shape,
- border = border?.borderStroke(enabled)?.value,
interactionSource = interactionSource
)
@@ -160,18 +160,18 @@
* services.
* @param leadingIcon optional icon at the start of the chip, preceding the [label] text
* @param trailingIcon optional icon at the end of the chip
- * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
- * for this chip. You can create and pass in your own `remember`ed instance to observe
- * [Interaction]s and customize the appearance / behavior of this chip in different states.
+ * @param shape defines the shape of this chip's container, border (when [border] is not null), and
+ * shadow (when using [elevation])
+ * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
+ * different states. See [AssistChipDefaults.elevatedAssistChipColors].
* @param elevation [ChipElevation] used to resolve the elevation for this chip in different states.
* This controls the size of the shadow below the chip. Additionally, when the container color is
* [ColorScheme.surface], this controls the amount of primary color applied as an overlay. See
* [AssistChipDefaults.elevatedAssistChipElevation].
- * @param shape defines the shape of this chip's container, border (when [border] is not null), and
- * shadow (when using [elevation])
* @param border the border to draw around the container of this chip
- * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
- * different states. See [AssistChipDefaults.elevatedAssistChipColors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this chip. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this chip in different states.
*/
@ExperimentalMaterial3Api
@Composable
@@ -182,11 +182,11 @@
enabled: Boolean = true,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- elevation: ChipElevation? = AssistChipDefaults.elevatedAssistChipElevation(),
shape: Shape = AssistChipDefaults.shape,
+ colors: ChipColors = AssistChipDefaults.elevatedAssistChipColors(),
+ elevation: ChipElevation? = AssistChipDefaults.elevatedAssistChipElevation(),
border: ChipBorder? = null,
- colors: ChipColors = AssistChipDefaults.elevatedAssistChipColors()
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) = Chip(
modifier = modifier,
onClick = onClick,
@@ -240,19 +240,19 @@
* [selected] is true, this icon may visually indicate that the chip is selected (for example, via a
* checkmark icon).
* @param trailingIcon optional icon at the end of the chip
- * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
- * for this chip. You can create and pass in your own `remember`ed instance to observe
- * [Interaction]s and customize the appearance / behavior of this chip in different states.
+ * @param shape defines the shape of this chip's container, border (when [border] is not null), and
+ * shadow (when using [elevation])
+ * @param colors [SelectableChipColors] that will be used to resolve the colors used for this chip
+ * in different states. See [FilterChipDefaults.filterChipColors].
* @param elevation [SelectableChipElevation] used to resolve the elevation for this chip in
* different states. This controls the size of the shadow below the chip. Additionally, when the
* container color is [ColorScheme.surface], this controls the amount of primary color applied as an
* overlay. See [FilterChipDefaults.filterChipElevation].
- * @param shape defines the shape of this chip's container, border (when [border] is not null), and
- * shadow (when using [elevation])
* @param border the border to draw around the container of this chip. Pass `null` for no border.
* See [FilterChipDefaults.filterChipBorder].
- * @param colors [SelectableChipColors] that will be used to resolve the colors used for this chip
- * in different states. See [FilterChipDefaults.filterChipColors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this chip. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this chip in different states.
*/
@ExperimentalMaterial3Api
@Composable
@@ -264,11 +264,11 @@
enabled: Boolean = true,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- elevation: SelectableChipElevation? = FilterChipDefaults.filterChipElevation(),
shape: Shape = FilterChipDefaults.shape,
+ colors: SelectableChipColors = FilterChipDefaults.filterChipColors(),
+ elevation: SelectableChipElevation? = FilterChipDefaults.filterChipElevation(),
border: SelectableChipBorder? = FilterChipDefaults.filterChipBorder(),
- colors: SelectableChipColors = FilterChipDefaults.filterChipColors()
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) = SelectableChip(
selected = selected,
modifier = modifier,
@@ -320,19 +320,19 @@
* [selected] is true, this icon may visually indicate that the chip is selected (for example, via a
* checkmark icon).
* @param trailingIcon optional icon at the end of the chip
- * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
- * for this chip. You can create and pass in your own `remember`ed instance to observe
- * [Interaction]s and customize the appearance / behavior of this chip in different states.
+ * @param shape defines the shape of this chip's container, border (when [border] is not null), and
+ * shadow (when using [elevation])
+ * @param colors [SelectableChipColors] that will be used to resolve the colors used for this chip
+ * in different states. See [FilterChipDefaults.elevatedFilterChipColors].
* @param elevation [SelectableChipElevation] used to resolve the elevation for this chip in
* different states. This controls the size of the shadow below the chip. Additionally, when the
* container color is [ColorScheme.surface], this controls the amount of primary color applied as an
* overlay. See [FilterChipDefaults.filterChipElevation].
- * @param shape defines the shape of this chip's container, border (when [border] is not null), and
- * shadow (when using [elevation])
* @param border the border to draw around the container of this chip. Pass `null` for no border.
* See [FilterChipDefaults.filterChipBorder].
- * @param colors [SelectableChipColors] that will be used to resolve the colors used for this chip
- * in different states. See [FilterChipDefaults.elevatedFilterChipColors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this chip. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this chip in different states.
*/
@ExperimentalMaterial3Api
@Composable
@@ -344,11 +344,11 @@
enabled: Boolean = true,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- elevation: SelectableChipElevation? = FilterChipDefaults.elevatedFilterChipElevation(),
shape: Shape = FilterChipDefaults.shape,
+ colors: SelectableChipColors = FilterChipDefaults.elevatedFilterChipColors(),
+ elevation: SelectableChipElevation? = FilterChipDefaults.elevatedFilterChipElevation(),
border: SelectableChipBorder? = null,
- colors: SelectableChipColors = FilterChipDefaults.elevatedFilterChipColors()
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) = SelectableChip(
selected = selected,
modifier = modifier,
@@ -404,19 +404,19 @@
* @param leadingIcon optional icon at the start of the chip, preceding the [label] text
* @param avatar optional avatar at the start of the chip, preceding the [label] text
* @param trailingIcon optional icon at the end of the chip
- * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
- * for this chip. You can create and pass in your own `remember`ed instance to observe
- * [Interaction]s and customize the appearance / behavior of this chip in different states.
+ * @param shape defines the shape of this chip's container, border (when [border] is not null), and
+ * shadow (when using [elevation])
+ * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
+ * different states. See [InputChipDefaults.inputChipColors].
* @param elevation [ChipElevation] used to resolve the elevation for this chip in different states.
* This controls the size of the shadow below the chip. Additionally, when the container color is
* [ColorScheme.surface], this controls the amount of primary color applied as an overlay. See
* [InputChipDefaults.inputChipElevation].
- * @param shape defines the shape of this chip's container, border (when [border] is not null), and
- * shadow (when using [elevation])
* @param border the border to draw around the container of this chip. Pass `null` for no border.
* See [InputChipDefaults.inputChipBorder].
- * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
- * different states. See [InputChipDefaults.inputChipColors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this chip. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this chip in different states.
*/
@ExperimentalMaterial3Api
@Composable
@@ -429,11 +429,11 @@
leadingIcon: @Composable (() -> Unit)? = null,
avatar: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- elevation: SelectableChipElevation? = InputChipDefaults.inputChipElevation(),
shape: Shape = InputChipDefaults.shape,
+ colors: SelectableChipColors = InputChipDefaults.inputChipColors(),
+ elevation: SelectableChipElevation? = InputChipDefaults.inputChipElevation(),
border: SelectableChipBorder? = InputChipDefaults.inputChipBorder(),
- colors: SelectableChipColors = InputChipDefaults.inputChipColors()
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
// If given, place the avatar in an InputChipTokens.AvatarShape shape before passing it into the
// Chip function.
@@ -464,16 +464,16 @@
leadingIcon = leadingIcon,
avatar = shapedAvatar,
trailingIcon = trailingIcon,
- elevation = elevation,
+ shape = shape,
colors = colors,
+ elevation = elevation,
+ border = border?.borderStroke(enabled, selected)?.value,
minHeight = InputChipDefaults.Height,
paddingValues = inputChipPadding(
hasAvatar = shapedAvatar != null,
hasLeadingIcon = leadingIcon != null,
hasTrailingIcon = trailingIcon != null
),
- shape = shape,
- border = border?.borderStroke(enabled, selected)?.value,
interactionSource = interactionSource
)
}
@@ -503,19 +503,19 @@
* respond to user input, and it will appear visually disabled and disabled to accessibility
* services.
* @param icon optional icon at the start of the chip, preceding the [label] text
- * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
- * for this chip. You can create and pass in your own `remember`ed instance to observe
- * [Interaction]s and customize the appearance / behavior of this chip in different states.
+ * @param shape defines the shape of this chip's container, border (when [border] is not null), and
+ * shadow (when using [elevation])
+ * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
+ * different states. See [SuggestionChipDefaults.suggestionChipColors].
* @param elevation [ChipElevation] used to resolve the elevation for this chip in different states.
* This controls the size of the shadow below the chip. Additionally, when the container color is
* [ColorScheme.surface], this controls the amount of primary color applied as an overlay. See
* [SuggestionChipDefaults.suggestionChipElevation].
- * @param shape defines the shape of this chip's container, border (when [border] is not null), and
- * shadow (when using [elevation])
* @param border the border to draw around the container of this chip. Pass `null` for no border.
* See [SuggestionChipDefaults.suggestionChipBorder].
- * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
- * different states. See [SuggestionChipDefaults.suggestionChipColors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this chip. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this chip in different states.
*/
@ExperimentalMaterial3Api
@Composable
@@ -525,11 +525,11 @@
modifier: Modifier = Modifier,
enabled: Boolean = true,
icon: @Composable (() -> Unit)? = null,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- elevation: ChipElevation? = SuggestionChipDefaults.suggestionChipElevation(),
shape: Shape = SuggestionChipDefaults.shape,
+ colors: ChipColors = SuggestionChipDefaults.suggestionChipColors(),
+ elevation: ChipElevation? = SuggestionChipDefaults.suggestionChipElevation(),
border: ChipBorder? = SuggestionChipDefaults.suggestionChipBorder(),
- colors: ChipColors = SuggestionChipDefaults.suggestionChipColors()
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) = Chip(
modifier = modifier,
onClick = onClick,
@@ -539,12 +539,12 @@
labelColor = colors.labelColor(enabled).value,
leadingIcon = icon,
trailingIcon = null,
- elevation = elevation,
+ shape = shape,
colors = colors,
+ elevation = elevation,
+ border = border?.borderStroke(enabled)?.value,
minHeight = SuggestionChipDefaults.Height,
paddingValues = SuggestionChipPadding,
- shape = shape,
- border = border?.borderStroke(enabled)?.value,
interactionSource = interactionSource
)
@@ -573,18 +573,18 @@
* respond to user input, and it will appear visually disabled and disabled to accessibility
* services.
* @param icon optional icon at the start of the chip, preceding the [label] text
- * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
- * for this chip. You can create and pass in your own `remember`ed instance to observe
- * [Interaction]s and customize the appearance / behavior of this chip in different states.
+ * @param shape defines the shape of this chip's container, border (when [border] is not null), and
+ * shadow (when using [elevation])
+ * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
* @param elevation [ChipElevation] used to resolve the elevation for this chip in different states.
* This controls the size of the shadow below the chip. Additionally, when the container color is
* [ColorScheme.surface], this controls the amount of primary color applied as an overlay. See
* [Surface] and [SuggestionChipDefaults.elevatedSuggestionChipElevation].
- * @param shape defines the shape of this chip's container, border (when [border] is not null), and
- * shadow (when using [elevation])
* @param border the border to draw around the container of this chip
- * @param colors [ChipColors] that will be used to resolve the colors used for this chip in
* different states. See [SuggestionChipDefaults.elevatedSuggestionChipColors].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this chip. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this chip in different states.
*/
@ExperimentalMaterial3Api
@Composable
@@ -594,11 +594,11 @@
modifier: Modifier = Modifier,
enabled: Boolean = true,
icon: @Composable (() -> Unit)? = null,
- interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- elevation: ChipElevation? = SuggestionChipDefaults.elevatedSuggestionChipElevation(),
shape: Shape = SuggestionChipDefaults.shape,
+ colors: ChipColors = SuggestionChipDefaults.elevatedSuggestionChipColors(),
+ elevation: ChipElevation? = SuggestionChipDefaults.elevatedSuggestionChipElevation(),
border: ChipBorder? = null,
- colors: ChipColors = SuggestionChipDefaults.elevatedSuggestionChipColors()
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) = Chip(
modifier = modifier,
onClick = onClick,
@@ -1637,12 +1637,12 @@
labelColor: Color,
leadingIcon: @Composable (() -> Unit)?,
trailingIcon: @Composable (() -> Unit)?,
- elevation: ChipElevation?,
+ shape: Shape,
colors: ChipColors,
+ elevation: ChipElevation?,
+ border: BorderStroke?,
minHeight: Dp,
paddingValues: PaddingValues,
- shape: Shape,
- border: BorderStroke?,
interactionSource: MutableInteractionSource,
) {
Surface(
@@ -1683,12 +1683,12 @@
leadingIcon: @Composable (() -> Unit)?,
avatar: @Composable (() -> Unit)?,
trailingIcon: @Composable (() -> Unit)?,
- elevation: SelectableChipElevation?,
+ shape: Shape,
colors: SelectableChipColors,
+ elevation: SelectableChipElevation?,
+ border: BorderStroke?,
minHeight: Dp,
paddingValues: PaddingValues,
- shape: Shape,
- border: BorderStroke?,
interactionSource: MutableInteractionSource
) {
// TODO(b/229794614): Animate transition between unselected and selected.
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
index 0fea002..d1f14be 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
@@ -438,14 +438,14 @@
*
* @param modifier the [Modifier] to be applied to this drawer's content
* @param drawerShape defines the shape of this drawer's container
- * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
- * primary color overlay is applied on top of the container. A higher tonal elevation value will
- * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
* @param drawerContainerColor the color used for the background of this drawer. Use
* [Color.Transparent] to have no color.
* @param drawerContentColor the preferred color for content inside this drawer. Defaults to either
* the matching content color for [drawerContainerColor], or to the current [LocalContentColor] if
* [drawerContainerColor] is not a color from the theme.
+ * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
+ * primary color overlay is applied on top of the container. A higher tonal elevation value will
+ * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
* @param content content inside of a modal navigation drawer
*/
@ExperimentalMaterial3Api
@@ -453,17 +453,17 @@
fun ModalDrawerSheet(
modifier: Modifier = Modifier,
drawerShape: Shape = DrawerDefaults.shape,
- drawerTonalElevation: Dp = DrawerDefaults.ModalDrawerElevation,
drawerContainerColor: Color = MaterialTheme.colorScheme.surface,
drawerContentColor: Color = contentColorFor(drawerContainerColor),
+ drawerTonalElevation: Dp = DrawerDefaults.ModalDrawerElevation,
content: @Composable ColumnScope.() -> Unit
) {
DrawerSheet(
modifier,
drawerShape,
- drawerTonalElevation,
drawerContainerColor,
drawerContentColor,
+ drawerTonalElevation,
content
)
}
@@ -473,14 +473,14 @@
*
* @param modifier the [Modifier] to be applied to this drawer's content
* @param drawerShape defines the shape of this drawer's container
- * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
- * primary color overlay is applied on top of the container. A higher tonal elevation value will
- * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
* @param drawerContainerColor the color used for the background of this drawer. Use
* [Color.Transparent] to have no color.
* @param drawerContentColor the preferred color for content inside this drawer. Defaults to either
* the matching content color for [drawerContainerColor], or to the current [LocalContentColor] if
* [drawerContainerColor] is not a color from the theme.
+ * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
+ * primary color overlay is applied on top of the container. A higher tonal elevation value will
+ * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
* @param content content inside of a dismissible navigation drawer
*/
@ExperimentalMaterial3Api
@@ -488,17 +488,17 @@
fun DismissibleDrawerSheet(
modifier: Modifier = Modifier,
drawerShape: Shape = RectangleShape,
- drawerTonalElevation: Dp = DrawerDefaults.DismissibleDrawerElevation,
drawerContainerColor: Color = MaterialTheme.colorScheme.surface,
drawerContentColor: Color = contentColorFor(drawerContainerColor),
+ drawerTonalElevation: Dp = DrawerDefaults.DismissibleDrawerElevation,
content: @Composable ColumnScope.() -> Unit
) {
DrawerSheet(
modifier,
drawerShape,
- drawerTonalElevation,
drawerContainerColor,
drawerContentColor,
+ drawerTonalElevation,
content
)
}
@@ -508,14 +508,14 @@
*
* @param modifier the [Modifier] to be applied to this drawer's content
* @param drawerShape defines the shape of this drawer's container
- * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
- * primary color overlay is applied on top of the container. A higher tonal elevation value will
- * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
* @param drawerContainerColor the color used for the background of this drawer. Use
* [Color.Transparent] to have no color.
* @param drawerContentColor the preferred color for content inside this drawer. Defaults to either
* the matching content color for [drawerContainerColor], or to the current [LocalContentColor] if
* [drawerContainerColor] is not a color from the theme.
+ * @param drawerTonalElevation when [drawerContainerColor] is [ColorScheme.surface], a translucent
+ * primary color overlay is applied on top of the container. A higher tonal elevation value will
+ * result in a darker color in light theme and lighter color in dark theme. See also: [Surface].
* @param content content inside a permanent navigation drawer
*/
@ExperimentalMaterial3Api
@@ -523,9 +523,9 @@
fun PermanentDrawerSheet(
modifier: Modifier = Modifier,
drawerShape: Shape = RectangleShape,
- drawerTonalElevation: Dp = DrawerDefaults.PermanentDrawerElevation,
drawerContainerColor: Color = MaterialTheme.colorScheme.surface,
drawerContentColor: Color = contentColorFor(drawerContainerColor),
+ drawerTonalElevation: Dp = DrawerDefaults.PermanentDrawerElevation,
content: @Composable ColumnScope.() -> Unit
) {
val navigationMenu = getString(Strings.NavigationMenu)
@@ -534,9 +534,9 @@
paneTitle = navigationMenu
},
drawerShape,
- drawerTonalElevation,
drawerContainerColor,
drawerContentColor,
+ drawerTonalElevation,
content
)
}
@@ -546,9 +546,9 @@
private fun DrawerSheet(
modifier: Modifier = Modifier,
drawerShape: Shape = RectangleShape,
- drawerTonalElevation: Dp = DrawerDefaults.PermanentDrawerElevation,
drawerContainerColor: Color = MaterialTheme.colorScheme.surface,
drawerContentColor: Color = contentColorFor(drawerContainerColor),
+ drawerTonalElevation: Dp = DrawerDefaults.PermanentDrawerElevation,
content: @Composable ColumnScope.() -> Unit
) {
Surface(
diff --git a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt
index 89882d6..3b428b0 100644
--- a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt
+++ b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt
@@ -21,6 +21,7 @@
import android.os.Bundle
import androidx.compose.animation.core.InternalAnimationApi
import androidx.compose.ui.tooling.animation.PreviewAnimationClock
+import androidx.compose.ui.tooling.animation.UnsupportedComposeAnimation
import androidx.compose.ui.tooling.data.UiToolingDataApi
import androidx.compose.ui.tooling.test.R
import androidx.test.filters.LargeTest
@@ -155,7 +156,7 @@
@Test
fun animatedContentIsSubscribed() {
- checkUnsupportedIsSubscribed("AnimatedContentPreview", listOf("AnimatedContent"))
+ checkAnimationsAreSubscribed("AnimatedContentPreview", listOf("AnimatedContent"))
}
@Test
@@ -170,7 +171,7 @@
@Test
fun animateXAsStateIsSubscribed() {
- checkUnsupportedIsSubscribed(
+ checkAnimationsAreSubscribed(
"AnimateAsStatePreview",
listOf("animateValueAsState", "animateValueAsState")
)
@@ -178,7 +179,7 @@
@Test
fun animateContentSizeIsSubscribed() {
- checkUnsupportedIsSubscribed("AnimateContentSizePreview", listOf("animateContentSize"))
+ checkAnimationsAreSubscribed("AnimateContentSizePreview", listOf("animateContentSize"))
}
@Test
@@ -188,20 +189,35 @@
@Test
fun targetBasedAnimationIsSubscribed() {
- checkUnsupportedIsSubscribed("TargetBasedAnimationPreview", listOf("TargetBasedAnimation"))
+ checkAnimationsAreSubscribed("TargetBasedAnimationPreview", listOf("TargetBasedAnimation"))
}
@Test
fun decayAnimationIsSubscribed() {
- checkUnsupportedIsSubscribed("DecayAnimationPreview", listOf("DecayAnimation"))
+ checkAnimationsAreSubscribed("DecayAnimationPreview", listOf("DecayAnimation"))
}
@Test
fun infiniteTransitionIsSubscribed() {
- checkUnsupportedIsSubscribed("InfiniteTransitionPreview", listOf("InfiniteTransition"))
+ checkAnimationsAreSubscribed("InfiniteTransitionPreview", listOf("InfiniteTransition"))
}
- private fun checkUnsupportedIsSubscribed(preview: String, labels: List<String>) {
+ @Test
+ fun unsupportedAreNotSubscribedWhenEnumIsNotAvailable() {
+ UnsupportedComposeAnimation.testOverrideAvailability(false)
+ checkAnimationsAreSubscribed(
+ "AllAnimations",
+ emptyList(),
+ listOf("String", "checkBoxAnim")
+ )
+ UnsupportedComposeAnimation.testOverrideAvailability(true)
+ }
+
+ private fun checkAnimationsAreSubscribed(
+ preview: String,
+ unsupported: List<String>,
+ transitions: List<String> = emptyList()
+ ) {
val clock = PreviewAnimationClock()
activityTestRule.runOnUiThread {
@@ -212,9 +228,11 @@
composeViewAdapter.clock = clock
assertFalse(composeViewAdapter.hasAnimations())
assertTrue(clock.trackedTransitions.isEmpty())
+ assertTrue(clock.trackedUnsupported.isEmpty())
+ assertTrue(clock.trackedAnimatedVisibility.isEmpty())
}
- waitFor("Composable to have animations", 1, TimeUnit.SECONDS) {
+ waitFor("Composable to have animations", 2, TimeUnit.SECONDS) {
// Handle the case where onLayout was called too soon. Calling requestLayout will
// make sure onLayout will be called again.
composeViewAdapter.requestLayout()
@@ -222,8 +240,8 @@
}
activityTestRule.runOnUiThread {
- assertEquals(labels, clock.trackedUnsupported.map { it.label }.sortedBy { it })
- assertEquals(0, clock.trackedTransitions.size)
+ assertEquals(unsupported, clock.trackedUnsupported.map { it.label }.sortedBy { it })
+ assertEquals(transitions, clock.trackedTransitions.map { it.label }.sortedBy { it })
assertEquals(0, clock.trackedAnimatedVisibility.size)
}
}
diff --git a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/TestAnimationPreview.kt b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/TestAnimationPreview.kt
index 408d822..e322def 100644
--- a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/TestAnimationPreview.kt
+++ b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/TestAnimationPreview.kt
@@ -89,6 +89,19 @@
}
}
+@Preview(name = "All unsupported and transition animations")
+@Composable
+fun AllAnimations() {
+ AnimatedContentPreview()
+ CheckBox()
+ AnimateAsStatePreview()
+ CrossFadePreview()
+ AnimateContentSizePreview()
+ TargetBasedAnimationPreview()
+ DecayAnimationPreview()
+ InfiniteTransitionPreview()
+}
+
@OptIn(ExperimentalAnimationApi::class)
@Preview(name = "AnimatedContent")
@Composable
diff --git a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/ComposeViewAdapter.kt b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/ComposeViewAdapter.kt
index fe71638..c200a5f 100644
--- a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/ComposeViewAdapter.kt
+++ b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/ComposeViewAdapter.kt
@@ -73,6 +73,7 @@
import androidx.compose.animation.core.DecayAnimation
import androidx.compose.animation.core.InfiniteTransition
import androidx.compose.animation.core.TargetBasedAnimation
+import androidx.compose.ui.tooling.animation.UnsupportedComposeAnimation
import kotlin.reflect.KClass
import kotlin.reflect.safeCast
@@ -435,10 +436,14 @@
val animatedVisibilitySearch = AnimatedVisibilitySearch {
clock.trackAnimatedVisibility(it as Transition<Any>, ::requestLayout)
}
-
- val search = listOf(
+ // All supported animations.
+ val supportedSearch = setOf(
transitionSearch,
animatedVisibilitySearch,
+ )
+
+ // All unsupported animations, if API is available.
+ val extraSearch = if (UnsupportedComposeAnimation.apiAvailable) setOf(
animatedContentSearch,
AnimateXAsStateSearch { clock.trackAnimateXAsState(it as Animatable<*, *>) },
AnimateContentSizeSearch { clock.trackAnimateContentSize(it) },
@@ -451,14 +456,21 @@
RememberSearch(InfiniteTransition::class) {
clock.trackInfiniteTransition(it as InfiniteTransition)
}
- )
+ ) else emptyList()
+
+ // Animations to track in PreviewAnimationClock.
+ val setToTrack = supportedSearch + extraSearch
+
+ // Animations to search. animatedContentSearch is included even if it's not going to be
+ // tracked as it should be excluded from transitionSearch.
+ val setToSearch = setToTrack + setOf(animatedContentSearch)
// Check all the slot tables, since some animations might not be present in the same
// table as the one containing the `@Composable` being previewed, e.g. when they're
// defined using sub-composition.
slotTrees.forEach { tree ->
val treeWithLocation = tree.findAll { it.location != null }
- search.forEach { it.parse(treeWithLocation) }
+ setToSearch.forEach { it.parse(treeWithLocation) }
// Remove all AnimatedVisibility parent transitions from the transitions list,
// otherwise we'd duplicate them in the Android Studio Animation Preview because we
@@ -470,11 +482,11 @@
transitionSearch.animations.removeAll(animatedContentSearch.animations)
}
- hasAnimations = search.any { it.hasAnimations() }
+ hasAnimations = setToTrack.any { it.hasAnimations() }
// Make the `PreviewAnimationClock` track all the transitions found.
if (::clock.isInitialized) {
- search.forEach { it.track() }
+ setToTrack.forEach { it.track() }
}
}
diff --git a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/ComposeAnimationParser.kt b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/ComposeAnimationParser.kt
index abc83a1..f5cc293 100644
--- a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/ComposeAnimationParser.kt
+++ b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/ComposeAnimationParser.kt
@@ -20,6 +20,7 @@
import androidx.compose.animation.core.Transition
import androidx.compose.animation.tooling.ComposeAnimation
import androidx.compose.animation.tooling.ComposeAnimationType
+import org.jetbrains.annotations.TestOnly
// TODO(b/160126628): support other animation types, e.g. single animated value
/**
@@ -73,12 +74,32 @@
/**
* [ComposeAnimation] of type [ComposeAnimationType.UNSUPPORTED].
*/
-internal class UnsupportedComposeAnimation(
+internal class UnsupportedComposeAnimation private constructor(
override val label: String?
) : ComposeAnimation {
override val type = ComposeAnimationType.UNSUPPORTED
override val animationObject: Any = 0
override val states = emptySet<Int>()
+
+ companion object {
+ /**
+ * [ComposeAnimationType] from ANIMATABLE to UNSUPPORTED are not available in previous
+ * versions of the library. To avoid creating non-existing enum,
+ * [UnsupportedComposeAnimation] should only be instantiated if [ComposeAnimationType] API
+ * for UNSUPPORTED enum is available.
+ */
+ var apiAvailable = enumValues<ComposeAnimationType>().any { it.name == "UNSUPPORTED" }
+ private set
+
+ fun create(label: String?) =
+ if (apiAvailable) UnsupportedComposeAnimation(label) else null
+
+ /** This method is for testing only. */
+ @TestOnly
+ fun testOverrideAvailability(override: Boolean) {
+ apiAvailable = override
+ }
+ }
}
/**
diff --git a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/PreviewAnimationClock.kt b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/PreviewAnimationClock.kt
index 4329d4f..a33b646 100644
--- a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/PreviewAnimationClock.kt
+++ b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/animation/PreviewAnimationClock.kt
@@ -119,6 +119,7 @@
private val lock = Any()
fun trackAnimation(animation: T, label: String) {
+ if (!UnsupportedComposeAnimation.apiAvailable) return
synchronized(lock) {
if (animations.contains(animation)) {
if (DEBUG) {
@@ -133,9 +134,10 @@
Log.d(TAG, "Animation $animation is now tracked")
}
- val composeAnimation = UnsupportedComposeAnimation(label)
- trackedUnsupported.add(composeAnimation)
- notifySubscribe(composeAnimation)
+ UnsupportedComposeAnimation.create(label)?.let {
+ trackedUnsupported.add(it)
+ notifySubscribe(it)
+ }
}
fun clear() {
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index d01baf1..13f51f0 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -46,7 +46,7 @@
api(project(":compose:ui:ui-graphics"))
api(project(":compose:ui:ui-text"))
api(project(":compose:ui:ui-unit"))
- api(project(":annotation:annotation"))
+ api("androidx.annotation:annotation:1.5.0-alpha01")
// This has stub APIs for access to legacy Android APIs, so we don't want
// any dependency on this module.
diff --git a/compose/ui/ui/src/androidMain/res/values-af/strings.xml b/compose/ui/ui/src/androidMain/res/values-af/strings.xml
index bec1e20..9307353 100644
--- a/compose/ui/ui/src/androidMain/res/values-af/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-af/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Gedeeltelik gemerk"</string>
<string name="on" msgid="8655164131929253426">"Aan"</string>
<string name="off" msgid="875452955155264703">"Af"</string>
<string name="selected" msgid="6043586758067023">"Gekies"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-am/strings.xml b/compose/ui/ui/src/androidMain/res/values-am/strings.xml
index d4e13c5..afabf32 100644
--- a/compose/ui/ui/src/androidMain/res/values-am/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-am/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"በከፊል የተፈተሸ"</string>
<string name="on" msgid="8655164131929253426">"በርቷል"</string>
<string name="off" msgid="875452955155264703">"ጠፍቷል"</string>
<string name="selected" msgid="6043586758067023">"ተመርጧል"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ar/strings.xml b/compose/ui/ui/src/androidMain/res/values-ar/strings.xml
index e8e446d..0170f4f 100644
--- a/compose/ui/ui/src/androidMain/res/values-ar/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ar/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"تم وضع علامة في المربّع بشكل جزئي"</string>
<string name="on" msgid="8655164131929253426">"مفعّل"</string>
<string name="off" msgid="875452955155264703">"غير مفعّل"</string>
<string name="selected" msgid="6043586758067023">"محدّد"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-as/strings.xml b/compose/ui/ui/src/androidMain/res/values-as/strings.xml
index e7bdb96..fd3d3d4 100644
--- a/compose/ui/ui/src/androidMain/res/values-as/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-as/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"আংশিকভাৱে পৰীক্ষা কৰা হৈছে"</string>
<string name="on" msgid="8655164131929253426">"অন কৰা আছে"</string>
<string name="off" msgid="875452955155264703">"অফ আছে"</string>
<string name="selected" msgid="6043586758067023">"বাছনি কৰা হৈছে"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-b+sr+Latn/strings.xml b/compose/ui/ui/src/androidMain/res/values-b+sr+Latn/strings.xml
index 24ce253..61a85cc 100644
--- a/compose/ui/ui/src/androidMain/res/values-b+sr+Latn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-b+sr+Latn/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Delimično označeno"</string>
<string name="on" msgid="8655164131929253426">"Uključeno"</string>
<string name="off" msgid="875452955155264703">"Isključeno"</string>
<string name="selected" msgid="6043586758067023">"Izabrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-be/strings.xml b/compose/ui/ui/src/androidMain/res/values-be/strings.xml
index d8ba140..98e1f8c 100644
--- a/compose/ui/ui/src/androidMain/res/values-be/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-be/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Выбрана часткова"</string>
<string name="on" msgid="8655164131929253426">"Уключана"</string>
<string name="off" msgid="875452955155264703">"Выключана"</string>
<string name="selected" msgid="6043586758067023">"Выбрана"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-bg/strings.xml b/compose/ui/ui/src/androidMain/res/values-bg/strings.xml
index 4d8841d..67e38ca 100644
--- a/compose/ui/ui/src/androidMain/res/values-bg/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-bg/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Частично отметнато"</string>
<string name="on" msgid="8655164131929253426">"Вкл."</string>
<string name="off" msgid="875452955155264703">"Изкл."</string>
<string name="selected" msgid="6043586758067023">"Избрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-bn/strings.xml b/compose/ui/ui/src/androidMain/res/values-bn/strings.xml
index 1ba969f..dffc528 100644
--- a/compose/ui/ui/src/androidMain/res/values-bn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-bn/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"কয়েকটি বিকল্প বেছে নেওয়া হয়েছে"</string>
<string name="on" msgid="8655164131929253426">"চালু আছে"</string>
<string name="off" msgid="875452955155264703">"বন্ধ আছে"</string>
<string name="selected" msgid="6043586758067023">"বেছে নেওয়া হয়েছে"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-bs/strings.xml b/compose/ui/ui/src/androidMain/res/values-bs/strings.xml
index f940559..9f847eb 100644
--- a/compose/ui/ui/src/androidMain/res/values-bs/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-bs/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Djelimično označeno"</string>
<string name="on" msgid="8655164131929253426">"Uključeno"</string>
<string name="off" msgid="875452955155264703">"Isključeno"</string>
<string name="selected" msgid="6043586758067023">"Odabrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ca/strings.xml b/compose/ui/ui/src/androidMain/res/values-ca/strings.xml
index 4d001db..2971e2d 100644
--- a/compose/ui/ui/src/androidMain/res/values-ca/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ca/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Marcat parcialment"</string>
<string name="on" msgid="8655164131929253426">"Activat"</string>
<string name="off" msgid="875452955155264703">"Desactivat"</string>
<string name="selected" msgid="6043586758067023">"Seleccionat"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-cs/strings.xml b/compose/ui/ui/src/androidMain/res/values-cs/strings.xml
index d02f4f8..4b7be76 100644
--- a/compose/ui/ui/src/androidMain/res/values-cs/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-cs/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Částečně zaškrtnuto"</string>
<string name="on" msgid="8655164131929253426">"Zapnuto"</string>
<string name="off" msgid="875452955155264703">"Vypnuto"</string>
<string name="selected" msgid="6043586758067023">"Vybráno"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-da/strings.xml b/compose/ui/ui/src/androidMain/res/values-da/strings.xml
index f9d0a98..74d814d 100644
--- a/compose/ui/ui/src/androidMain/res/values-da/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-da/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Delvist markeret"</string>
<string name="on" msgid="8655164131929253426">"Til"</string>
<string name="off" msgid="875452955155264703">"Fra"</string>
<string name="selected" msgid="6043586758067023">"Valgt"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-de/strings.xml b/compose/ui/ui/src/androidMain/res/values-de/strings.xml
index 7a0e339..756ecac 100644
--- a/compose/ui/ui/src/androidMain/res/values-de/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-de/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Teilweise aktiviert"</string>
<string name="on" msgid="8655164131929253426">"An"</string>
<string name="off" msgid="875452955155264703">"Aus"</string>
<string name="selected" msgid="6043586758067023">"Ausgewählt"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-es-rUS/strings.xml b/compose/ui/ui/src/androidMain/res/values-es-rUS/strings.xml
index 2d63515..d310d00 100644
--- a/compose/ui/ui/src/androidMain/res/values-es-rUS/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-es-rUS/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Verificado parcialmente"</string>
<string name="on" msgid="8655164131929253426">"Sí"</string>
<string name="off" msgid="875452955155264703">"No"</string>
<string name="selected" msgid="6043586758067023">"Seleccionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-es/strings.xml b/compose/ui/ui/src/androidMain/res/values-es/strings.xml
index 5dfa6df..29f0179 100644
--- a/compose/ui/ui/src/androidMain/res/values-es/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-es/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Marcado parcialmente"</string>
<string name="on" msgid="8655164131929253426">"Activado"</string>
<string name="off" msgid="875452955155264703">"Desactivado"</string>
<string name="selected" msgid="6043586758067023">"Seleccionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-et/strings.xml b/compose/ui/ui/src/androidMain/res/values-et/strings.xml
index 6855b96..2dca078 100644
--- a/compose/ui/ui/src/androidMain/res/values-et/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-et/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Osaliselt märgitud"</string>
<string name="on" msgid="8655164131929253426">"Sees"</string>
<string name="off" msgid="875452955155264703">"Väljas"</string>
<string name="selected" msgid="6043586758067023">"Valitud"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-eu/strings.xml b/compose/ui/ui/src/androidMain/res/values-eu/strings.xml
index 6858d16..55be3eb 100644
--- a/compose/ui/ui/src/androidMain/res/values-eu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-eu/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Erdi-markatuta"</string>
<string name="on" msgid="8655164131929253426">"Aktibatuta"</string>
<string name="off" msgid="875452955155264703">"Desaktibatuta"</string>
<string name="selected" msgid="6043586758067023">"Hautatuta"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-fa/strings.xml b/compose/ui/ui/src/androidMain/res/values-fa/strings.xml
index a670069..3f782d5 100644
--- a/compose/ui/ui/src/androidMain/res/values-fa/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fa/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"برخی موارد علامتگذاری شده است"</string>
<string name="on" msgid="8655164131929253426">"روشن"</string>
<string name="off" msgid="875452955155264703">"خاموش"</string>
<string name="selected" msgid="6043586758067023">"انتخاب شد"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-fi/strings.xml b/compose/ui/ui/src/androidMain/res/values-fi/strings.xml
index a2946a5..636843a 100644
--- a/compose/ui/ui/src/androidMain/res/values-fi/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fi/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Osittain tarkistettu"</string>
<string name="on" msgid="8655164131929253426">"Päällä"</string>
<string name="off" msgid="875452955155264703">"Pois"</string>
<string name="selected" msgid="6043586758067023">"Valittu"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-fr-rCA/strings.xml b/compose/ui/ui/src/androidMain/res/values-fr-rCA/strings.xml
index c52e9d5..9fbbc12 100644
--- a/compose/ui/ui/src/androidMain/res/values-fr-rCA/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fr-rCA/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Partiellement vérifié"</string>
<string name="on" msgid="8655164131929253426">"Activé"</string>
<string name="off" msgid="875452955155264703">"Désactivé"</string>
<string name="selected" msgid="6043586758067023">"Sélectionné"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-fr/strings.xml b/compose/ui/ui/src/androidMain/res/values-fr/strings.xml
index de53dd7..83442c7 100644
--- a/compose/ui/ui/src/androidMain/res/values-fr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fr/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Partiellement coché"</string>
<string name="on" msgid="8655164131929253426">"Activé"</string>
<string name="off" msgid="875452955155264703">"Désactivé"</string>
<string name="selected" msgid="6043586758067023">"Sélectionné"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-gl/strings.xml b/compose/ui/ui/src/androidMain/res/values-gl/strings.xml
index 795a358..393fe3c 100644
--- a/compose/ui/ui/src/androidMain/res/values-gl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-gl/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Marcada parcialmente"</string>
<string name="on" msgid="8655164131929253426">"Activado"</string>
<string name="off" msgid="875452955155264703">"Desactivado"</string>
<string name="selected" msgid="6043586758067023">"Seleccionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-gu/strings.xml b/compose/ui/ui/src/androidMain/res/values-gu/strings.xml
index c9bb052..bf68b3e 100644
--- a/compose/ui/ui/src/androidMain/res/values-gu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-gu/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"આંશિક રીતે ચેક કરેલા વિકલ્પો"</string>
<string name="on" msgid="8655164131929253426">"ચાલુ છે"</string>
<string name="off" msgid="875452955155264703">"બંધ છે"</string>
<string name="selected" msgid="6043586758067023">"પસંદગી કરી"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-hi/strings.xml b/compose/ui/ui/src/androidMain/res/values-hi/strings.xml
index 5d7d0b2..a9abd97 100644
--- a/compose/ui/ui/src/androidMain/res/values-hi/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hi/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"कुछ विकल्पों को चुना गया"</string>
<string name="on" msgid="8655164131929253426">"चालू है"</string>
<string name="off" msgid="875452955155264703">"बंद है"</string>
<string name="selected" msgid="6043586758067023">"चुना गया"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-hr/strings.xml b/compose/ui/ui/src/androidMain/res/values-hr/strings.xml
index 82e083c..89cb2a9 100644
--- a/compose/ui/ui/src/androidMain/res/values-hr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hr/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Djelomično potvrđeno"</string>
<string name="on" msgid="8655164131929253426">"Uključeno"</string>
<string name="off" msgid="875452955155264703">"Isključeno"</string>
<string name="selected" msgid="6043586758067023">"Odabrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-hu/strings.xml b/compose/ui/ui/src/androidMain/res/values-hu/strings.xml
index bc13636..2747ec8 100644
--- a/compose/ui/ui/src/androidMain/res/values-hu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hu/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Részlegesen ellenőrizve"</string>
<string name="on" msgid="8655164131929253426">"Be"</string>
<string name="off" msgid="875452955155264703">"Ki"</string>
<string name="selected" msgid="6043586758067023">"Kijelölve"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-hy/strings.xml b/compose/ui/ui/src/androidMain/res/values-hy/strings.xml
index cdc836c..5e57c00 100644
--- a/compose/ui/ui/src/androidMain/res/values-hy/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hy/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Մասնակի նշված"</string>
<string name="on" msgid="8655164131929253426">"Միացված է"</string>
<string name="off" msgid="875452955155264703">"Անջատված է"</string>
<string name="selected" msgid="6043586758067023">"Ընտրված է"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-in/strings.xml b/compose/ui/ui/src/androidMain/res/values-in/strings.xml
index a47444c..47357b8 100644
--- a/compose/ui/ui/src/androidMain/res/values-in/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-in/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Diperiksa sebagian"</string>
<string name="on" msgid="8655164131929253426">"Aktif"</string>
<string name="off" msgid="875452955155264703">"Nonaktif"</string>
<string name="selected" msgid="6043586758067023">"Dipilih"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-is/strings.xml b/compose/ui/ui/src/androidMain/res/values-is/strings.xml
index b7f7b05..e46b177 100644
--- a/compose/ui/ui/src/androidMain/res/values-is/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-is/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Hakað við að hluta til"</string>
<string name="on" msgid="8655164131929253426">"Kveikt"</string>
<string name="off" msgid="875452955155264703">"Slökkt"</string>
<string name="selected" msgid="6043586758067023">"Valið"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-it/strings.xml b/compose/ui/ui/src/androidMain/res/values-it/strings.xml
index 414acd8..0cd3192 100644
--- a/compose/ui/ui/src/androidMain/res/values-it/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-it/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Elemento parzialmente selezionato"</string>
<string name="on" msgid="8655164131929253426">"On"</string>
<string name="off" msgid="875452955155264703">"Off"</string>
<string name="selected" msgid="6043586758067023">"Elemento selezionato"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-iw/strings.xml b/compose/ui/ui/src/androidMain/res/values-iw/strings.xml
index 2cdab54..a2b4851 100644
--- a/compose/ui/ui/src/androidMain/res/values-iw/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-iw/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"מסומנת חלקית"</string>
<string name="on" msgid="8655164131929253426">"פועל"</string>
<string name="off" msgid="875452955155264703">"כבוי"</string>
<string name="selected" msgid="6043586758067023">"נבחר"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ja/strings.xml b/compose/ui/ui/src/androidMain/res/values-ja/strings.xml
index 24c7609..be60e61 100644
--- a/compose/ui/ui/src/androidMain/res/values-ja/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ja/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"一部 ON"</string>
<string name="on" msgid="8655164131929253426">"オン"</string>
<string name="off" msgid="875452955155264703">"オフ"</string>
<string name="selected" msgid="6043586758067023">"選択済み"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ka/strings.xml b/compose/ui/ui/src/androidMain/res/values-ka/strings.xml
index 7875bad..5eb8939 100644
--- a/compose/ui/ui/src/androidMain/res/values-ka/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ka/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"ნაწილობრივ შემოწმებულია"</string>
<string name="on" msgid="8655164131929253426">"ჩართული"</string>
<string name="off" msgid="875452955155264703">"გამორთული"</string>
<string name="selected" msgid="6043586758067023">"არჩეული"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-kk/strings.xml b/compose/ui/ui/src/androidMain/res/values-kk/strings.xml
index f30b035..82a029b 100644
--- a/compose/ui/ui/src/androidMain/res/values-kk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-kk/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Жартылай белгіленді."</string>
<string name="on" msgid="8655164131929253426">"Қосулы"</string>
<string name="off" msgid="875452955155264703">"Өшірулі"</string>
<string name="selected" msgid="6043586758067023">"Таңдалды"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-km/strings.xml b/compose/ui/ui/src/androidMain/res/values-km/strings.xml
index 58b175d..8e580fc 100644
--- a/compose/ui/ui/src/androidMain/res/values-km/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-km/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"បានធីកខ្លះ"</string>
<string name="on" msgid="8655164131929253426">"បើក"</string>
<string name="off" msgid="875452955155264703">"បិទ"</string>
<string name="selected" msgid="6043586758067023">"បានជ្រើសរើស"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-kn/strings.xml b/compose/ui/ui/src/androidMain/res/values-kn/strings.xml
index 8850e24..0b655e7 100644
--- a/compose/ui/ui/src/androidMain/res/values-kn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-kn/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"ಭಾಗಶಃ ಪರೀಕ್ಷಿಸಲಾಗಿದೆ"</string>
<string name="on" msgid="8655164131929253426">"ಆನ್ ಆಗಿದೆ"</string>
<string name="off" msgid="875452955155264703">"ಆಫ್ ಆಗಿದೆ"</string>
<string name="selected" msgid="6043586758067023">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ko/strings.xml b/compose/ui/ui/src/androidMain/res/values-ko/strings.xml
index 875fec1..bc50254 100644
--- a/compose/ui/ui/src/androidMain/res/values-ko/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ko/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"일부 선택됨"</string>
<string name="on" msgid="8655164131929253426">"켜짐"</string>
<string name="off" msgid="875452955155264703">"꺼짐"</string>
<string name="selected" msgid="6043586758067023">"선택됨"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ky/strings.xml b/compose/ui/ui/src/androidMain/res/values-ky/strings.xml
index 739a6b3..9cde977 100644
--- a/compose/ui/ui/src/androidMain/res/values-ky/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ky/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Жарым-жартылай текшерилди"</string>
<string name="on" msgid="8655164131929253426">"Күйүк"</string>
<string name="off" msgid="875452955155264703">"Өчүк"</string>
<string name="selected" msgid="6043586758067023">"Тандалды"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-lo/strings.xml b/compose/ui/ui/src/androidMain/res/values-lo/strings.xml
index 0935bf6..79b9834 100644
--- a/compose/ui/ui/src/androidMain/res/values-lo/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-lo/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"ກວດສອບບາງສ່ວນແລ້ວ"</string>
<string name="on" msgid="8655164131929253426">"ເປີດ"</string>
<string name="off" msgid="875452955155264703">"ປິດ"</string>
<string name="selected" msgid="6043586758067023">"ເລືອກແລ້ວ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-lt/strings.xml b/compose/ui/ui/src/androidMain/res/values-lt/strings.xml
index 1e0f248..3e28091 100644
--- a/compose/ui/ui/src/androidMain/res/values-lt/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-lt/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Iš dalies pažymėta"</string>
<string name="on" msgid="8655164131929253426">"Įjungta"</string>
<string name="off" msgid="875452955155264703">"Išjungta"</string>
<string name="selected" msgid="6043586758067023">"Pasirinkta"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-lv/strings.xml b/compose/ui/ui/src/androidMain/res/values-lv/strings.xml
index 8c533528..3b92218 100644
--- a/compose/ui/ui/src/androidMain/res/values-lv/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-lv/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Daļēji atzīmēta"</string>
<string name="on" msgid="8655164131929253426">"Ieslēgts"</string>
<string name="off" msgid="875452955155264703">"Izslēgts"</string>
<string name="selected" msgid="6043586758067023">"Atlasīts"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-mk/strings.xml b/compose/ui/ui/src/androidMain/res/values-mk/strings.xml
index 0c5ed6e..79eb9b2 100644
--- a/compose/ui/ui/src/androidMain/res/values-mk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-mk/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Делумно проверено"</string>
<string name="on" msgid="8655164131929253426">"Вклучено"</string>
<string name="off" msgid="875452955155264703">"Исклучено"</string>
<string name="selected" msgid="6043586758067023">"Избрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ml/strings.xml b/compose/ui/ui/src/androidMain/res/values-ml/strings.xml
index f4d88e8..0ad1342 100644
--- a/compose/ui/ui/src/androidMain/res/values-ml/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ml/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"ഭാഗികമായി ചെക്ക് മാർക്കിട്ടു"</string>
<string name="on" msgid="8655164131929253426">"ഓണാണ്"</string>
<string name="off" msgid="875452955155264703">"ഓഫാണ്"</string>
<string name="selected" msgid="6043586758067023">"തിരഞ്ഞെടുത്തു"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-mn/strings.xml b/compose/ui/ui/src/androidMain/res/values-mn/strings.xml
index ca6f341..7908393 100644
--- a/compose/ui/ui/src/androidMain/res/values-mn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-mn/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Хэсэгчлэн тэмдэглэсэн"</string>
<string name="on" msgid="8655164131929253426">"Асаалттай"</string>
<string name="off" msgid="875452955155264703">"Унтраалттай"</string>
<string name="selected" msgid="6043586758067023">"Сонгосон"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-mr/strings.xml b/compose/ui/ui/src/androidMain/res/values-mr/strings.xml
index 73cf75d..8075612 100644
--- a/compose/ui/ui/src/androidMain/res/values-mr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-mr/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"अंशतः तपासले"</string>
<string name="on" msgid="8655164131929253426">"सुरू आहे"</string>
<string name="off" msgid="875452955155264703">"बंद आहे"</string>
<string name="selected" msgid="6043586758067023">"निवडला"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ms/strings.xml b/compose/ui/ui/src/androidMain/res/values-ms/strings.xml
index 7a99bb2..2106240 100644
--- a/compose/ui/ui/src/androidMain/res/values-ms/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ms/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Sebahagiannya ditandai"</string>
<string name="on" msgid="8655164131929253426">"Hidup"</string>
<string name="off" msgid="875452955155264703">"Mati"</string>
<string name="selected" msgid="6043586758067023">"Dipilih"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-my/strings.xml b/compose/ui/ui/src/androidMain/res/values-my/strings.xml
index ba757fb..378fdec 100644
--- a/compose/ui/ui/src/androidMain/res/values-my/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-my/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"တစ်ဝက်တစ်ပျက် စစ်ဆေးထားသည်"</string>
<string name="on" msgid="8655164131929253426">"ဖွင့်"</string>
<string name="off" msgid="875452955155264703">"ပိတ်"</string>
<string name="selected" msgid="6043586758067023">"ရွေးထားသည်"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-nb/strings.xml b/compose/ui/ui/src/androidMain/res/values-nb/strings.xml
index 6c5bc69..635894a0 100644
--- a/compose/ui/ui/src/androidMain/res/values-nb/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-nb/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Delvis avmerket"</string>
<string name="on" msgid="8655164131929253426">"På"</string>
<string name="off" msgid="875452955155264703">"Av"</string>
<string name="selected" msgid="6043586758067023">"Valgt"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ne/strings.xml b/compose/ui/ui/src/androidMain/res/values-ne/strings.xml
index cb332e7..1cda72d 100644
--- a/compose/ui/ui/src/androidMain/res/values-ne/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ne/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"आंशिक रूपमा जाँच गरिएको"</string>
<string name="on" msgid="8655164131929253426">"अन छ"</string>
<string name="off" msgid="875452955155264703">"अफ छ"</string>
<string name="selected" msgid="6043586758067023">"चयन गरिएको छ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-nl/strings.xml b/compose/ui/ui/src/androidMain/res/values-nl/strings.xml
index d1428af..3ebc974 100644
--- a/compose/ui/ui/src/androidMain/res/values-nl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-nl/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Gedeeltelijk aangevinkt"</string>
<string name="on" msgid="8655164131929253426">"Aan"</string>
<string name="off" msgid="875452955155264703">"Uit"</string>
<string name="selected" msgid="6043586758067023">"Geselecteerd"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-or/strings.xml b/compose/ui/ui/src/androidMain/res/values-or/strings.xml
index 0d82f58..39ddb0d 100644
--- a/compose/ui/ui/src/androidMain/res/values-or/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-or/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"ଆଶିଂକ ଭାବେ ଯାଞ୍ଚ କରାଯାଇଛି"</string>
<string name="on" msgid="8655164131929253426">"ଚାଲୁ ଅଛି"</string>
<string name="off" msgid="875452955155264703">"ବନ୍ଦ ଅଛି"</string>
<string name="selected" msgid="6043586758067023">"ଚୟନିତ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pa/strings.xml b/compose/ui/ui/src/androidMain/res/values-pa/strings.xml
index c1e3b7d..6e7256a 100644
--- a/compose/ui/ui/src/androidMain/res/values-pa/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pa/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"ਅੰਸ਼ਕ ਤੌਰ ਤੇ ਜਾਂਚ ਕੀਤੀ ਗਈ"</string>
<string name="on" msgid="8655164131929253426">"ਚਾਲੂ ਹੈ"</string>
<string name="off" msgid="875452955155264703">"ਬੰਦ ਹੈ"</string>
<string name="selected" msgid="6043586758067023">"ਚੁਣੀ ਗਈ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pl/strings.xml b/compose/ui/ui/src/androidMain/res/values-pl/strings.xml
index 75aa49e..4167c3e 100644
--- a/compose/ui/ui/src/androidMain/res/values-pl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pl/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Częściowo zaznaczone"</string>
<string name="on" msgid="8655164131929253426">"Włączono"</string>
<string name="off" msgid="875452955155264703">"Wyłączono"</string>
<string name="selected" msgid="6043586758067023">"Wybrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pt-rBR/strings.xml b/compose/ui/ui/src/androidMain/res/values-pt-rBR/strings.xml
index d0607fd..2b2737c 100644
--- a/compose/ui/ui/src/androidMain/res/values-pt-rBR/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pt-rBR/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Parcialmente selecionada"</string>
<string name="on" msgid="8655164131929253426">"Ativado"</string>
<string name="off" msgid="875452955155264703">"Desativado"</string>
<string name="selected" msgid="6043586758067023">"Selecionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pt-rPT/strings.xml b/compose/ui/ui/src/androidMain/res/values-pt-rPT/strings.xml
index 14c7c04..d27d78d 100644
--- a/compose/ui/ui/src/androidMain/res/values-pt-rPT/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pt-rPT/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Parcialmente selecionada"</string>
<string name="on" msgid="8655164131929253426">"Ativado"</string>
<string name="off" msgid="875452955155264703">"Desativado"</string>
<string name="selected" msgid="6043586758067023">"Selecionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-pt/strings.xml b/compose/ui/ui/src/androidMain/res/values-pt/strings.xml
index d0607fd..2b2737c 100644
--- a/compose/ui/ui/src/androidMain/res/values-pt/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pt/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Parcialmente selecionada"</string>
<string name="on" msgid="8655164131929253426">"Ativado"</string>
<string name="off" msgid="875452955155264703">"Desativado"</string>
<string name="selected" msgid="6043586758067023">"Selecionado"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ro/strings.xml b/compose/ui/ui/src/androidMain/res/values-ro/strings.xml
index 38108f8..6e8e9d6 100644
--- a/compose/ui/ui/src/androidMain/res/values-ro/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ro/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Bifată parțial"</string>
<string name="on" msgid="8655164131929253426">"Activat"</string>
<string name="off" msgid="875452955155264703">"Dezactivat"</string>
<string name="selected" msgid="6043586758067023">"Selectat"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ru/strings.xml b/compose/ui/ui/src/androidMain/res/values-ru/strings.xml
index 1928e3f..0e23ca7 100644
--- a/compose/ui/ui/src/androidMain/res/values-ru/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ru/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Отмечено частично"</string>
<string name="on" msgid="8655164131929253426">"Включено"</string>
<string name="off" msgid="875452955155264703">"Отключено"</string>
<string name="selected" msgid="6043586758067023">"Выбрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-si/strings.xml b/compose/ui/ui/src/androidMain/res/values-si/strings.xml
index 13ffe02..1cbdac1 100644
--- a/compose/ui/ui/src/androidMain/res/values-si/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-si/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"අඩ වශයෙන් ලකුණු කළ"</string>
<string name="on" msgid="8655164131929253426">"ක්රියාත්මකයි"</string>
<string name="off" msgid="875452955155264703">"ක්රියාවිරහිතයි"</string>
<string name="selected" msgid="6043586758067023">"තෝරන ලදි"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sk/strings.xml b/compose/ui/ui/src/androidMain/res/values-sk/strings.xml
index c068e63..ebef855 100644
--- a/compose/ui/ui/src/androidMain/res/values-sk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sk/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Čiastočne začiarknuté"</string>
<string name="on" msgid="8655164131929253426">"Zapnuté"</string>
<string name="off" msgid="875452955155264703">"Vypnuté"</string>
<string name="selected" msgid="6043586758067023">"Vybrané"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sl/strings.xml b/compose/ui/ui/src/androidMain/res/values-sl/strings.xml
index 067183a..72bd314 100644
--- a/compose/ui/ui/src/androidMain/res/values-sl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sl/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Delno potrjeno"</string>
<string name="on" msgid="8655164131929253426">"Vklopljeno"</string>
<string name="off" msgid="875452955155264703">"Izklopljeno"</string>
<string name="selected" msgid="6043586758067023">"Izbrano"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sq/strings.xml b/compose/ui/ui/src/androidMain/res/values-sq/strings.xml
index 0f3cf23..7abc00b 100644
--- a/compose/ui/ui/src/androidMain/res/values-sq/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sq/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Pjesërisht e shënuar"</string>
<string name="on" msgid="8655164131929253426">"Aktiv"</string>
<string name="off" msgid="875452955155264703">"Joaktiv"</string>
<string name="selected" msgid="6043586758067023">"Zgjedhur"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sr/strings.xml b/compose/ui/ui/src/androidMain/res/values-sr/strings.xml
index f761f7e..71ab0b4 100644
--- a/compose/ui/ui/src/androidMain/res/values-sr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sr/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Делимично означено"</string>
<string name="on" msgid="8655164131929253426">"Укључено"</string>
<string name="off" msgid="875452955155264703">"Искључено"</string>
<string name="selected" msgid="6043586758067023">"Изабрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sv/strings.xml b/compose/ui/ui/src/androidMain/res/values-sv/strings.xml
index 8843eba..775deb3 100644
--- a/compose/ui/ui/src/androidMain/res/values-sv/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sv/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Delvis markerad"</string>
<string name="on" msgid="8655164131929253426">"På"</string>
<string name="off" msgid="875452955155264703">"Av"</string>
<string name="selected" msgid="6043586758067023">"Valt"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-sw/strings.xml b/compose/ui/ui/src/androidMain/res/values-sw/strings.xml
index 9a960fe..6e72ed8 100644
--- a/compose/ui/ui/src/androidMain/res/values-sw/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sw/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Imekaguliwa kwa kiasi fulani"</string>
<string name="on" msgid="8655164131929253426">"Imewashwa"</string>
<string name="off" msgid="875452955155264703">"Imezimwa"</string>
<string name="selected" msgid="6043586758067023">"Umechagua"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ta/strings.xml b/compose/ui/ui/src/androidMain/res/values-ta/strings.xml
index b8e6df2..b5af40e 100644
--- a/compose/ui/ui/src/androidMain/res/values-ta/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ta/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"சில மட்டுமே தேர்ந்தெடுக்கப்பட்டுள்ளன"</string>
<string name="on" msgid="8655164131929253426">"இயக்கப்பட்டுள்ளது"</string>
<string name="off" msgid="875452955155264703">"முடக்கப்பட்டுள்ளது"</string>
<string name="selected" msgid="6043586758067023">"தேர்ந்தெடுக்கப்பட்டுள்ளது"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-te/strings.xml b/compose/ui/ui/src/androidMain/res/values-te/strings.xml
index 7f1588e..c7a73ea 100644
--- a/compose/ui/ui/src/androidMain/res/values-te/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-te/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"పాక్షికంగా ఎంచుకోబడింది"</string>
<string name="on" msgid="8655164131929253426">"ఆన్లో ఉంది"</string>
<string name="off" msgid="875452955155264703">"ఆఫ్లో ఉంది"</string>
<string name="selected" msgid="6043586758067023">"ఎంచుకోబడింది"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-th/strings.xml b/compose/ui/ui/src/androidMain/res/values-th/strings.xml
index 3d9e8cb..9ed9c01 100644
--- a/compose/ui/ui/src/androidMain/res/values-th/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-th/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"เลือกบางส่วน"</string>
<string name="on" msgid="8655164131929253426">"เปิด"</string>
<string name="off" msgid="875452955155264703">"ปิด"</string>
<string name="selected" msgid="6043586758067023">"เลือกไว้"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-tl/strings.xml b/compose/ui/ui/src/androidMain/res/values-tl/strings.xml
index 489df2b..f270d0f 100644
--- a/compose/ui/ui/src/androidMain/res/values-tl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-tl/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Bahagyang may check"</string>
<string name="on" msgid="8655164131929253426">"Naka-on"</string>
<string name="off" msgid="875452955155264703">"Naka-off"</string>
<string name="selected" msgid="6043586758067023">"Napili"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-tr/strings.xml b/compose/ui/ui/src/androidMain/res/values-tr/strings.xml
index d51a068..047a5e7 100644
--- a/compose/ui/ui/src/androidMain/res/values-tr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-tr/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Kısmi olarak kontrol edildi"</string>
<string name="on" msgid="8655164131929253426">"Açık"</string>
<string name="off" msgid="875452955155264703">"Kapalı"</string>
<string name="selected" msgid="6043586758067023">"Seçili"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-uk/strings.xml b/compose/ui/ui/src/androidMain/res/values-uk/strings.xml
index d7656ee..86ac2d9 100644
--- a/compose/ui/ui/src/androidMain/res/values-uk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-uk/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Частково вибрано"</string>
<string name="on" msgid="8655164131929253426">"Увімкнено"</string>
<string name="off" msgid="875452955155264703">"Вимкнено"</string>
<string name="selected" msgid="6043586758067023">"Вибрано"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-ur/strings.xml b/compose/ui/ui/src/androidMain/res/values-ur/strings.xml
index fcb05a7..29c81e4 100644
--- a/compose/ui/ui/src/androidMain/res/values-ur/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ur/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"جزوی طور پر چیک کردہ"</string>
<string name="on" msgid="8655164131929253426">"آن ہے"</string>
<string name="off" msgid="875452955155264703">"آف ہے"</string>
<string name="selected" msgid="6043586758067023">"منتخب کردہ"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-uz/strings.xml b/compose/ui/ui/src/androidMain/res/values-uz/strings.xml
index 2ec8ad47..de15793 100644
--- a/compose/ui/ui/src/androidMain/res/values-uz/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-uz/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Qisman belgilandi"</string>
<string name="on" msgid="8655164131929253426">"Yoniq"</string>
<string name="off" msgid="875452955155264703">"Oʻchiq"</string>
<string name="selected" msgid="6043586758067023">"Tanlangan"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-vi/strings.xml b/compose/ui/ui/src/androidMain/res/values-vi/strings.xml
index a95de5d..84a7a82 100644
--- a/compose/ui/ui/src/androidMain/res/values-vi/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-vi/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Đã kiểm tra một phần"</string>
<string name="on" msgid="8655164131929253426">"Đang bật"</string>
<string name="off" msgid="875452955155264703">"Đang tắt"</string>
<string name="selected" msgid="6043586758067023">"Đã chọn"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-zh-rCN/strings.xml b/compose/ui/ui/src/androidMain/res/values-zh-rCN/strings.xml
index 64da085..ced331e 100644
--- a/compose/ui/ui/src/androidMain/res/values-zh-rCN/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-zh-rCN/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"部分选中"</string>
<string name="on" msgid="8655164131929253426">"已开启"</string>
<string name="off" msgid="875452955155264703">"已关闭"</string>
<string name="selected" msgid="6043586758067023">"已选择"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-zh-rHK/strings.xml b/compose/ui/ui/src/androidMain/res/values-zh-rHK/strings.xml
index f3153ed..53784a3 100644
--- a/compose/ui/ui/src/androidMain/res/values-zh-rHK/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-zh-rHK/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"部分剔咗"</string>
<string name="on" msgid="8655164131929253426">"開"</string>
<string name="off" msgid="875452955155264703">"閂"</string>
<string name="selected" msgid="6043586758067023">"揀咗"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-zh-rTW/strings.xml b/compose/ui/ui/src/androidMain/res/values-zh-rTW/strings.xml
index 4b3e173..cd21630 100644
--- a/compose/ui/ui/src/androidMain/res/values-zh-rTW/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-zh-rTW/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"部分檢查"</string>
<string name="on" msgid="8655164131929253426">"已開啟"</string>
<string name="off" msgid="875452955155264703">"已關閉"</string>
<string name="selected" msgid="6043586758067023">"已選取"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-zu/strings.xml b/compose/ui/ui/src/androidMain/res/values-zu/strings.xml
index 0394f97..afe39be 100644
--- a/compose/ui/ui/src/androidMain/res/values-zu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-zu/strings.xml
@@ -17,8 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for indeterminate (7933458017204019916) -->
- <skip />
+ <string name="indeterminate" msgid="7933458017204019916">"Kuhlolwe kancane"</string>
<string name="on" msgid="8655164131929253426">"Vuliwe"</string>
<string name="off" msgid="875452955155264703">"Valiwe"</string>
<string name="selected" msgid="6043586758067023">"Okukhethiwe"</string>
diff --git a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/SimpleActor.kt b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/SimpleActor.kt
index 8da6bf5..7894e59 100644
--- a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/SimpleActor.kt
+++ b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/SimpleActor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
diff --git a/datastore/datastore-multiprocess/build.gradle b/datastore/datastore-multiprocess/build.gradle
index 30b35a7..3d340db 100644
--- a/datastore/datastore-multiprocess/build.gradle
+++ b/datastore/datastore-multiprocess/build.gradle
@@ -19,6 +19,8 @@
plugins {
id("AndroidXPlugin")
id("com.android.library")
+ id("com.google.protobuf")
+ id("java-test-fixtures")
id("org.jetbrains.kotlin.android")
}
@@ -30,15 +32,36 @@
androidTestImplementation(libs.junit)
androidTestImplementation(libs.kotlinCoroutinesTest)
+ androidTestImplementation(libs.protobufLite)
androidTestImplementation(libs.truth)
androidTestImplementation(project(":internal-testutils-truth"))
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.testCore)
androidTestImplementation(project(":datastore:datastore-core"))
- androidTestImplementation(project(":datastore:datastore-proto"))
+}
+
+protobuf {
+ protoc {
+ artifact = libs.protobufCompiler.get()
+ }
+ // Generates the java proto-lite code for the protos in this project. See
+ // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
+ // for more information.
+ generateProtoTasks {
+ all().each { task ->
+ task.builtins {
+ java {
+ option "lite"
+ }
+ }
+ }
+ }
}
android {
+ defaultConfig {
+ minSdkVersion 19
+ }
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
diff --git a/datastore/datastore-multiprocess/src/androidTest/AndroidManifest.xml b/datastore/datastore-multiprocess/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..6b9bbc2
--- /dev/null
+++ b/datastore/datastore-multiprocess/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <application>
+ <service
+ android:name="androidx.datastore.multiprocess.MultiProcessDataStoreMultiProcessTest$SimpleUpdateService"
+ android:enabled="true"
+ android:exported="false"
+ android:process=":SimpleUpdateService" />
+ <service
+ android:name="androidx.datastore.multiprocess.MultiProcessDataStoreMultiProcessTest$ConcurrentReadUpdateWriterService"
+ android:enabled="true"
+ android:exported="false"
+ android:process=":ConcurrentReadUpdateWriterService" />
+ <service
+ android:name="androidx.datastore.multiprocess.MultiProcessDataStoreMultiProcessTest$ConcurrentReadUpdateReaderService"
+ android:enabled="true"
+ android:exported="false"
+ android:process=":ConcurrentReadUpdateReaderService" />
+ <service
+ android:name="androidx.datastore.multiprocess.MultiProcessDataStoreMultiProcessTest$InterleavedUpdateDataService"
+ android:enabled="true"
+ android:exported="false"
+ android:process=":InterleavedUpdateDataService" />
+ <service
+ android:name="androidx.datastore.multiprocess.MultiProcessDataStoreMultiProcessTest$InterleavedUpdateDataWithReadService"
+ android:enabled="true"
+ android:exported="false"
+ android:process=":InterleavedUpdateDataWithReadService" />
+ <service
+ android:name="androidx.datastore.multiprocess.MultiProcessDataStoreMultiProcessTest$CancelledUpdateDataService"
+ android:enabled="true"
+ android:exported="false"
+ android:process=":CancelledUpdateDataService" />
+ <service
+ android:name="androidx.datastore.multiprocess.MultiProcessDataStoreMultiProcessTest$InterleavedHandlerUpdateDataService"
+ android:enabled="true"
+ android:exported="false"
+ android:process=":InterleavedHandlerUpdateDataService" />
+ </application>
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+</manifest>
\ No newline at end of file
diff --git a/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/DirectTestService.kt b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/DirectTestService.kt
new file mode 100644
index 0000000..733f9eb
--- /dev/null
+++ b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/DirectTestService.kt
@@ -0,0 +1,279 @@
+/*
+ * 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.datastore.multiprocess
+
+import android.app.Service
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.os.Message
+import android.os.Messenger
+import android.os.RemoteException
+import androidx.testing.TestMessageProto.FooProto
+import com.google.common.collect.ImmutableList
+import java.io.Serializable
+import java.util.concurrent.CountDownLatch
+
+private val THROWABLE_BUNDLE_KEY: String = "throwable"
+
+internal class Latch {
+ private var signaled: Boolean = false
+
+ @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+ fun signal() {
+ synchronized(this) {
+ signaled = true
+ (this as java.lang.Object).notify()
+ }
+ }
+
+ @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+ fun awaitSignal() {
+ synchronized(this) {
+ while (!signaled) {
+ try {
+ (this as java.lang.Object).wait()
+ } catch (_: InterruptedException) {
+ // Ignore.
+ }
+ }
+ signaled = false
+ }
+ }
+}
+
+abstract class DirectTestService() : Service() {
+ private val SERVICE_CLASS = this::class.java
+
+ @Suppress("DEPRECATION")
+ private val messenger: Messenger = Messenger(Handler(HandlerCallback()))
+ private val resume: Latch = Latch()
+ private val done: Latch = Latch()
+ private lateinit var testData: Bundle
+ private lateinit var thread: Thread
+ private lateinit var testFailure: Throwable
+
+ // It should be setup in `beforeTest`
+ internal lateinit var store: MultiProcessDataStore<FooProto>
+
+ override fun onBind(intent: Intent): IBinder {
+ return messenger.getBinder()
+ }
+
+ override fun onCreate() {
+ // No-op
+ }
+
+ override fun onDestroy() {
+ // No-op
+ }
+
+ protected abstract fun beforeTest(testData: Bundle)
+
+ protected abstract fun runTest()
+
+ protected fun waitForSignal() {
+ done.signal()
+ resume.awaitSignal()
+ }
+
+ private fun handle(msg: Message) {
+ if (!this::thread.isInitialized) {
+ testData = msg.getData()
+ thread = Thread(Runner())
+ thread.start()
+ } else {
+ resume.signal()
+ }
+ done.awaitSignal()
+ try {
+ val response: Message = Message.obtain()
+ if (this::testFailure.isInitialized) {
+ val data = Bundle()
+ data.putSerializable(THROWABLE_BUNDLE_KEY, testFailure as Serializable)
+ response.setData(data)
+ }
+ msg.replyTo.send(response)
+ } catch (ex: RemoteException) {
+ throw RuntimeException("Test service failed to ack message", ex)
+ }
+ }
+
+ private inner class HandlerCallback : Handler.Callback {
+ override fun handleMessage(msg: Message): Boolean {
+ handle(msg)
+ return true
+ }
+ }
+
+ private inner class Runner : Runnable {
+ override fun run() {
+ try {
+ beforeTest(testData)
+ waitForSignal()
+ runTest()
+ } catch (t: Throwable) {
+ testFailure = t
+ } finally {
+ done.signal()
+ }
+ }
+ }
+}
+
+class BlockingServiceConnection(
+ private val context: Context,
+ private val serviceIntent: Intent
+) :
+ ServiceConnection {
+ private lateinit var isConnected: CountDownLatch
+ private var service: Messenger? = null
+ private var remoteException: Throwable? = null
+
+ override fun onServiceConnected(className: ComponentName, serviceBinder: IBinder) {
+ service = Messenger(serviceBinder)
+ isConnected.countDown()
+ }
+
+ override fun onServiceDisconnected(className: ComponentName) {
+ service = null
+ }
+
+ fun isServiceConnected(): Boolean {
+ return service != null
+ }
+
+ @Suppress("DEPRECATION")
+ fun connect(connectedLatch: CountDownLatch) {
+ isConnected = connectedLatch
+ val serviceExists: Boolean =
+ context.bindService(serviceIntent, this, Context.BIND_AUTO_CREATE)
+
+ if (!serviceExists) {
+ val targetPackage: String = serviceIntent.getComponent()!!.getPackageName()
+ val targetService: String = serviceIntent.getComponent()!!.getClassName()
+
+ try {
+ context.getPackageManager().getPackageInfo(targetPackage, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ throw IllegalStateException("Package not installed [$targetPackage]", e)
+ }
+ throw IllegalStateException("Package installed but service not found [$targetService]")
+ }
+ }
+
+ fun signal(msgData: Bundle?, isDelivered: CountDownLatch) {
+ val blockingHandler: BlockingHandler = BlockingHandler(Looper.getMainLooper(), isDelivered)
+ val msg: Message = Message.obtain()
+ msg.replyTo = Messenger(blockingHandler)
+ msgData?.let { msg.setData(it) }
+ try {
+ service?.send(msg)
+ } catch (e: RemoteException) {
+ throw RuntimeException("Remote service failed", e)
+ }
+ }
+
+ fun propagateRemoteExceptionIfPresent() {
+ if (remoteException != null) {
+ throw RuntimeException(remoteException!!)
+ }
+ }
+
+ private inner class BlockingHandler constructor(
+ private val looper: Looper,
+ private val isDelivered: CountDownLatch
+ ) :
+ Handler(looper) {
+
+ @Suppress("DEPRECATION")
+ override fun handleMessage(msg: Message) {
+ remoteException = msg.getData().getSerializable(THROWABLE_BUNDLE_KEY) as Throwable?
+ isDelivered.countDown()
+ }
+ }
+}
+
+internal fun setUpService(
+ context: Context,
+ service: Class<out Service>,
+ testData: Bundle
+): BlockingServiceConnection {
+ val serviceIntent = Intent(context, service)
+ return setUpServicesInternal(context, ImmutableList.of(serviceIntent), testData)[0]
+}
+
+internal fun setUpServices(
+ context: Context,
+ services: List<Class<out Service>>,
+ testData: Bundle
+): List<BlockingServiceConnection> {
+ val serviceIntents: MutableList<Intent> = ArrayList()
+ for (service in services) {
+ serviceIntents.add(Intent(context, service))
+ }
+ return setUpServicesInternal(context, serviceIntents, testData)
+}
+
+private fun setUpServicesInternal(
+ context: Context,
+ serviceIntents: List<Intent>,
+ testData: Bundle
+): List<BlockingServiceConnection> {
+ val connections: MutableList<BlockingServiceConnection> = ArrayList()
+ for (serviceIntent in serviceIntents) {
+ val connection = BlockingServiceConnection(context, serviceIntent)
+ connections.add(connection)
+ }
+ val connectLatch = CountDownLatch(connections.size)
+ for (connection in connections) {
+ connection.connect(connectLatch)
+ }
+ connectLatch.await()
+
+ // Send initial test data
+ val signalLatch = CountDownLatch(connections.size)
+ for (connection in connections) {
+ connection.signal(testData, signalLatch)
+ }
+ signalLatch.await()
+ for (connection in connections) {
+ connection.propagateRemoteExceptionIfPresent()
+ }
+ return connections
+}
+
+internal fun signalService(connection: BlockingServiceConnection) {
+ return signalServices(ImmutableList.of(connection))
+}
+
+internal fun signalServices(connections: List<BlockingServiceConnection>) {
+ val latch = CountDownLatch(connections.size)
+ for (connection in connections) {
+ connection.signal( /* msgData= */null, latch)
+ }
+ latch.await()
+ for (connection in connections) {
+ connection.propagateRemoteExceptionIfPresent()
+ }
+}
\ No newline at end of file
diff --git a/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/MultiProcessDataStoreMultiProcessTest.kt b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/MultiProcessDataStoreMultiProcessTest.kt
new file mode 100644
index 0000000..f7fbb83
--- /dev/null
+++ b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/MultiProcessDataStoreMultiProcessTest.kt
@@ -0,0 +1,478 @@
+/*
+ * 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.datastore.multiprocess
+
+import android.content.Context
+import android.os.Bundle
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.Serializer
+import androidx.datastore.multiprocess.handlers.NoOpCorruptionHandler
+import androidx.test.core.app.ApplicationProvider
+import androidx.testing.TestMessageProto.FooProto
+import com.google.common.truth.Truth.assertThat
+import com.google.protobuf.ExtensionRegistryLite
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.OutputStreamWriter
+import kotlin.jvm.Throws
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.newSingleThreadContext
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private const val PATH_BUNDLE_KEY: String = "path"
+private val PROTO_SERIALIZER: Serializer<FooProto> = ProtoSerializer<FooProto>(
+ FooProto.getDefaultInstance(),
+ ExtensionRegistryLite.getEmptyRegistry()
+)
+private const val TEST_TEXT: String = "abc"
+internal val WRITE_TEXT: (FooProto) -> FooProto = { f: FooProto ->
+ f.toBuilder().setText(TEST_TEXT).build()
+}
+private val WRITE_BOOLEAN: (FooProto) -> FooProto = { f: FooProto ->
+ f.toBuilder().setBoolean(true).build()
+}
+private val INCREMENT_INTEGER: (FooProto) -> FooProto = { f: FooProto ->
+ f.toBuilder().setInteger(f.getInteger() + 1).build()
+}
+
+private val DEFAULT_FOO: FooProto = FooProto.getDefaultInstance()
+private val FOO_WITH_TEXT: FooProto =
+ FooProto.newBuilder().setText(TEST_TEXT).build()
+private val FOO_WITH_TEXT_AND_BOOLEAN: FooProto =
+ FooProto.newBuilder().setText(TEST_TEXT).setBoolean(true).build()
+
+@ExperimentalCoroutinesApi
+private fun createDataStore(
+ bundle: Bundle,
+ scope: TestScope,
+ corruptionHandler: CorruptionHandler<FooProto> = NoOpCorruptionHandler<FooProto>()
+): MultiProcessDataStore<FooProto> {
+ return MultiProcessDataStore<FooProto>(
+ { File(bundle.getString(PATH_BUNDLE_KEY)!!) },
+ PROTO_SERIALIZER,
+ scope = scope,
+ corruptionHandler = corruptionHandler
+ )
+}
+
+@OptIn(DelicateCoroutinesApi::class)
+@ExperimentalCoroutinesApi
+@RunWith(JUnit4::class)
+class MultiProcessDataStoreMultiProcessTest {
+ @get:Rule
+ val tempFolder = TemporaryFolder()
+
+ private lateinit var testFile: File
+ private lateinit var dataStoreScope: TestScope
+
+ private val TAG = "MPDS test"
+
+ private val protoSerializer: Serializer<FooProto> = ProtoSerializer<FooProto>(
+ FooProto.getDefaultInstance(),
+ ExtensionRegistryLite.getEmptyRegistry()
+ )
+ private val mainContext: Context = ApplicationProvider.getApplicationContext()
+
+ private fun createDataStoreBundle(path: String): Bundle {
+ val data = Bundle()
+ data.putString(PATH_BUNDLE_KEY, path)
+ return data
+ }
+
+ internal fun createDataStore(
+ bundle: Bundle,
+ scope: TestScope
+ ): MultiProcessDataStore<FooProto> {
+ return MultiProcessDataStore<FooProto>(
+ { File(bundle.getString(PATH_BUNDLE_KEY)!!) },
+ protoSerializer,
+ scope = scope
+ )
+ }
+
+ @Before
+ fun setUp() {
+ testFile = tempFolder.newFile()
+ dataStoreScope = TestScope(UnconfinedTestDispatcher() + Job())
+ }
+
+ @Test
+ fun testSimpleUpdateData() = runTest {
+ val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
+ val dataStore: MultiProcessDataStore<FooProto> =
+ createDataStore(testData, dataStoreScope)
+ val connection: BlockingServiceConnection =
+ setUpService(mainContext, SimpleUpdateService::class.java, testData)
+
+ assertThat(dataStore.data.first()).isEqualTo(DEFAULT_FOO)
+
+ // Other proc commits TEST_TEXT update
+ signalService(connection)
+
+ assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+ }
+
+ class SimpleUpdateService(
+ private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
+ ) : DirectTestService() {
+ override fun beforeTest(testData: Bundle) {
+ store = createDataStore(testData, scope)
+ }
+
+ override fun runTest() = runBlocking<Unit> {
+ store.updateData {
+ it.let { WRITE_TEXT(it) }
+ }
+ }
+ }
+
+ @Test
+ fun testConcurrentReadUpdate() = runTest {
+ val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
+ val dataStore: MultiProcessDataStore<FooProto> =
+ createDataStore(testData, dataStoreScope)
+ val writerConnection: BlockingServiceConnection =
+ setUpService(mainContext, ConcurrentReadUpdateWriterService::class.java, testData)
+
+ // Start with TEST_TEXT
+ dataStore.updateData { f: FooProto -> WRITE_TEXT(f) }
+ assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+
+ // Writer process starts (but does not yet commit) "true"
+ signalService(writerConnection)
+
+ // We can continue reading datastore while the writer process is mid-write
+ assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+
+ // New processes that start in the meantime can also read
+ val readerConnection: BlockingServiceConnection =
+ setUpService(mainContext, ConcurrentReadUpdateReaderService::class.java, testData)
+ signalService(readerConnection)
+
+ // The other process finishes writing "true"; we (and other readers) should pick up the new data
+ signalService(writerConnection)
+
+ assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
+ signalService(readerConnection)
+ }
+
+ class ConcurrentReadUpdateWriterService(
+ private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
+ ) : DirectTestService() {
+ override fun beforeTest(testData: Bundle) {
+ store = createDataStore(testData, scope)
+ }
+
+ override fun runTest() = runBlocking<Unit> {
+ store.updateData {
+ waitForSignal()
+ it.let { WRITE_BOOLEAN(it) }
+ }
+ }
+ }
+
+ class ConcurrentReadUpdateReaderService(
+ private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
+ ) : DirectTestService() {
+ override fun beforeTest(testData: Bundle) {
+ store = createDataStore(testData, scope)
+ }
+
+ override fun runTest() = runBlocking<Unit> {
+ assertThat(store.data.first()).isEqualTo(FOO_WITH_TEXT)
+ waitForSignal()
+ assertThat(store.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
+ }
+ }
+
+ @Test
+ fun testInterleavedUpdateData() = runTest(UnconfinedTestDispatcher()) {
+ val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
+ val dataStore: MultiProcessDataStore<FooProto> =
+ createDataStore(testData, dataStoreScope)
+ val connection: BlockingServiceConnection =
+ setUpService(mainContext, InterleavedUpdateDataService::class.java, testData)
+
+ // Other proc starts TEST_TEXT update, then waits for signal
+ signalService(connection)
+
+ // We start "true" update, then wait for condition
+ val condition = CompletableDeferred<Unit>()
+ val write = async(newSingleThreadContext("blockedWriter")) {
+ dataStore.updateData {
+ condition.await()
+ it.let { WRITE_BOOLEAN(it) }
+ }
+ }
+
+ // Allow the other proc's update to run to completion, then allow ours to run to completion
+ val unblockOurUpdate = async {
+ delay(100)
+ signalService(connection)
+ condition.complete(Unit)
+ }
+
+ unblockOurUpdate.await()
+ write.await()
+
+ assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
+ }
+
+ class InterleavedUpdateDataService(
+ private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
+ ) : DirectTestService() {
+ override fun beforeTest(testData: Bundle) {
+ store = createDataStore(testData, scope)
+ }
+
+ override fun runTest() = runBlocking<Unit> {
+ store.updateData {
+ waitForSignal()
+ it.let { WRITE_TEXT(it) }
+ }
+ }
+ }
+
+ @Test
+ fun testInterleavedUpdateDataWithLocalRead() = runTest(UnconfinedTestDispatcher()) {
+ val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
+ val dataStore: MultiProcessDataStore<FooProto> =
+ createDataStore(testData, dataStoreScope)
+ val connection: BlockingServiceConnection =
+ setUpService(mainContext, InterleavedUpdateDataWithReadService::class.java, testData)
+
+ // Invalidate any local cache
+ assertThat(dataStore.data.first()).isEqualTo(DEFAULT_FOO)
+ signalService(connection)
+
+ // Queue and start local write
+ val writeStarted = CompletableDeferred<Unit>()
+ val finishWrite = CompletableDeferred<Unit>()
+
+ val write = async {
+ dataStore.updateData {
+ writeStarted.complete(Unit)
+ finishWrite.await()
+ FOO_WITH_TEXT
+ }
+ }
+ writeStarted.await()
+
+ // Queue remote write
+ signalService(connection)
+
+ // Local uncached read; this should see data initially written remotely.
+ assertThat(dataStore.data.first()).isEqualTo(FooProto.newBuilder().setInteger(1).build())
+
+ // Unblock writes; the local write is delayed to ensure the remote write remains blocked.
+ val remoteWrite = async(newSingleThreadContext("blockedWriter")) {
+ signalService(connection)
+ }
+
+ val localWrite = async(newSingleThreadContext("unblockLocalWrite")) {
+ delay(500)
+ finishWrite.complete(Unit)
+ write.await()
+ }
+
+ localWrite.await()
+ remoteWrite.await()
+
+ assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT_AND_BOOLEAN)
+ }
+
+ class InterleavedUpdateDataWithReadService(
+ private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
+ ) : DirectTestService() {
+ override fun beforeTest(testData: Bundle) {
+ store = createDataStore(testData, scope)
+ }
+
+ override fun runTest() = runBlocking<Unit> {
+ store.updateData {
+ it.let { INCREMENT_INTEGER(it) }
+ }
+
+ waitForSignal()
+
+ val write = async {
+ store.updateData {
+ it.let { WRITE_BOOLEAN(it) }
+ }
+ }
+ waitForSignal()
+ write.await()
+ }
+ }
+
+ @Test
+ fun testUpdateDataExceptionUnblocksOtherProcessFromWriting() = runTest {
+ val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
+ val dataStore: MultiProcessDataStore<FooProto> =
+ createDataStore(testData, dataStoreScope)
+ val connection: BlockingServiceConnection =
+ setUpService(mainContext, CancelledUpdateDataService::class.java, testData)
+
+ val blockWrite = CompletableDeferred<Unit>()
+ val waitForWrite = CompletableDeferred<Unit>()
+
+ val write = async {
+ try {
+ dataStore.updateData {
+ blockWrite.await()
+ throw IOException("Something went wrong")
+ }
+ } catch (e: IOException) {
+ waitForWrite.complete(Unit)
+ }
+ }
+
+ assertThat(write.isActive).isTrue()
+ assertThat(write.isCompleted).isFalse()
+
+ blockWrite.complete(Unit)
+ waitForWrite.await()
+
+ assertThat(write.isActive).isFalse()
+ assertThat(write.isCompleted).isTrue()
+
+ signalService(connection)
+
+ assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+ }
+
+ class CancelledUpdateDataService(
+ private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
+ ) : DirectTestService() {
+ override fun beforeTest(testData: Bundle) {
+ store = createDataStore(testData, scope)
+ }
+
+ override fun runTest() = runBlocking<Unit> {
+ store.updateData {
+ it.let { WRITE_TEXT(it) }
+ }
+ }
+ }
+
+ @Test
+ fun testUpdateDataCancellationUnblocksOtherProcessFromWriting() = runTest(
+ UnconfinedTestDispatcher()
+ ) {
+ val localScope = TestScope(UnconfinedTestDispatcher() + Job())
+ val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
+ val dataStore: MultiProcessDataStore<FooProto> =
+ createDataStore(testData, localScope)
+ val connection: BlockingServiceConnection =
+ setUpService(mainContext, CancelledUpdateDataService::class.java, testData)
+
+ val blockWrite = CompletableDeferred<Unit>()
+
+ val write = localScope.async {
+ dataStore.updateData {
+ blockWrite.await()
+ it.let { WRITE_BOOLEAN(it) }
+ }
+ }
+
+ assertThat(write.isActive).isTrue()
+ assertThat(write.isCompleted).isFalse()
+
+ // dataStore.updateData cancelled immediately
+ localScope.coroutineContext.cancelChildren()
+
+ assertThat(write.isActive).isFalse()
+ assertThat(write.isCompleted).isTrue()
+
+ signalService(connection)
+
+ // able to read the new value written from the other process
+ assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+ }
+
+ @Test
+ fun testReadUpdateCorrupt() = runTest {
+ FileOutputStream(testFile).use {
+ OutputStreamWriter(it).write("garbage")
+ }
+ val testData: Bundle = createDataStoreBundle(testFile.absolutePath)
+ val connection: BlockingServiceConnection =
+ setUpService(mainContext, InterleavedHandlerUpdateDataService::class.java, testData)
+ val corruptionHandler = ReplaceFileCorruptionHandler<FooProto> {
+ signalService(connection)
+ FOO_WITH_TEXT_AND_BOOLEAN
+ }
+ val dataStore: MultiProcessDataStore<FooProto> =
+ createDataStore(testData, dataStoreScope, corruptionHandler)
+
+ // Other proc starts TEST_TEXT then waits for signal within handler
+ signalService(connection)
+
+ assertThat(dataStore.data.first()).isEqualTo(FOO_WITH_TEXT)
+ }
+
+ class InterleavedHandlerUpdateDataService(
+ private val scope: TestScope = TestScope(UnconfinedTestDispatcher() + Job())
+ ) : DirectTestService() {
+ override fun beforeTest(testData: Bundle) {
+ val corruptionHandler: CorruptionHandler<FooProto> =
+ ReplaceFileCorruptionHandler<FooProto> {
+ waitForSignal()
+ DEFAULT_FOO
+ }
+ store = createDataStore(testData, scope, corruptionHandler)
+ }
+
+ override fun runTest() = runBlocking<Unit> {
+ store.updateData {
+ it.let { WRITE_TEXT(it) }
+ }
+ }
+ }
+
+ /**
+ * A corruption handler that attempts to replace the on-disk data with data from produceNewData.
+ *
+ * TODO(zhiyuanwang): replace with androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
+ */
+ private class ReplaceFileCorruptionHandler<T>(
+ private val produceNewData: (CorruptionException) -> T
+ ) : CorruptionHandler<T> {
+
+ @Throws(IOException::class)
+ override suspend fun handleCorruption(ex: CorruptionException): T {
+ return produceNewData(ex)
+ }
+ }
+}
\ No newline at end of file
diff --git a/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/MultiProcessDataStoreTest.kt b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/MultiProcessDataStoreTest.kt
new file mode 100644
index 0000000..666863e
--- /dev/null
+++ b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/MultiProcessDataStoreTest.kt
@@ -0,0 +1,998 @@
+/*
+ * 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.datastore.multiprocess
+
+import android.os.StrictMode
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.Serializer
+import androidx.datastore.multiprocess.handlers.NoOpCorruptionHandler
+import androidx.test.filters.LargeTest
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+import java.util.concurrent.Executors
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
+import kotlin.coroutines.AbstractCoroutineContextElement
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.newSingleThreadContext
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.withContext
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/**
+ * A testing class based on duplicate from "SingleProcessDataStoreTest" that only tests the features
+ * in a single process use case. More tests are added for StrictMode.
+ */
+@OptIn(DelicateCoroutinesApi::class)
+@ExperimentalCoroutinesApi
+@LargeTest
+@RunWith(JUnit4::class)
+class MultiProcessDataStoreTest {
+ @get:Rule
+ val tempFolder = TemporaryFolder()
+
+ private lateinit var store: DataStore<Byte>
+ private lateinit var testingSerializer: TestingSerializer
+ private lateinit var testFile: File
+ private lateinit var dataStoreScope: TestScope
+
+ private fun newDataStore(
+ file: File = testFile,
+ serializer: Serializer<Byte> = testingSerializer,
+ scope: CoroutineScope = dataStoreScope,
+ initTasksList: List<suspend (api: InitializerApi<Byte>) -> Unit> = listOf(),
+ corruptionHandler: CorruptionHandler<Byte> = NoOpCorruptionHandler<Byte>()
+ ): DataStore<Byte> {
+ return MultiProcessDataStore(
+ { file },
+ serializer = serializer,
+ scope = scope,
+ initTasksList = initTasksList,
+ corruptionHandler = corruptionHandler
+ )
+ }
+
+ @Before
+ fun setUp() {
+ testingSerializer = TestingSerializer()
+ testFile = tempFolder.newFile()
+ dataStoreScope = TestScope(UnconfinedTestDispatcher() + Job())
+ store =
+ MultiProcessDataStore<Byte>(
+ { testFile },
+ testingSerializer,
+ scope = dataStoreScope
+ )
+ }
+
+ @Test
+ fun testReadNewMessage() = runTest {
+ assertThat(store.data.first()).isEqualTo(0)
+ }
+
+ @Test
+ fun testReadWithNewInstance() = runTest {
+ coroutineScope {
+ val newStore = newDataStore(testFile, scope = this)
+ newStore.updateData { 1 }
+ }
+ coroutineScope {
+ val newStore = newDataStore(testFile, scope = this)
+ assertThat(newStore.data.first()).isEqualTo(1)
+ }
+ }
+
+ @Test
+ fun testReadUnreadableFile() = runTest {
+ testFile.setReadable(false)
+ val result = runCatching {
+ store.data.first()
+ }
+
+ assertThat(result.exceptionOrNull()).isInstanceOf(IOException::class.java)
+ assertThat(result.exceptionOrNull()).hasMessageThat().contains("Permission denied")
+ }
+
+ @Test
+ fun testReadAfterTransientBadRead() = runTest {
+ testFile.setReadable(false)
+
+ assertThrows<IOException> { store.data.first() }.hasMessageThat()
+ .contains("Permission denied")
+
+ testFile.setReadable(true)
+ assertThat(store.data.first()).isEqualTo(0)
+ }
+
+ @Test
+ fun testScopeCancelledWithActiveFlow() = runTest {
+ val storeScope = CoroutineScope(Job())
+ val dataStore = newDataStore(scope = storeScope)
+ val collection = async {
+ dataStore.data.take(2).collect {
+ // Do nothing, this will wait on another element which will never arrive
+ }
+ }
+
+ storeScope.cancel()
+ collection.join()
+
+ assertThat(collection.isCompleted).isTrue()
+ assertThat(collection.isActive).isFalse()
+ }
+
+ @Test
+ fun testWriteAndRead() = runTest {
+ store.updateData { 1 }
+ assertThat(store.data.first()).isEqualTo(1)
+ }
+
+ @Test
+ fun testWritesDontBlockReadsInSameProcess() = runTest {
+ val transformStarted = CompletableDeferred<Unit>()
+ val continueTransform = CompletableDeferred<Unit>()
+
+ val slowUpdate = async {
+ store.updateData {
+ transformStarted.complete(Unit)
+ continueTransform.await()
+ it.inc()
+ }
+ }
+ // Wait for the transform to begin.
+ transformStarted.await()
+
+ // Read is not blocked.
+ assertThat(store.data.first()).isEqualTo(0)
+
+ continueTransform.complete(Unit)
+ slowUpdate.await()
+
+ // After update completes, update runs, and read shows new data.
+ assertThat(store.data.first()).isEqualTo(1)
+ }
+
+ @Test
+ fun testWriteMultiple() = runTest {
+ store.updateData { 2 }
+
+ assertThat(store.data.first()).isEqualTo(2)
+
+ store.updateData { it.dec() }
+
+ assertThat(store.data.first()).isEqualTo(1)
+ }
+
+ @Test
+ fun testReadAfterTransientBadWrite() = runTest {
+ val file = tempFolder.newFile()
+ coroutineScope {
+ val store = newDataStore(file, scope = this)
+ store.updateData { 1 }
+ testingSerializer.failingWrite = true
+ assertThrows<IOException> { store.updateData { 2 } }
+ }
+
+ coroutineScope {
+ val newStore = newDataStore(file, scope = this)
+ assertThat(newStore.data.first()).isEqualTo(1)
+ }
+ }
+
+ @Test
+ fun testWriteToNonExistentDir() = runTest {
+ val fileInNonExistentDir =
+ File(tempFolder.newFolder(), "/this/does/not/exist/foo.tst")
+ coroutineScope {
+ val newStore = newDataStore(fileInNonExistentDir, scope = this)
+
+ newStore.updateData { 1 }
+
+ assertThat(newStore.data.first()).isEqualTo(1)
+ }
+
+ coroutineScope {
+ val newStore = newDataStore(fileInNonExistentDir, scope = this)
+ assertThat(newStore.data.first()).isEqualTo(1)
+ }
+ }
+
+ @Test
+ fun testReadFromNonExistentFile() = runTest {
+ val nonExistentFile = tempFolder.newFile()
+ assertThat(nonExistentFile.delete()).isTrue()
+ val newStore = newDataStore(nonExistentFile)
+ assertThat(newStore.data.first()).isEqualTo(0)
+ }
+
+ @Test
+ fun testWriteToDirFails() = runTest {
+ val directoryFile =
+ File(tempFolder.newFolder(), "/this/is/a/directory")
+ directoryFile.mkdirs()
+ assertThat(directoryFile.isDirectory).isTrue()
+
+ val newStore = newDataStore(directoryFile)
+ assertThrows<IOException> { newStore.data.first() }
+ }
+
+ @Test
+ fun testExceptionWhenCreatingFilePropagates() = runTest {
+ var failFileProducer = true
+
+ val fileProducer = {
+ if (failFileProducer) {
+ throw IOException("Exception when producing file")
+ }
+ testFile
+ }
+
+ val newStore = MultiProcessDataStore(
+ fileProducer,
+ serializer = testingSerializer,
+ scope = dataStoreScope,
+ initTasksList = listOf()
+ )
+
+ assertThrows<IOException> { newStore.data.first() }.hasMessageThat().isEqualTo(
+ "Exception when producing file"
+ )
+
+ failFileProducer = false
+
+ assertThat(newStore.data.first()).isEqualTo(0)
+ }
+
+ @Test
+ fun testWriteTransformCancellation() = runTest {
+ val transform = CompletableDeferred<Byte>()
+
+ val write = async { store.updateData { transform.await() } }
+
+ assertThat(write.isCompleted).isFalse()
+
+ transform.cancel()
+
+ assertThrows<CancellationException> { write.await() }
+
+ // Check that the datastore's scope is still active:
+
+ assertThat(store.updateData { it.inc().inc() }).isEqualTo(2)
+ }
+
+ @Test
+ fun testWriteAfterTransientBadRead() = runTest {
+ testingSerializer.failingRead = true
+
+ assertThrows<IOException> { store.data.first() }
+
+ testingSerializer.failingRead = false
+
+ store.updateData { 1 }
+ assertThat(store.data.first()).isEqualTo(1)
+ }
+
+ @Test
+ fun testWriteWithBadReadFails() = runTest {
+ testingSerializer.failingRead = true
+
+ assertThrows<IOException> { store.updateData { 1 } }
+ }
+
+ @Test
+ fun testCancellingDataStoreScopePropagatesToWrites() = runBlocking<Unit> {
+ val scope = CoroutineScope(Job())
+
+ val dataStore = newDataStore(scope = scope)
+
+ val latch = CompletableDeferred<Unit>()
+
+ val slowUpdate = async {
+ dataStore.updateData {
+ latch.await()
+ it.inc()
+ }
+ }
+
+ val notStartedUpdate = async {
+ dataStore.updateData {
+ it.inc()
+ }
+ }
+
+ scope.cancel()
+
+ assertThrows<CancellationException> { slowUpdate.await() }
+
+ assertThrows<CancellationException> { notStartedUpdate.await() }
+
+ assertThrows<CancellationException> { dataStore.updateData { 123 } }
+ }
+
+ @Test
+ fun testCancellingCallerScopePropagatesToWrites() = runBlocking<Unit> {
+ val dsScope = CoroutineScope(Job())
+ val callerScope = CoroutineScope(Job())
+
+ val dataStore = newDataStore(scope = dsScope)
+
+ val latch = CompletableDeferred<Unit>()
+
+ // The ordering of the following are not guaranteed but I think they won't be flaky with
+ // Dispatchers.Unconfined
+ val awaitingCancellation = callerScope.async(Dispatchers.Unconfined) {
+ dataStore.updateData { awaitCancellation() }
+ }
+
+ dsScope.launch(Dispatchers.Unconfined) {
+ dataStore.updateData {
+ latch.await()
+ it.inc()
+ }
+ }
+
+ val notStarted = callerScope.async(Dispatchers.Unconfined) {
+ dataStore.updateData { it.inc() }
+ }
+
+ callerScope.coroutineContext.job.cancelAndJoin()
+
+ assertThat(awaitingCancellation.isCancelled).isTrue()
+ assertThat(notStarted.isCancelled).isTrue()
+ }
+
+ @Test
+ fun testCanWriteFromInitTask() = runTest {
+ store = newDataStore(initTasksList = listOf { api -> api.updateData { 1 } })
+
+ assertThat(store.data.first()).isEqualTo(1)
+ }
+
+ @Test
+ fun testInitTaskFailsFirstTimeDueToReadFail() = runTest {
+ store = newDataStore(initTasksList = listOf { api -> api.updateData { 1 } })
+
+ testingSerializer.failingRead = true
+ assertThrows<IOException> { store.updateData { 2 } }
+
+ testingSerializer.failingRead = false
+ store.updateData { it.inc().inc() }
+
+ assertThat(store.data.first()).isEqualTo(3)
+ }
+
+ @Test
+ fun testInitTaskFailsFirstTimeDueToException() = runTest {
+ val failInit = AtomicBoolean(true)
+ store = newDataStore(
+ initTasksList = listOf { _ ->
+ if (failInit.get()) {
+ throw IOException("I was asked to fail init")
+ }
+ }
+ )
+ assertThrows<IOException> { store.updateData { 5 } }
+
+ failInit.set(false)
+
+ store.updateData { it.inc() }
+ assertThat(store.data.first()).isEqualTo(1)
+ }
+
+ @Test
+ fun testInitTaskOnlyRunsOnce() = runTest {
+ val count = AtomicInteger()
+ val newStore = newDataStore(
+ testFile,
+ initTasksList = listOf { _ ->
+ count.incrementAndGet()
+ }
+ )
+
+ repeat(10) {
+ newStore.updateData { it.inc() }
+ newStore.data.first()
+ }
+
+ assertThat(count.get()).isEqualTo(1)
+ }
+
+ @Test
+ fun testWriteDuringInit() = runTest {
+ val continueInit = CompletableDeferred<Unit>()
+
+ store = newDataStore(
+ initTasksList = listOf { api ->
+ continueInit.await()
+ api.updateData { 1 }
+ }
+ )
+
+ val update = async {
+ store.updateData { b ->
+ assertThat(b).isEqualTo(1)
+ b
+ }
+ }
+
+ continueInit.complete(Unit)
+ update.await()
+
+ assertThat(store.data.first()).isEqualTo(1)
+ }
+
+ @Test
+ fun testCancelDuringInit() = runTest {
+ val continueInit = CompletableDeferred<Unit>()
+
+ store = newDataStore(
+ initTasksList = listOf { api ->
+ continueInit.await()
+ api.updateData { 1 }
+ }
+ )
+
+ val update = async {
+ store.updateData { it }
+ }
+
+ val read = async {
+ store.data.first()
+ }
+
+ update.cancel()
+ read.cancel()
+ continueInit.complete(Unit)
+
+ assertThrows<CancellationException> { update.await() }
+ assertThrows<CancellationException> { read.await() }
+
+ store.updateData { it.inc().inc() }
+
+ assertThat(store.data.first()).isEqualTo(3)
+ }
+
+ @Test
+ fun testConcurrentUpdatesInit() = runTest {
+ val continueUpdate = CompletableDeferred<Unit>()
+
+ val concurrentUpdateInitializer: suspend (InitializerApi<Byte>) -> Unit = { api ->
+ val update1 = async {
+ api.updateData {
+ continueUpdate.await()
+ it.inc().inc()
+ }
+ }
+ api.updateData {
+ it.inc()
+ }
+ update1.await()
+ }
+
+ store = newDataStore(initTasksList = listOf(concurrentUpdateInitializer))
+ val getData = async { store.data.first() }
+ continueUpdate.complete(Unit)
+
+ assertThat(getData.await()).isEqualTo(3)
+ }
+
+ @Test
+ fun testInitUpdateBlockRead() = runTest {
+ val continueInit = CompletableDeferred<Unit>()
+ val continueUpdate = CompletableDeferred<Unit>()
+
+ val updateInitializer: suspend (InitializerApi<Byte>) -> Unit = { api ->
+ api.updateData {
+ continueInit.await()
+ it.inc()
+ }
+ }
+
+ store = newDataStore(initTasksList = listOf(updateInitializer))
+ val getData = async { store.data.first() }
+ val updateData = async {
+ store.updateData {
+ continueUpdate.await()
+ it.inc()
+ }
+ }
+
+ assertThat(getData.isCompleted).isFalse()
+ assertThat(getData.isActive).isTrue()
+
+ continueInit.complete(Unit)
+ assertThat(getData.await()).isEqualTo(1)
+
+ assertThat(updateData.isCompleted).isFalse()
+ assertThat(updateData.isActive).isTrue()
+
+ continueUpdate.complete(Unit)
+ assertThat(updateData.await()).isEqualTo(2)
+ assertThat(store.data.first()).isEqualTo(2)
+ }
+
+ @Test
+ fun testUpdateSuccessfullyCommittedInit() = runTest {
+ var otherStorage: Byte = 123
+
+ val initializer: suspend (InitializerApi<Byte>) -> Unit = { api ->
+ api.updateData {
+ otherStorage
+ }
+ // Similar to cleanUp():
+ otherStorage = 0
+ }
+
+ val store = newDataStore(initTasksList = listOf(initializer))
+
+ testingSerializer.failingWrite = true
+ assertThrows<IOException> { store.data.first() }
+
+ testingSerializer.failingWrite = false
+ assertThat(store.data.first()).isEqualTo(123)
+ }
+
+ @Test
+ fun testInitApiUpdateThrowsAfterInitTasksComplete() = runTest {
+ var savedApi: InitializerApi<Byte>? = null
+
+ val initializer: suspend (InitializerApi<Byte>) -> Unit = { api ->
+ savedApi = api
+ }
+
+ val store = newDataStore(initTasksList = listOf(initializer))
+
+ assertThat(store.data.first()).isEqualTo(0)
+
+ assertThrows<IllegalStateException> { savedApi?.updateData { 123 } }
+ }
+
+ @Test
+ fun testFlowReceivesUpdates() = runTest {
+ val collectedBytes = mutableListOf<Byte>()
+
+ val flowCollectionJob = async {
+ store.data.take(8).toList(collectedBytes)
+ }
+
+ repeat(7) {
+ store.updateData { it.inc() }
+ }
+
+ flowCollectionJob.join()
+
+ assertThat(collectedBytes).isEqualTo(mutableListOf<Byte>(0, 1, 2, 3, 4, 5, 6, 7))
+ }
+
+ @Test
+ fun testMultipleFlowsReceiveData() = runTest {
+ val flowOf8 = store.data.take(8)
+
+ val bytesFromFirstCollect = mutableListOf<Byte>()
+ val bytesFromSecondCollect = mutableListOf<Byte>()
+
+ val flowCollection1 = async {
+ flowOf8.toList(bytesFromFirstCollect)
+ }
+
+ val flowCollection2 = async {
+ flowOf8.toList(bytesFromSecondCollect)
+ }
+
+ repeat(7) {
+ store.updateData { it.inc() }
+ }
+
+ flowCollection1.join()
+ flowCollection2.join()
+
+ assertThat(bytesFromFirstCollect).isEqualTo(mutableListOf<Byte>(0, 1, 2, 3, 4, 5, 6, 7))
+ assertThat(bytesFromSecondCollect).isEqualTo(mutableListOf<Byte>(0, 1, 2, 3, 4, 5, 6, 7))
+ }
+
+ @Test
+ fun testExceptionInFlowDoesNotBreakUpstream() = runTest {
+ val flowOf8 = store.data.take(8)
+
+ val collectedBytes = mutableListOf<Byte>()
+
+ val failedFlowCollection = async {
+ assertThrows<Exception> {
+ flowOf8.collect {
+ throw Exception("Failure while collecting")
+ }
+ }.hasMessageThat().contains("Failure while collecting")
+ }
+
+ val successfulFlowCollection = async {
+ flowOf8.take(8).toList(collectedBytes)
+ }
+
+ repeat(7) {
+ store.updateData { it.inc() }
+ }
+
+ successfulFlowCollection.join()
+ failedFlowCollection.await()
+
+ assertThat(collectedBytes).isEqualTo(mutableListOf<Byte>(0, 1, 2, 3, 4, 5, 6, 7))
+ }
+
+ @Test
+ fun testSlowConsumerDoesntBlockOtherConsumers() = runTest {
+ val flowOf8 = store.data.take(8)
+
+ val collectedBytes = mutableListOf<Byte>()
+
+ val flowCollection2 = async {
+ flowOf8.toList(collectedBytes)
+ }
+
+ val blockedCollection = async {
+ flowOf8.collect {
+ flowCollection2.await()
+ }
+ }
+
+ repeat(15) {
+ store.updateData { it.inc() }
+ }
+
+ flowCollection2.await()
+ assertThat(collectedBytes).isEqualTo(mutableListOf<Byte>(0, 1, 2, 3, 4, 5, 6, 7))
+
+ blockedCollection.await()
+ }
+
+ @Test
+ fun testHandlerNotCalledGoodData() = runTest {
+ coroutineScope {
+ newDataStore(testFile, scope = this).updateData { 1 }
+ }
+
+ coroutineScope {
+ val testingHandler: TestingCorruptionHandler = TestingCorruptionHandler()
+ val newStore = newDataStore(corruptionHandler = testingHandler, file = testFile)
+
+ newStore.updateData { 2 }
+ newStore.data.first()
+
+ assertThat(testingHandler.numCalls).isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun handlerNotCalledNonCorruption() = runTest {
+ coroutineScope {
+ newDataStore(testFile, scope = this).updateData { 1 }
+ }
+
+ coroutineScope {
+ val testingHandler = TestingCorruptionHandler()
+ testingSerializer.failingRead = true
+ val newStore = newDataStore(corruptionHandler = testingHandler, file = testFile)
+
+ assertThrows<IOException> { newStore.updateData { 2 } }
+ assertThrows<IOException> { newStore.data.first() }
+
+ assertThat(testingHandler.numCalls).isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun testHandlerCalledCorruptDataRead() = runTest {
+ coroutineScope {
+ val newStore = newDataStore(testFile, scope = this)
+ newStore.updateData { 1 } // Pre-seed the data so the file exists.
+ }
+
+ coroutineScope {
+ val testingHandler: TestingCorruptionHandler = TestingCorruptionHandler()
+ testingSerializer.failReadWithCorruptionException = true
+ val newStore = newDataStore(corruptionHandler = testingHandler, file = testFile)
+
+ assertThrows<IOException> { newStore.data.first() }.hasMessageThat().contains(
+ "Handler thrown exception."
+ )
+
+ assertThat(testingHandler.numCalls).isEqualTo(1)
+ }
+ }
+
+ @Test
+ fun testHandlerCalledCorruptDataWrite() = runTest {
+ coroutineScope {
+ val newStore = newDataStore(file = testFile, scope = this)
+ newStore.updateData { 1 }
+ }
+
+ coroutineScope {
+ val testingHandler: TestingCorruptionHandler = TestingCorruptionHandler()
+ testingSerializer.failReadWithCorruptionException = true
+ val newStore = newDataStore(corruptionHandler = testingHandler, file = testFile)
+
+ assertThrows<IOException> { newStore.updateData { 1 } }.hasMessageThat().contains(
+ "Handler thrown exception."
+ )
+
+ assertThat(testingHandler.numCalls).isEqualTo(1)
+ }
+ }
+
+ @Test
+ fun testHandlerReplaceData() = runTest {
+ coroutineScope {
+ newDataStore(file = testFile, scope = this).updateData { 1 }
+ }
+
+ coroutineScope {
+ val testingHandler: TestingCorruptionHandler =
+ TestingCorruptionHandler(replaceWith = 10)
+ testingSerializer.failReadWithCorruptionException = true
+ val newStore = newDataStore(
+ corruptionHandler = testingHandler, file = testFile,
+ scope = this
+ )
+
+ assertThat(newStore.data.first()).isEqualTo(10)
+ }
+ }
+
+ @Test
+ fun testMutatingDataStoreFails() = runTest {
+
+ val dataStore = MultiProcessDataStore(
+ { testFile },
+ ByteWrapper.ByteWrapperSerializer(),
+ scope = dataStoreScope,
+ )
+
+ assertThrows<IllegalStateException> {
+ dataStore.updateData { input: ByteWrapper ->
+ // mutating our wrapper causes us to fail
+ input.byte = 123.toByte()
+ input
+ }
+ }
+ }
+
+ @Test
+ fun testDefaultValueUsedWhenNoDataOnDisk() = runTest {
+ val dataStore = MultiProcessDataStore(
+ { testFile },
+ TestingSerializer(defaultValue = 99),
+ scope = dataStoreScope
+ )
+
+ assertThat(testFile.delete()).isTrue()
+
+ assertThat(dataStore.data.first()).isEqualTo(99)
+ }
+
+ @Test
+ fun testTransformRunInCallersContext() = runBlocking<Unit> {
+ suspend fun getContext(): CoroutineContext {
+ return kotlin.coroutines.coroutineContext
+ }
+
+ withContext(TestElement("123")) {
+ store.updateData {
+ val context = getContext()
+ assertThat(context[TestElement.Key]!!.name).isEqualTo("123")
+ it.inc()
+ }
+ }
+ }
+
+ private class TestElement(
+ val name: String
+ ) : AbstractCoroutineContextElement(Key) {
+ companion object Key : CoroutineContext.Key<TestElement>
+ }
+
+ @Test
+ fun testCancelInflightWrite() = runBlocking<Unit> {
+ val myScope =
+ CoroutineScope(Job() + Executors.newSingleThreadExecutor().asCoroutineDispatcher())
+
+ val updateStarted = CompletableDeferred<Unit>()
+ myScope.launch {
+ store.updateData {
+ updateStarted.complete(Unit)
+ awaitCancellation()
+ }
+ }
+ updateStarted.await()
+ myScope.coroutineContext[Job]!!.cancelAndJoin()
+ }
+
+ @Test
+ fun testWrite_afterCanceledWrite_succeeds() = runBlocking<Unit> {
+ val myScope =
+ CoroutineScope(Job() + Executors.newSingleThreadExecutor().asCoroutineDispatcher())
+
+ val cancelNow = CompletableDeferred<Unit>()
+
+ myScope.launch {
+ store.updateData {
+ cancelNow.complete(Unit)
+ awaitCancellation()
+ }
+ }
+
+ cancelNow.await()
+ myScope.coroutineContext[Job]!!.cancelAndJoin()
+
+ store.updateData { 123 }
+ }
+
+ @Test
+ fun testWrite_fromOtherScope_doesntGetCancelledFromDifferentScope() = runBlocking<Unit> {
+
+ val otherScope = CoroutineScope(Job())
+
+ val callerScope = CoroutineScope(Job())
+
+ val firstUpdateStarted = CompletableDeferred<Unit>()
+ val finishFirstUpdate = CompletableDeferred<Byte>()
+
+ val firstUpdate = otherScope.async(Dispatchers.Unconfined) {
+ store.updateData {
+ firstUpdateStarted.complete(Unit)
+ finishFirstUpdate.await()
+ }
+ }
+
+ callerScope.launch(Dispatchers.Unconfined) {
+ store.updateData {
+ awaitCancellation()
+ }
+ }
+
+ firstUpdateStarted.await()
+ callerScope.coroutineContext.job.cancelAndJoin()
+ finishFirstUpdate.complete(1)
+ firstUpdate.await()
+
+ // It's still usable:
+ assertThat(store.updateData { it.inc() }).isEqualTo(2)
+ }
+
+ @Test
+ fun testCreateDuplicateActiveDataStore() = runTest {
+ val file = tempFolder.newFile()
+ val dataStore = newDataStore(file = file, scope = CoroutineScope(Job()))
+
+ dataStore.data.first()
+
+ val duplicateDataStore = newDataStore(file = file, scope = CoroutineScope(Job()))
+
+ assertThrows<IllegalStateException> {
+ duplicateDataStore.data.first()
+ }
+ }
+
+ @Test
+ fun testCreateDataStore_withSameFileAsInactiveDataStore() = runTest {
+ val file = tempFolder.newFile()
+ val scope1 = CoroutineScope(Job())
+ val dataStore1 = newDataStore(file = file, scope = scope1)
+
+ dataStore1.data.first()
+
+ scope1.coroutineContext.job.cancelAndJoin()
+
+ val dataStore2 = newDataStore(file = file, scope = CoroutineScope(Job()))
+
+ // This shouldn't throw an exception bc the scope1 has been cancelled.
+ dataStore2.data.first()
+ }
+
+ @Test
+ fun testCreateDataStoreAndRead_withStrictMode() = runTest {
+ StrictMode.setThreadPolicy(
+ StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().penaltyDeath()
+ .build()
+ )
+ val dataStore =
+ newDataStore(file = testFile, scope = CoroutineScope(newSingleThreadContext("test")))
+ assertThat(dataStore.data.first()).isEqualTo(0)
+ StrictMode.allowThreadDiskReads()
+ StrictMode.allowThreadDiskWrites()
+ }
+
+ @Test
+ fun testCreateDataStoreAndUpdate_withStrictMode() = runTest {
+ StrictMode.setThreadPolicy(
+ StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().penaltyDeath()
+ .build()
+ )
+ val dataStore =
+ newDataStore(file = testFile, scope = CoroutineScope(newSingleThreadContext("test")))
+ dataStore.updateData { it.inc() }
+ assertThat(dataStore.data.first()).isEqualTo(1)
+ StrictMode.allowThreadDiskReads()
+ StrictMode.allowThreadDiskWrites()
+ }
+
+ // Mutable wrapper around a byte
+ data class ByteWrapper(var byte: Byte) {
+ internal class ByteWrapperSerializer() : Serializer<ByteWrapper> {
+ private val delegate = TestingSerializer()
+
+ override val defaultValue = ByteWrapper(delegate.defaultValue)
+
+ override suspend fun readFrom(input: InputStream): ByteWrapper {
+ return ByteWrapper(delegate.readFrom(input))
+ }
+
+ override suspend fun writeTo(t: ByteWrapper, output: OutputStream) {
+ delegate.writeTo(t.byte, output)
+ }
+ }
+ }
+
+ private class TestingCorruptionHandler(
+ private val replaceWith: Byte? = null
+ ) : CorruptionHandler<Byte> {
+
+ @Volatile
+ var numCalls = 0
+
+ override suspend fun handleCorruption(ex: CorruptionException): Byte {
+ numCalls++
+
+ replaceWith?.let {
+ return it
+ }
+
+ throw IOException("Handler thrown exception.")
+ }
+ }
+}
\ No newline at end of file
diff --git a/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/ProtoSerializer.kt b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/ProtoSerializer.kt
new file mode 100644
index 0000000..3a6f92a
--- /dev/null
+++ b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/ProtoSerializer.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.datastore.multiprocess
+
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.Serializer
+import com.google.protobuf.ExtensionRegistryLite
+import com.google.protobuf.InvalidProtocolBufferException
+import com.google.protobuf.MessageLite
+import java.io.InputStream
+import java.io.OutputStream
+
+/** Serializer for using DataStore with protos. */
+internal class ProtoSerializer<T : MessageLite>(
+ /** The default proto of this type, obtained via {@code T.getDefaultInstance()} */
+ override val defaultValue: T,
+ /**
+ * Set the extensionRegistryLite to use when deserializing T. If no extension registry is
+ * necessary, use {@code ExtensionRegistryLite.getEmptyRegistry()}.
+ */
+ private val extensionRegistryLite: ExtensionRegistryLite
+) : Serializer<T> {
+
+ @Suppress("UNCHECKED_CAST")
+ override suspend fun readFrom(input: InputStream): T {
+ try {
+ return defaultValue.parserForType.parseFrom(input, extensionRegistryLite) as T
+ } catch (invalidProtocolBufferException: InvalidProtocolBufferException) {
+ throw CorruptionException(
+ "Cannot read proto.", invalidProtocolBufferException
+ )
+ }
+ }
+
+ override suspend fun writeTo(t: T, output: OutputStream) {
+ t.writeTo(output)
+ }
+}
\ No newline at end of file
diff --git a/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/TestingSerializer.kt b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/TestingSerializer.kt
new file mode 100644
index 0000000..86d7763
--- /dev/null
+++ b/datastore/datastore-multiprocess/src/androidTest/java/androidx/datastore/multiprocess/TestingSerializer.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.datastore.multiprocess
+
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.Serializer
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+
+internal class TestingSerializer(
+ @Volatile var failReadWithCorruptionException: Boolean = false,
+ @Volatile var failingRead: Boolean = false,
+ @Volatile var failingWrite: Boolean = false,
+ override val defaultValue: Byte = 0
+) : Serializer<Byte> {
+ override suspend fun readFrom(input: InputStream): Byte {
+ if (failReadWithCorruptionException) {
+ throw CorruptionException(
+ "CorruptionException",
+ IOException()
+ )
+ }
+
+ if (failingRead) {
+ throw IOException("I was asked to fail on reads")
+ }
+
+ val read = input.read()
+ if (read == -1) {
+ return 0
+ }
+ return read.toByte()
+ }
+
+ override suspend fun writeTo(t: Byte, output: OutputStream) {
+ if (failingWrite) {
+ throw IOException("I was asked to fail on writes")
+ }
+ output.write(t.toInt())
+ }
+}
\ No newline at end of file
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/AutoTestFrameClock.kt b/datastore/datastore-multiprocess/src/androidTest/proto/test.proto
similarity index 61%
copy from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/AutoTestFrameClock.kt
copy to datastore/datastore-multiprocess/src/androidTest/proto/test.proto
index 19e2105..0ad7b69 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/AutoTestFrameClock.kt
+++ b/datastore/datastore-multiprocess/src/androidTest/proto/test.proto
@@ -14,15 +14,17 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy
+// Protos for use in tests
+syntax = "proto2";
-import androidx.compose.runtime.MonotonicFrameClock
-import java.util.concurrent.atomic.AtomicLong
+package androidx.testing;
-class AutoTestFrameClock : MonotonicFrameClock {
- private val time = AtomicLong(0)
+option java_package = "androidx.testing";
+option java_outer_classname = "TestMessageProto";
- override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R {
- return onFrame(time.getAndAdd(16_000_000))
- }
-}
\ No newline at end of file
+message FooProto {
+ optional string text = 1;
+ optional bool boolean = 2;
+ optional int32 integer = 3;
+ optional bytes bytes = 4;
+}
diff --git a/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/MultiProcessDataStore.kt b/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/MultiProcessDataStore.kt
index 51e4717..07cc6edc 100644
--- a/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/MultiProcessDataStore.kt
+++ b/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/MultiProcessDataStore.kt
@@ -16,6 +16,7 @@
package androidx.datastore.multiprocess
+import android.os.FileObserver
import androidx.annotation.GuardedBy
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.DataStore
@@ -26,6 +27,7 @@
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
+import java.util.concurrent.Semaphore
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext
import kotlinx.coroutines.completeWith
@@ -47,8 +49,6 @@
/**
* Multi process implementation of DataStore. It is multi-process safe.
*/
-// TODO(zhiyuanwang): copied straight from {@link androidx.datastore.core.SingleProcessDataStore},
-// replace with the real multi process implementation
internal class MultiProcessDataStore<T>(
private val produceFile: () -> File,
private val serializer: Serializer<T>,
@@ -63,7 +63,6 @@
private val corruptionHandler: CorruptionHandler<T> = NoOpCorruptionHandler<T>(),
private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
) : DataStore<T> {
-
override val data: Flow<T> = flow {
/**
* If downstream flow is UnInitialized, no data has been read yet, we need to trigger a new
@@ -73,7 +72,9 @@
* data. We need to trigger a new read then start emitting values once we have seen a new
* value (or exception).
*
- * If downstream flow has Data, we should just start emitting from downstream flow.
+ * If downstream flow has Data, we should start emitting from downstream flow as long as its
+ * version is not stale compared to the version read from the shared counter when we enter
+ * the flow.
*
* If Downstream flow is Final, the scope has been cancelled so the data store is no
* longer usable. We should just propagate this exception.
@@ -82,20 +83,25 @@
* Final. ReadException can transition to another ReadException, Data or Final.
* Data can transition to another Data or Final. Final will not change.
*/
-
+ // TODO(b/241290444): avoid coroutine switching by loading native lib during initialization
+ val latestVersionAtRead = withContext(scope.coroutineContext) { sharedCounter.getValue() }
val currentDownStreamFlowState = downstreamFlow.value
- if (currentDownStreamFlowState !is Data) {
- // We need to send a read request because we don't have data yet.
- actor.offer(Message.Read(currentDownStreamFlowState))
+ if ((currentDownStreamFlowState !is Data) ||
+ (currentDownStreamFlowState.version < latestVersionAtRead)
+ ) {
+ // We need to send a read request because we don't have data yet / cached data is stale.
+ readActor.offer(Message.Read(currentDownStreamFlowState))
}
emitAll(
downstreamFlow.dropWhile {
- if (currentDownStreamFlowState is Data<T> ||
- currentDownStreamFlowState is Final<T>
- ) {
- // We don't need to drop any Data or Final values.
+ if (currentDownStreamFlowState is Data<T>) {
+ // we need to drop until initTasks are completed and set to null, and data
+ // version >= the current version when entering flow
+ (it !is Data) || (it.version < latestVersionAtRead)
+ } else if (currentDownStreamFlowState is Final<T>) {
+ // We don't need to drop Final values.
false
} else {
// we need to drop the last seen state since it was either an exception or
@@ -109,9 +115,7 @@
is Final<T> -> throw it.finalException
is Data<T> -> it.value
is UnInitialized -> error(
- "This is a bug in DataStore. Please file a bug at: " +
- "https://issuetracker.google.com/issues/new?" +
- "component=907884&template=1466542"
+ BUG_MESSAGE
)
}
}
@@ -119,33 +123,48 @@
}
override suspend fun updateData(transform: suspend (t: T) -> T): T {
- /**
- * The states here are the same as the states for reads. Additionally we send an ack that
- * the actor *must* respond to (even if it is cancelled).
- */
val ack = CompletableDeferred<T>()
val currentDownStreamFlowState = downstreamFlow.value
val updateMsg =
Message.Update(transform, ack, currentDownStreamFlowState, coroutineContext)
- actor.offer(updateMsg)
+ writeActor.offer(updateMsg)
return ack.await()
}
private val SCRATCH_SUFFIX = ".tmp"
+ private val LOCK_SUFFIX = ".lock"
+ private val VERSION_SUFFIX = ".version"
+ private val BUG_MESSAGE = "This is a bug in DataStore. Please file a bug at: " +
+ "https://issuetracker.google.com/issues/new?component=907884&template=1466542"
+ private val INVALID_VERSION = -1
+ private var initTasks: List<suspend (api: InitializerApi<T>) -> Unit>? =
+ initTasksList.toList()
+ private val sharedCounter: SharedCounter by lazy {
+ SharedCounter.loadLib()
+ // TODO(b/241471375): remove the enableMlock option here
+ SharedCounter.create(/* enableMlock = */ false) {
+ val versionFile = fileWithSuffix(VERSION_SUFFIX)
+ versionFile.createIfNotExists()
+ versionFile
+ }
+ }
+ private val threadLockSemaphore = Semaphore(1)
- private val file: File by lazy {
+ // file is protected rather than private to avoid requiring synthetic accessor for its usage in
+ // the definition of FileObserver
+ protected val file: File by lazy {
val file = produceFile()
file.absolutePath.let {
synchronized(activeFilesLock) {
check(!activeFiles.contains(it)) {
- "There are multiple DataStores active for the same file: $file. You should " +
- "either maintain your DataStore as a singleton or confirm that there is " +
- "no two DataStore's active on the same file (by confirming that the scope" +
- " is cancelled)."
+ "There are multiple DataStores in the same process active for the same file: " +
+ "$file. You should either maintain your DataStore as a singleton or " +
+ "confirm that there is no two DataStore's active on the same file in the " +
+ "same process(by confirming that the scope is cancelled)."
}
activeFiles.add(it)
}
@@ -154,37 +173,29 @@
file
}
+ private val lockFile: File by lazy {
+ val lockFile = fileWithSuffix(LOCK_SUFFIX)
+ lockFile.createIfNotExists()
+ lockFile
+ }
+
+ private val fileObserver: FileObserver by lazy {
+ @Suppress("DEPRECATION")
+ object : FileObserver(file.canonicalFile.parent!!, FileObserver.MOVED_TO) {
+ // It will be triggered by same-process-write as well. Shared memory version check will
+ // prevent it from reading again. parameter `path` is relative to the observed directory
+ override fun onEvent(event: Int, path: String?) {
+ if ((downstreamFlow.value !is Final) && (path!! == file.name)) {
+ readActor.offer(Message.Read(downstreamFlow.value))
+ }
+ }
+ }
+ }
+
@Suppress("UNCHECKED_CAST")
private val downstreamFlow = MutableStateFlow(UnInitialized as State<T>)
- private var initTasks: List<suspend (api: InitializerApi<T>) -> Unit>? =
- initTasksList.toList()
-
- /** The actions for the actor. */
- private sealed class Message<T> {
- abstract val lastState: State<T>?
-
- /**
- * Represents a read operation. If the data is already cached, this is a no-op. If data
- * has not been cached, it triggers a new read to the specified dataChannel.
- */
- class Read<T>(
- override val lastState: State<T>?
- ) : Message<T>()
-
- /** Represents an update operation. */
- class Update<T>(
- val transform: suspend (t: T) -> T,
- /**
- * Used to signal (un)successful completion of the update to the caller.
- */
- val ack: CompletableDeferred<T>,
- override val lastState: State<T>?,
- val callerContext: CoroutineContext
- ) : Message<T>()
- }
-
- private val actor = SimpleActor<Message<T>>(
+ private val writeActor = SimpleActor<Message.Update<T>>(
scope = scope,
onComplete = {
it?.let {
@@ -198,32 +209,31 @@
}
},
onUndeliveredElement = { msg, ex ->
- if (msg is Message.Update) {
- // TODO(rohitsat): should we instead use scope.ensureActive() to get the original
- // cancellation cause? Should we instead have something like
- // UndeliveredElementException?
- msg.ack.completeExceptionally(
- ex ?: CancellationException(
- "DataStore scope was cancelled before updateData could complete"
- )
+ msg.ack.completeExceptionally(
+ ex ?: CancellationException(
+ "DataStore scope was cancelled before updateData could complete"
)
- }
+ )
}
) { msg ->
- when (msg) {
- is Message.Read -> {
- handleRead(msg)
- }
- is Message.Update -> {
- handleUpdate(msg)
- }
- }
+ handleUpdate(msg)
+ }
+
+ private val readActor = SimpleActor<Message.Read<T>>(
+ scope = scope,
+ onComplete = {
+ // no more reads so stop listening to file changes
+ fileObserver.stopWatching()
+ },
+ onUndeliveredElement = { _, _ -> }
+ ) { msg ->
+ handleRead(msg)
}
private suspend fun handleRead(read: Message.Read<T>) {
when (val currentState = downstreamFlow.value) {
is Data -> {
- // We already have data so just return...
+ readData()
}
is ReadException -> {
if (currentState === read.lastState) {
@@ -241,15 +251,13 @@
}
private suspend fun handleUpdate(update: Message.Update<T>) {
- // All branches of this *must* complete ack either successfully or exceptionally.
- // We must *not* throw an exception, just propagate it to the ack.
update.ack.completeWith(
runCatching {
-
+ var result: T
when (val currentState = downstreamFlow.value) {
is Data -> {
// We are already initialized, we just need to perform the update
- transformAndWrite(update.transform, update.callerContext)
+ result = transformAndWrite(update.transform, update.callerContext)
}
is ReadException, is UnInitialized -> {
if (currentState === update.lastState) {
@@ -257,7 +265,7 @@
readAndInitOrPropagateAndThrowFailure()
// We've successfully read, now we need to perform the update
- transformAndWrite(update.transform, update.callerContext)
+ result = transformAndWrite(update.transform, update.callerContext)
} else {
// Someone else beat us to read but also failed. We just need to
// signal the writer that is waiting on ack.
@@ -269,6 +277,7 @@
is Final -> throw currentState.finalException // won't happen
}
+ result
}
)
}
@@ -290,55 +299,94 @@
}
}
+ // It handles the read when data needs to be initialized.
private suspend fun readAndInit() {
- // This should only be called if we don't already have cached data.
- check(downstreamFlow.value == UnInitialized || downstreamFlow.value is ReadException)
+ initTaskLock.withLock() {
+ // This should only be called if we don't already have cached data.
+ if (downstreamFlow.value != UnInitialized && downstreamFlow.value !is ReadException) {
+ // downstreamFlow.value is Data or Final, no need to readAndInit. As there are two
+ // actors, we return here instead of throwing exception to properly handle the race
+ // condition where one actor call `readAndInit()` after the other has completed
+ // successfully.
+ return
+ }
- val updateLock = Mutex()
- var initData = readDataOrHandleCorruption()
+ var initData: Data<T>
+ if ((initTasks == null) || initTasks!!.isEmpty()) {
+ initData = readDataOrHandleCorruption(hasWriteFileLock = false)
+ } else {
+ initData = getWriteFileLock {
+ val updateLock = Mutex()
+ var initializationComplete: Boolean = false
+ var currentData = readDataOrHandleCorruption(hasWriteFileLock = true).value
- var initializationComplete: Boolean = false
+ val api = object : InitializerApi<T> {
+ override suspend fun updateData(transform: suspend (t: T) -> T): T {
+ return updateLock.withLock() {
+ check(!initializationComplete) {
+ "InitializerApi.updateData should not be called after " +
+ "initialization is complete."
+ }
- // TODO(b/151635324): Consider using Context Element to throw an error on re-entrance.
- val api = object : InitializerApi<T> {
- override suspend fun updateData(transform: suspend (t: T) -> T): T {
- return updateLock.withLock() {
- if (initializationComplete) {
- throw IllegalStateException(
- "InitializerApi.updateData should not be " +
- "called after initialization is complete."
- )
+ val newData = transform(currentData)
+ if (newData != currentData) {
+ writeData(newData, updateCache = false)
+ currentData = newData
+ }
+
+ currentData
+ }
+ }
}
- val newData = transform(initData)
- if (newData != initData) {
- writeData(newData)
- initData = newData
+ initTasks?.forEach { it(api) }
+ // Init tasks have run successfully, we don't need them anymore.
+ initTasks = null
+ updateLock.withLock {
+ initializationComplete = true
}
- initData
+ // only to make compiler happy
+ currentData
}
}
+ downstreamFlow.value = initData
+ fileObserver.startWatching()
}
-
- initTasks?.forEach { it(api) }
- initTasks = null // Init tasks have run successfully, we don't need them anymore.
- updateLock.withLock {
- initializationComplete = true
- }
-
- downstreamFlow.value = Data(initData, initData.hashCode(), /* unused */ version = 0)
}
- private suspend fun readDataOrHandleCorruption(): T {
+ // Only be called from `readAndInit`. State is UnInitialized or ReadException.
+ private suspend fun readDataOrHandleCorruption(hasWriteFileLock: Boolean): Data<T> {
try {
- return readData()
+ if (hasWriteFileLock) {
+ val data = readDataFromFileOrDefault()
+ return Data(data, data.hashCode(), version = sharedCounter.getValue())
+ } else {
+ return tryGetReadFileLock {
+ val data = readDataFromFileOrDefault()
+ val version = if (it) sharedCounter.getValue() else INVALID_VERSION
+ Data(
+ data,
+ data.hashCode(),
+ version
+ )
+ }
+ }
} catch (ex: CorruptionException) {
-
val newData: T = corruptionHandler.handleCorruption(ex)
+ var version: Int = INVALID_VERSION // should be overridden if write successfully
try {
- writeData(newData)
+ // TODO(b/241286493): acquire the write lock and confirm the data is still corrupted
+ // before overwriting to avoid race condition
+ if (hasWriteFileLock) {
+ version = writeData(newData)
+ } else {
+ getWriteFileLock {
+ version = writeData(newData)
+ newData
+ }
+ }
} catch (writeEx: IOException) {
// If we fail to write the handled data, add the new exception as a suppressed
// exception.
@@ -347,14 +395,39 @@
}
// If we reach this point, we've successfully replaced the data on disk with newData.
- return newData
+ return Data(newData, newData.hashCode(), version)
}
}
+ // It handles the read when the current state is Data
private suspend fun readData(): T {
+ // Check if the cached version matches with shared memory counter
+ val currentState = downstreamFlow.value
+ val version = sharedCounter.getValue()
+ val cachedVersion = if (currentState is Data) currentState.version else INVALID_VERSION
+
+ // Return cached value if cached version is latest
+ if (currentState is Data && version == cachedVersion) {
+ return currentState.value
+ }
+ val data = tryGetReadFileLock {
+ val result = readDataFromFileOrDefault()
+ Data(
+ result,
+ result.hashCode(),
+ if (it) sharedCounter.getValue() else INVALID_VERSION
+ )
+ }
+ downstreamFlow.value = data
+ return data.value
+ }
+
+ // Caller is responsible for (try to) getting file lock. It reads from the file directly without
+ // checking shared counter version and returns serializer default value if file is not found.
+ private suspend fun readDataFromFileOrDefault(): T {
try {
- FileInputStream(file).use { stream ->
- return serializer.readFrom(stream)
+ return FileInputStream(file).use { stream ->
+ serializer.readFrom(stream)
}
} catch (ex: FileNotFoundException) {
if (file.exists()) {
@@ -364,36 +437,25 @@
}
}
- // downstreamFlow.value must be successfully set to data before calling this
private suspend fun transformAndWrite(
transform: suspend (t: T) -> T,
callerContext: CoroutineContext
- ): T {
- // value is not null or an exception because we must have the value set by now so this cast
- // is safe.
- val curDataAndHash = downstreamFlow.value as Data<T>
- curDataAndHash.checkHashCode()
-
- val curData = curDataAndHash.value
+ ): T = getWriteFileLock {
+ val curData = readDataFromFileOrDefault()
+ val curDataAndHash = Data(curData, curData.hashCode(), /* unused */ version = 0)
val newData = withContext(callerContext) { transform(curData) }
// Check that curData has not changed...
curDataAndHash.checkHashCode()
- return if (curData == newData) {
- curData
- } else {
+ if (curData != newData) {
writeData(newData)
- downstreamFlow.value = Data(newData, newData.hashCode(), /* unused */ version = 0)
- newData
}
- }
+ newData
+ }.value
- /**
- * Internal only to prevent creation of synthetic accessor function. Do not call this from
- * outside this class.
- */
- internal suspend fun writeData(newData: T) {
+ // Write data to disk and return the corresponding version if succeed.
+ internal suspend fun writeData(newData: T, updateCache: Boolean = true): Int {
file.createParentDirectories()
val scratchFile = File(file.absolutePath + SCRATCH_SUFFIX)
@@ -402,9 +464,11 @@
serializer.writeTo(newData, UncloseableOutputStream(stream))
stream.fd.sync()
// TODO(b/151635324): fsync the directory, otherwise a badly timed crash could
- // result in reverting to a previous state.
+ // result in reverting to a previous state.
}
+ val newVersion = sharedCounter.incrementAndGetValue()
+
if (!scratchFile.renameTo(file)) {
throw IOException(
"Unable to rename $scratchFile." +
@@ -413,6 +477,12 @@
"datastore for this file."
)
}
+
+ if (updateCache) {
+ downstreamFlow.value = Data(newData, newData.hashCode(), newVersion)
+ }
+
+ return newVersion
} catch (ex: IOException) {
if (scratchFile.exists()) {
scratchFile.delete() // Swallow failure to delete
@@ -421,6 +491,17 @@
}
}
+ private fun fileWithSuffix(suffix: String): File {
+ return File(file.absolutePath + suffix)
+ }
+
+ private fun File.createIfNotExists() {
+ createParentDirectories()
+ if (!exists()) {
+ createNewFile()
+ }
+ }
+
private fun File.createParentDirectories() {
val parent: File? = canonicalFile.parentFile
@@ -432,6 +513,36 @@
}
}
+ private suspend fun getWriteFileLock(block: suspend () -> T): Data<T> {
+ // TODO(b/239970979): use kotlinx.coroutines.sync.Mutex instead of adhoc ThreadLock with
+ // semaphore to make the best use of coroutine
+ ThreadLock.acquire(threadLockSemaphore).use {
+ FileOutputStream(lockFile).use { lockFileStream ->
+ lockFileStream.getChannel().lock(0L, Long.MAX_VALUE, /* shared= */ false)
+ .use { _ ->
+ val data = block()
+ return Data(data, data.hashCode(), sharedCounter.getValue())
+ }
+ }
+ }
+ }
+
+ private suspend fun tryGetReadFileLock(
+ block: suspend (Boolean) -> Data<T>
+ ): Data<T> {
+ ThreadLock.tryAcquire(threadLockSemaphore).use {
+ if (it.acquired() == false) {
+ return block(false)
+ }
+ FileInputStream(lockFile).use { lockFileStream ->
+ lockFileStream.getChannel().tryLock(0L, Long.MAX_VALUE, /* shared= */ true)
+ .use { lock ->
+ return block(lock != null)
+ }
+ }
+ }
+ }
+
internal companion object {
/**
* Active files should contain the absolute path for which there are currently active
@@ -443,5 +554,7 @@
internal val activeFiles = mutableSetOf<String>()
internal val activeFilesLock = Any()
+
+ internal val initTaskLock = Mutex()
}
}
\ No newline at end of file
diff --git a/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/ThreadLock.kt b/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/ThreadLock.kt
new file mode 100644
index 0000000..27ef8fa
--- /dev/null
+++ b/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/ThreadLock.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.datastore.multiprocess
+
+import java.io.Closeable
+import java.io.InterruptedIOException
+import java.util.concurrent.Semaphore
+
+internal class ThreadLock(
+ private val semaphore: Semaphore?
+) : Closeable {
+ fun acquired(): Boolean {
+ return semaphore != null
+ }
+
+ override fun close() {
+ if (acquired()) {
+ semaphore!!.release()
+ }
+ }
+
+ internal companion object {
+ suspend fun tryAcquire(semaphore: Semaphore): ThreadLock {
+ val acquired = semaphore.tryAcquire()
+ return if (acquired) ThreadLock(semaphore) else ThreadLock(null)
+ }
+
+ suspend fun acquire(semaphore: Semaphore): ThreadLock {
+ try {
+ semaphore.acquire()
+ } catch (ex: InterruptedException) {
+ throw InterruptedIOException("semaphore not acquired: $ex")
+ }
+ return ThreadLock(semaphore)
+ }
+ }
+}
diff --git a/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/internal/Message.kt b/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/internal/Message.kt
index b3ae931..0b8ff0c 100644
--- a/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/internal/Message.kt
+++ b/datastore/datastore-multiprocess/src/main/java/androidx/datastore/multiprocess/internal/Message.kt
@@ -28,8 +28,7 @@
* has not been cached, it triggers a new read to the specified dataChannel.
*/
class Read<T>(
- override val lastState: State<T>?,
- val isBlocking: Boolean = false
+ override val lastState: State<T>?
) : Message<T>()
/** Represents an update operation. */
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt
index ee2d264..c9cca26 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt
@@ -33,10 +33,10 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.datastore.preferences.core.intPreferencesKey
-import androidx.glance.Button
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.LocalContext
+import androidx.glance.action.Action
import androidx.glance.action.ActionParameters
import androidx.glance.action.actionParametersOf
import androidx.glance.action.actionStartActivity
@@ -56,10 +56,13 @@
import androidx.glance.currentState
import androidx.glance.layout.Alignment
import androidx.glance.layout.Column
+import androidx.glance.layout.ColumnScope
import androidx.glance.layout.Row
+import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
+import androidx.glance.layout.size
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextDecoration
@@ -129,7 +132,7 @@
}
@Composable
-private fun StartActivityActions() {
+private fun ColumnScope.StartActivityActions() {
Button(
text = "Intent",
onClick = actionStartActivity(
@@ -161,12 +164,13 @@
actionParametersOf(
StartMessageKey to "Start activity by component name"
)
- )
+ ),
+ withSpace = false
)
}
@Composable
-private fun StartServiceActions() {
+private fun ColumnScope.StartServiceActions() {
Button(
text = "Intent",
onClick = actionStartService(
@@ -185,12 +189,13 @@
text = "Component name",
onClick = actionStartService(
ComponentName(LocalContext.current, ActionDemoService::class.java)
- )
+ ),
+ withSpace = false
)
}
@Composable
-private fun SendBroadcastActions() {
+private fun ColumnScope.SendBroadcastActions() {
Button(
text = "Intent",
onClick = actionSendBroadcast(
@@ -211,11 +216,24 @@
text = "Component name",
onClick = actionSendBroadcast(
ComponentName(LocalContext.current, ActionAppWidgetReceiver::class.java)
- )
+ ),
+ withSpace = false
)
}
/**
+ * Reimplementation of the [androidx.glance.Button] that adds a spacer after it.
+ */
+@Suppress("unused")
+@Composable
+private fun ColumnScope.Button(text: String, onClick: Action, withSpace: Boolean = true) {
+ androidx.glance.Button(text, onClick)
+ if (withSpace) {
+ Spacer(GlanceModifier.size(4.dp))
+ }
+}
+
+/**
* Action to update the [SelectedItemKey] value whenever users clicks on text
*/
class UpdateAction : ActionCallback {
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultStateAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultStateAppWidget.kt
index 854aadc..360c3de 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultStateAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/DefaultStateAppWidget.kt
@@ -50,6 +50,7 @@
Button(
modifier = GlanceModifier.defaultWeight(),
text = "-",
+ style = TextStyle(textAlign = TextAlign.Center),
onClick = actionRunCallback<ClickAction>(
actionParametersOf(ClickValueKey to -1)
)
@@ -62,6 +63,7 @@
Button(
modifier = GlanceModifier.defaultWeight(),
text = "+",
+ style = TextStyle(textAlign = TextAlign.Center),
onClick = actionRunCallback<ClickAction>(
actionParametersOf(ClickValueKey to 1)
)
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ImageAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ImageAppWidget.kt
index b27d911..f15b3d8 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ImageAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ImageAppWidget.kt
@@ -37,9 +37,11 @@
import androidx.glance.currentState
import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale
+import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
+import androidx.glance.layout.size
/**
* Sample AppWidget that showcase the [ContentScale] options for [Image]
@@ -61,6 +63,7 @@
modifier = GlanceModifier.fillMaxWidth(),
onClick = actionRunCallback<ChangeImageAction>()
)
+ Spacer(GlanceModifier.size(4.dp))
Image(
provider = ImageProvider(R.drawable.compose),
contentDescription = "Content Scale image sample (value: $type)",
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ResponsiveAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ResponsiveAppWidget.kt
index 2854922..a801ca0 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ResponsiveAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ResponsiveAppWidget.kt
@@ -24,6 +24,7 @@
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.glance.Button
+import androidx.glance.ButtonColors
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.LocalSize
@@ -44,6 +45,7 @@
import androidx.glance.layout.size
import androidx.glance.text.TextAlign
import androidx.glance.text.TextStyle
+import androidx.glance.unit.ColorProvider
/**
* Sample AppWidget that showcase the Responsive SizeMode changing its content to Row, Column or Box
@@ -152,6 +154,10 @@
Button(
text = text,
modifier = GlanceModifier.fillMaxSize().padding(8.dp).background(color),
+ colors = ButtonColors(
+ backgroundColor = ColorProvider(color),
+ contentColor = ColorProvider(Color.White)
+ ),
style = textStyle ?: TextStyle(textAlign = TextAlign.Center),
onClick = actionRunCallback<ResponsiveAction>(
actionParametersOf(
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index df01294..9dec36d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -44,7 +44,7 @@
protobuf = "3.19.4"
skiko = "0.7.7"
sqldelight = "1.3.0"
-wire = "3.6.0"
+wire = "4.4.1"
[libraries]
androidAccessibilityFramework = { module = "com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework", version = { strictly = "2.1" } }
@@ -58,6 +58,7 @@
androidLintChecks = { module = "com.android.tools.lint:lint-checks", version.ref = "androidLint" }
androidLintChecksMin = { module = "com.android.tools.lint:lint-checks", version.ref = "androidLintMin" }
androidLintTests = { module = "com.android.tools.lint:lint-tests", version.ref = "androidLint" }
+androidToolsCommon = { module = "com.android.tools:common", version.ref = "androidLint" }
autoCommon = { module = "com.google.auto:auto-common", version = "0.11" }
atomicFu = { module = "org.jetbrains.kotlinx:atomicfu", version.ref = "atomicFu" }
atomicFuPluginz = { module = "org.jetbrains.kotlinx:atomicfu-gradle-plugin", version.ref = "atomicFu" }
@@ -67,7 +68,9 @@
autoValueAnnotations = { module = "com.google.auto.value:auto-value-annotations", version.ref = "autoValue" }
autoValueParcel = { module = "com.ryanharter.auto.value:auto-value-parcel", version = "0.2.6" }
antlr4 = { module = "org.antlr:antlr4", version = "4.7.1" }
+apacheAnt = { module = "org.apache.ant:ant", version = "1.10.11" }
apacheCommonsCodec = { module = "commons-codec:commons-codec", version = "1.15" }
+apacheCommonIo = { module = "commons-io:commons-io", version = "2.4" }
assertj = { module = "org.assertj:assertj-core", version = "3.11.1" }
checkerframework = { module = "org.checkerframework:checker-qual", version = "2.5.3" }
checkmark = { module = "net.saff.checkmark:checkmark", version = "0.1.2" }
@@ -96,6 +99,7 @@
hiltCore = { module = "com.google.dagger:hilt-core", version.ref = "hilt" }
intellijAnnotations = { module = "com.intellij:annotations", version = "12.0" }
javapoet = { module = "com.squareup:javapoet", version = "1.13.0" }
+json = { module = "org.json:json", version = "20180813" }
jsonSimple = { module = "com.googlecode.json-simple:json-simple", version = "1.1" }
jsqlparser = { module = "com.github.jsqlparser:jsqlparser", version = "3.1" }
jsr250 = { module = "javax.annotation:javax.annotation-api", version = "1.2" }
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index e28fe80..51bedde 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -419,7 +419,10 @@
<trusted-key id="d790f72ea8fd39551012b62dcf9f3090ce4cb752" group="org.abego.treelayout" name="org.abego.treelayout.core"/>
<trusted-key id="da7a1bb85b19e4fb05073431205c8673dc742c7c" group="org.apache.maven"/>
<trusted-key id="db0597e3144342256bc81e3ec727d053c4481cf5" group="org.tensorflow"/>
- <trusted-key id="dbd744ace7ade6aa50dd591f66b50994442d2d40" group="com.squareup.okio"/>
+ <trusted-key id="dbd744ace7ade6aa50dd591f66b50994442d2d40">
+ <trusting group="com.squareup.okio"/>
+ <trusting group="com.squareup.wire"/>
+ </trusted-key>
<trusted-key id="dddafa7674e54418" group="org.testng"/>
<trusted-key id="e0130a3ed5a2079e" group="org.webjars"/>
<trusted-key id="e0cb7823cfd00fbf" group="com.jakewharton.android.repackaged"/>
diff --git a/mediarouter/mediarouter/src/main/res/values-zh-rCN/strings.xml b/mediarouter/mediarouter/src/main/res/values-zh-rCN/strings.xml
index 0ba8e06..bae9baf 100644
--- a/mediarouter/mediarouter/src/main/res/values-zh-rCN/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-zh-rCN/strings.xml
@@ -18,14 +18,14 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mr_system_route_name" msgid="7449553026175453403">"系统"</string>
<string name="mr_user_route_category_name" msgid="4088331695424166162">"设备"</string>
- <string name="mr_button_content_description" msgid="2939063992730535343">"投射"</string>
- <string name="mr_cast_button_disconnected" msgid="8071109333469380363">"投射。已断开连接"</string>
- <string name="mr_cast_button_connecting" msgid="6629927151350192407">"投射。正在连接"</string>
- <string name="mr_cast_button_connected" msgid="6073720094880410356">"投射。已连接"</string>
- <string name="mr_chooser_title" msgid="1419936397646839840">"投射到"</string>
+ <string name="mr_button_content_description" msgid="2939063992730535343">"投放"</string>
+ <string name="mr_cast_button_disconnected" msgid="8071109333469380363">"投放。已断开连接"</string>
+ <string name="mr_cast_button_connecting" msgid="6629927151350192407">"投放。正在连接"</string>
+ <string name="mr_cast_button_connected" msgid="6073720094880410356">"投放。已连接"</string>
+ <string name="mr_chooser_title" msgid="1419936397646839840">"投放到"</string>
<string name="mr_chooser_searching" msgid="6114250663023140921">"正在查找设备"</string>
<string name="mr_controller_disconnect" msgid="7812275474138309497">"断开连接"</string>
- <string name="mr_controller_stop_casting" msgid="804210341192624074">"停止投射"</string>
+ <string name="mr_controller_stop_casting" msgid="804210341192624074">"停止投放"</string>
<string name="mr_controller_close_description" msgid="5684434439232634509">"关闭"</string>
<string name="mr_controller_play" msgid="1253345086594430054">"播放"</string>
<string name="mr_controller_pause" msgid="747801650871398383">"暂停"</string>
@@ -36,7 +36,7 @@
<string name="mr_controller_volume_slider" msgid="2955862765169128170">"音量滑块"</string>
<string name="mr_controller_no_media_selected" msgid="5495452265246139458">"未选择任何媒体内容"</string>
<string name="mr_controller_no_info_available" msgid="855271725131981086">"没有任何相关信息"</string>
- <string name="mr_controller_casting_screen" msgid="9171231064758955152">"正在投射屏幕"</string>
+ <string name="mr_controller_casting_screen" msgid="9171231064758955152">"正在投放屏幕"</string>
<string name="mr_dialog_default_group_name" msgid="4115858704575247342">"群组"</string>
<string name="mr_dialog_groupable_header" msgid="4307018456678388936">"添加设备"</string>
<string name="mr_dialog_transferable_header" msgid="6068257520605505468">"在一组设备上播放"</string>
diff --git a/navigation/navigation-dynamic-features-fragment/src/main/res/values-fa/strings.xml b/navigation/navigation-dynamic-features-fragment/src/main/res/values-fa/strings.xml
index 4f43a5e..f4be2eb 100644
--- a/navigation/navigation-dynamic-features-fragment/src/main/res/values-fa/strings.xml
+++ b/navigation/navigation-dynamic-features-fragment/src/main/res/values-fa/strings.xml
@@ -21,6 +21,6 @@
<string name="installation_cancelled" msgid="475402237100444685">"نصب لغو شد."</string>
<string name="installing_module" msgid="5968445461040787716">"مدول نصب:"</string>
<string name="progress" msgid="8366783942222789124">"پیشرفت:"</string>
- <string name="retry" msgid="1065327189183624288">"سعی مجدد"</string>
+ <string name="retry" msgid="1065327189183624288">"مجدد امتحان کردن"</string>
<string name="ok" msgid="4702104660890557116">"تأیید"</string>
</resources>
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java
index a25c616..ef94ee4 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java
@@ -158,7 +158,7 @@
@Test
@SdkSuppress(minSdkVersion = 24)
public void testDrag_dest() {
- launchTestActivity(UiObject2TestDragActivity.class);
+ launchTestActivity(DragTestActivity.class);
UiObject2 dragButton = mDevice.findObject(By.res(TEST_APP, "drag_button"));
UiObject2 dragDestination = mDevice.findObject(By.res(TEST_APP, "drag_destination"));
@@ -172,7 +172,7 @@
@Test
@SdkSuppress(minSdkVersion = 24)
public void testDrag_destAndSpeed() {
- launchTestActivity(UiObject2TestDragActivity.class);
+ launchTestActivity(DragTestActivity.class);
UiObject2 dragButton = mDevice.findObject(By.res(TEST_APP, "drag_button"));
UiObject2 dragDestination = mDevice.findObject(By.res(TEST_APP, "drag_destination"));
@@ -186,7 +186,7 @@
@Test
@SdkSuppress(minSdkVersion = 24)
public void testDrag_destAndSpeed_throwsIllegalArgumentException() {
- launchTestActivity(UiObject2TestDragActivity.class);
+ launchTestActivity(DragTestActivity.class);
UiObject2 dragButton = mDevice.findObject(By.res(TEST_APP, "drag_button"));
UiObject2 dragDestination = mDevice.findObject(By.res(TEST_APP, "drag_destination"));
@@ -557,7 +557,7 @@
@Test
public void testSwipe() {
- launchTestActivity(UiObject2TestSwipeActivity.class);
+ launchTestActivity(SwipeTestActivity.class);
UiObject2 swipeRegion = mDevice.findObject(By.res(TEST_APP, "swipe_region"));
swipeRegion.setGestureMargin(SCROLL_MARGIN);
@@ -577,7 +577,7 @@
@Test
public void testSwipe_throwsIllegalArgumentException() {
- launchTestActivity(UiObject2TestSwipeActivity.class);
+ launchTestActivity(SwipeTestActivity.class);
UiObject2 swipeRegion = mDevice.findObject(By.res(TEST_APP, "swipe_region"));
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
index d1ce15d..5301a70 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
@@ -21,6 +21,9 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import android.graphics.Rect;
+
+import androidx.test.filters.SdkSuppress;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiObjectNotFoundException;
import androidx.test.uiautomator.UiSelector;
@@ -28,6 +31,7 @@
import org.junit.Test;
public class UiObjectTest extends BaseTest {
+ private static final int TIMEOUT_MS = 10_000;
@Test
public void testGetChild() throws Exception {
@@ -74,20 +78,94 @@
noNode::getChildCount);
}
+ @Test
+ @SdkSuppress(minSdkVersion = 24)
+ public void testDragTo_destObjAndSteps() throws Exception {
+ launchTestActivity(DragTestActivity.class);
+
+ UiObject dragButton = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/drag_button"));
+ UiObject dragDestination = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/drag_destination"));
+ UiObject expectedDragDest = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/drag_destination").text("drag_received"));
+
+ assertEquals("no_drag_yet", dragDestination.getText());
+ // Returning true from `dragTo` means that the drag action is performed successfully, not
+ // necessarily the target is dragged to the desired destination.
+ // The same applies to all the following tests.
+ assertTrue(dragButton.dragTo(dragDestination, 40));
+ assertTrue(expectedDragDest.waitForExists(TIMEOUT_MS));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 24)
+ public void testDragTo_destXAndDestYAndSteps() throws Exception {
+ launchTestActivity(DragTestActivity.class);
+
+ UiObject dragButton = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/drag_button"));
+ UiObject dragDestination = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/drag_destination"));
+ UiObject expectedDragDest = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/drag_destination").text("drag_received"));
+ Rect destBounds = dragDestination.getVisibleBounds();
+
+ assertEquals("no_drag_yet", dragDestination.getText());
+ assertTrue(dragButton.dragTo(destBounds.centerX(), destBounds.centerY(), 40));
+ assertTrue(expectedDragDest.waitForExists(TIMEOUT_MS));
+ }
+
+ @Test
+ public void testSwipeUp() throws Exception {
+ launchTestActivity(SwipeTestActivity.class);
+
+ UiObject swipeRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/swipe_region"));
+
+ assertEquals("no_swipe", swipeRegion.getText());
+ assertTrue(swipeRegion.swipeUp(100));
+ assertEquals("swipe_up", swipeRegion.getText());
+ }
+
+ @Test
+ public void testSwipeDown() throws Exception {
+ launchTestActivity(SwipeTestActivity.class);
+
+ UiObject swipeRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/swipe_region"));
+
+ assertEquals("no_swipe", swipeRegion.getText());
+ assertTrue(swipeRegion.swipeDown(100));
+ assertEquals("swipe_down", swipeRegion.getText());
+ }
+
+ @Test
+ public void testSwipeLeft() throws Exception {
+ launchTestActivity(SwipeTestActivity.class);
+
+ UiObject swipeRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/swipe_region"));
+
+ assertEquals("no_swipe", swipeRegion.getText());
+ assertTrue(swipeRegion.swipeLeft(100));
+ assertEquals("swipe_left", swipeRegion.getText());
+ }
+
+ @Test
+ public void testSwipeRight() throws Exception {
+ launchTestActivity(SwipeTestActivity.class);
+
+ UiObject swipeRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+ + "/swipe_region"));
+
+ assertEquals("no_swipe", swipeRegion.getText());
+ assertTrue(swipeRegion.swipeRight(100));
+ assertEquals("swipe_right", swipeRegion.getText());
+ }
+
/* TODO(b/241158642): Implement these tests, and the tests for exceptions of each tested method.
- public void testDragTo_destObjAndSteps() {}
-
- public void testDragTo_destXAndDestYAndSteps() {}
-
- public void testSwipeUp() {}
-
- public void testSwipeDown() {}
-
- public void testSwipeLeft() {}
-
- public void testSwipeRight() {}
-
public void testClick() {}
public void testClickAndWaitForNewWindow() {}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
index 59e6900..99fd1c1 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -41,6 +41,13 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
+ <activity android:name=".DragTestActivity"
+ android:exported="true"
+ android:theme="@android:style/Theme.Holo.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
<activity android:name=".ParentChildTestActivity"
android:exported="true"
android:theme="@android:style/Theme.Holo.NoActionBar">
@@ -65,6 +72,13 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
+ <activity android:name=".SwipeTestActivity"
+ android:exported="true"
+ android:theme="@android:style/Theme.Holo.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
<activity android:name=".UiDeviceTestClickActivity"
android:exported="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
@@ -94,13 +108,6 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
- <activity android:name=".UiObject2TestDragActivity"
- android:exported="true"
- android:theme="@android:style/Theme.Holo.NoActionBar">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- </intent-filter>
- </activity>
<activity android:name=".UiObject2TestFlingActivity"
android:exported="true"
android:theme="@android:style/Theme.Holo.NoActionBar">
@@ -157,13 +164,6 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
- <activity android:name=".UiObject2TestSwipeActivity"
- android:exported="true"
- android:theme="@android:style/Theme.Holo.NoActionBar">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- </intent-filter>
- </activity>
<activity android:name=".UiObject2TestVerticalScrollActivity"
android:exported="true"
android:theme="@android:style/Theme.Holo.NoActionBar">
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestDragActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/DragTestActivity.java
similarity index 86%
rename from test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestDragActivity.java
rename to test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/DragTestActivity.java
index be56bfd..5db41de 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestDragActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/DragTestActivity.java
@@ -29,16 +29,16 @@
import androidx.annotation.RequiresApi;
@RequiresApi(24)
-public class UiObject2TestDragActivity extends Activity {
+public class DragTestActivity extends Activity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.uiobject2_testdrag_activity);
+ setContentView(R.layout.drag_test_activity);
- Button dragButton = (Button) findViewById(R.id.drag_button);
- TextView dragDestination = (TextView) findViewById(R.id.drag_destination);
+ Button dragButton = findViewById(R.id.drag_button);
+ TextView dragDestination = findViewById(R.id.drag_destination);
dragButton.setOnLongClickListener(v -> {
v.startDragAndDrop(ClipData.newPlainText(null, null),
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestSwipeActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/SwipeTestActivity.java
similarity index 93%
rename from test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestSwipeActivity.java
rename to test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/SwipeTestActivity.java
index ed36e65..fda840d 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestSwipeActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/SwipeTestActivity.java
@@ -24,7 +24,7 @@
import androidx.annotation.Nullable;
-public class UiObject2TestSwipeActivity extends Activity {
+public class SwipeTestActivity extends Activity {
private GestureDetector mGestureDetector;
@@ -32,7 +32,7 @@
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.uiobject2_testswipe_activity);
+ setContentView(R.layout.swipe_test_activity);
TextView swipeRegion = findViewById(R.id.swipe_region);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testdrag_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/drag_test_activity.xml
similarity index 92%
rename from test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testdrag_activity.xml
rename to test/uiautomator/integration-tests/testapp/src/main/res/layout/drag_test_activity.xml
index aa9e570..47890f3 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testdrag_activity.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/drag_test_activity.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!--
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -18,7 +18,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- tools:context=".UiObject2TestDragActivity">
+ tools:context=".DragTestActivity">
<Button
android:id="@+id/drag_button"
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testswipe_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/swipe_test_activity.xml
similarity index 89%
rename from test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testswipe_activity.xml
rename to test/uiautomator/integration-tests/testapp/src/main/res/layout/swipe_test_activity.xml
index 9da8334..c9936e2 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testswipe_activity.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/swipe_test_activity.xml
@@ -18,12 +18,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- tools:context=".UiObject2TestSwipeActivity">
+ tools:context=".SwipeTestActivity">
<TextView
android:id="@+id/swipe_region"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_margin="30dp"
+ android:background="@android:color/white"
android:gravity="center"
android:text="no_swipe" />
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyScrollAccessibilityTest.kt b/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyScrollAccessibilityTest.kt
deleted file mode 100644
index 53c9775..0000000
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyScrollAccessibilityTest.kt
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * 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.tv.compose.foundation.lazy.list
-
-import android.R.id.accessibilityActionScrollDown
-import android.R.id.accessibilityActionScrollLeft
-import android.R.id.accessibilityActionScrollRight
-import android.R.id.accessibilityActionScrollUp
-import android.view.View
-import android.view.accessibility.AccessibilityNodeProvider
-import androidx.activity.ComponentActivity
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.requiredSize
-import androidx.compose.foundation.text.BasicText
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.platform.LocalView
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.semantics.SemanticsNode
-import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import androidx.core.view.ViewCompat
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD
-import androidx.test.filters.MediumTest
-import androidx.tv.foundation.PivotOffsets
-import com.google.common.truth.IterableSubject
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-@MediumTest
-@RunWith(Parameterized::class)
-class LazyScrollAccessibilityTest(private val config: TestConfig) {
- data class TestConfig(
- val horizontal: Boolean,
- val rtl: Boolean,
- val reversed: Boolean
- ) {
- val vertical = !horizontal
-
- override fun toString(): String {
- return (if (horizontal) "horizontal" else "vertical") +
- (if (rtl) ",rtl" else ",ltr") +
- (if (reversed) ",reversed" else "")
- }
- }
-
- companion object {
- @JvmStatic
- @Parameterized.Parameters(name = "{0}")
- fun params() =
- listOf(true, false).flatMap { horizontal ->
- listOf(false, true).flatMap { rtl ->
- listOf(false, true).map { reversed ->
- TestConfig(horizontal, rtl, reversed)
- }
- }
- }
- }
-
- @get:Rule
- val rule = createAndroidComposeRule<ComponentActivity>()
-
- private val scrollerTag = "ScrollerTest"
- private var composeView: View? = null
- private val accessibilityNodeProvider: AccessibilityNodeProvider
- get() = checkNotNull(composeView) {
- "composeView not initialized. Did `composeView = LocalView.current` not work?"
- }.let { composeView ->
- ViewCompat
- .getAccessibilityDelegate(composeView)!!
- .getAccessibilityNodeProvider(composeView)!!
- .provider as AccessibilityNodeProvider
- }
-
- @Test
- fun scrollForward() {
- testRelativeDirection(58, ACTION_SCROLL_FORWARD)
- }
-
- @Test
- fun scrollBackward() {
- testRelativeDirection(41, ACTION_SCROLL_BACKWARD)
- }
-
- @Test
- fun scrollRight() {
- testAbsoluteDirection(58, accessibilityActionScrollRight, config.horizontal)
- }
-
- @Test
- fun scrollLeft() {
- testAbsoluteDirection(41, accessibilityActionScrollLeft, config.horizontal)
- }
-
- @Test
- fun scrollDown() {
- testAbsoluteDirection(58, accessibilityActionScrollDown, config.vertical)
- }
-
- @Test
- fun scrollUp() {
- testAbsoluteDirection(41, accessibilityActionScrollUp, config.vertical)
- }
-
- @Test
- fun verifyScrollActionsAtStart() {
- createScrollableContent_StartAtStart()
- verifyNodeInfoScrollActions(
- expectForward = !config.reversed,
- expectBackward = config.reversed
- )
- }
-
- @Test
- fun verifyScrollActionsInMiddle() {
- createScrollableContent_StartInMiddle()
- verifyNodeInfoScrollActions(
- expectForward = true,
- expectBackward = true
- )
- }
-
- @Test
- fun verifyScrollActionsAtEnd() {
- createScrollableContent_StartAtEnd()
- verifyNodeInfoScrollActions(
- expectForward = config.reversed,
- expectBackward = !config.reversed
- )
- }
-
- /**
- * Setup the test, run the given [accessibilityAction], and check if the [canonicalTarget]
- * has been reached. The canonical target is the item that we expect to see when moving
- * forward in a non-reversed scrollable (e.g. down in LazyColumn or right in LazyRow in LTR).
- * The actual target is either the canonical target or the target that is as far from the
- * middle of the lazy list as the canonical target, but on the other side of the middle,
- * depending on the [configuration][config].
- */
- private fun testRelativeDirection(canonicalTarget: Int, accessibilityAction: Int) {
- val target = if (!config.reversed) canonicalTarget else 100 - canonicalTarget - 1
- testScrollAction(target, accessibilityAction)
- }
-
- /**
- * Setup the test, run the given [accessibilityAction], and check if the [canonicalTarget]
- * has been reached (but only if we [expect][expectActionSuccess] the action to succeed).
- * The canonical target is the item that we expect to see when moving forward in a
- * non-reversed scrollable (e.g. down in LazyColumn or right in LazyRow in LTR). The actual
- * target is either the canonical target or the target that is as far from the middle of the
- * scrollable as the canonical target, but on the other side of the middle, depending on the
- * [configuration][config].
- */
- private fun testAbsoluteDirection(
- canonicalTarget: Int,
- accessibilityAction: Int,
- expectActionSuccess: Boolean
- ) {
- var target = canonicalTarget
- if (config.horizontal && config.rtl) {
- target = 100 - target - 1
- }
- if (config.reversed) {
- target = 100 - target - 1
- }
- testScrollAction(target, accessibilityAction, expectActionSuccess)
- }
-
- /**
- * Setup the test, run the given [accessibilityAction], and check if the [target] has been
- * reached (but only if we [expect][expectActionSuccess] the action to succeed).
- */
- private fun testScrollAction(
- target: Int,
- accessibilityAction: Int,
- expectActionSuccess: Boolean = true
- ) {
- createScrollableContent_StartInMiddle()
- rule.onNodeWithText("$target").assertDoesNotExist()
-
- val returnValue = rule.onNodeWithTag(scrollerTag).withSemanticsNode {
- accessibilityNodeProvider.performAction(id, accessibilityAction, null)
- }
-
- assertThat(returnValue).isEqualTo(expectActionSuccess)
- if (expectActionSuccess) {
- rule.onNodeWithText("$target").assertIsDisplayed()
- } else {
- rule.onNodeWithText("$target").assertDoesNotExist()
- }
- }
-
- /**
- * Checks if all of the scroll actions are present or not according to what we expect based on
- * [expectForward] and [expectBackward]. The scroll actions that are checked are forward,
- * backward, left, right, up and down. The expectation parameters must already account for
- * [reversing][TestConfig.reversed].
- */
- private fun verifyNodeInfoScrollActions(expectForward: Boolean, expectBackward: Boolean) {
- val nodeInfo = rule.onNodeWithTag(scrollerTag).withSemanticsNode {
- rule.runOnUiThread {
- accessibilityNodeProvider.createAccessibilityNodeInfo(id)
- }
- }
-
- val actions = nodeInfo.actionList.map { it.id }
-
- assertThat(actions).contains(expectForward, ACTION_SCROLL_FORWARD)
- assertThat(actions).contains(expectBackward, ACTION_SCROLL_BACKWARD)
-
- if (config.horizontal) {
- val expectLeft = if (config.rtl) expectForward else expectBackward
- val expectRight = if (config.rtl) expectBackward else expectForward
- assertThat(actions).contains(expectLeft, accessibilityActionScrollLeft)
- assertThat(actions).contains(expectRight, accessibilityActionScrollRight)
- assertThat(actions).contains(false, accessibilityActionScrollDown)
- assertThat(actions).contains(false, accessibilityActionScrollUp)
- } else {
- assertThat(actions).contains(false, accessibilityActionScrollLeft)
- assertThat(actions).contains(false, accessibilityActionScrollRight)
- assertThat(actions).contains(expectForward, accessibilityActionScrollDown)
- assertThat(actions).contains(expectBackward, accessibilityActionScrollUp)
- }
- }
-
- private fun IterableSubject.contains(expectPresent: Boolean, element: Any) {
- if (expectPresent) {
- contains(element)
- } else {
- doesNotContain(element)
- }
- }
-
- /**
- * Creates a Row/Column that starts at the first item, according to [createScrollableContent]
- */
- private fun createScrollableContent_StartAtStart() {
- createScrollableContent {
- // Start at the start:
- // -> pretty basic
- rememberLazyListState(0, 0)
- }
- }
-
- /**
- * Creates a Row/Column that starts in the middle, according to [createScrollableContent]
- */
- private fun createScrollableContent_StartInMiddle() {
- createScrollableContent {
- // Start at the middle:
- // Content size: 100 items * 21dp per item = 2100dp
- // Viewport size: 200dp rect - 50dp padding on both sides = 100dp
- // Content outside viewport: 2100dp - 100dp = 2000dp
- // -> centered when 1000dp on either side, which is 47 items + 13dp
- rememberLazyListState(
- 47,
- with(LocalDensity.current) { 13.dp.roundToPx() }
- )
- }
- }
-
- /**
- * Creates a Row/Column that starts at the last item, according to [createScrollableContent]
- */
- private fun createScrollableContent_StartAtEnd() {
- createScrollableContent {
- // Start at the end:
- // Content size: 100 items * 21dp per item = 2100dp
- // Viewport size: 200dp rect - 50dp padding on both sides = 100dp
- // Content outside viewport: 2100dp - 100dp = 2000dp
- // -> at the end when offset at 2000dp, which is 95 items + 5dp
- rememberLazyListState(
- 95,
- with(LocalDensity.current) { 5.dp.roundToPx() }
- )
- }
- }
-
- /**
- * Creates a Row/Column with a viewport of 100.dp, containing 100 items each 17.dp in size.
- * The items have a text with their index (ASC), and where the viewport starts is determined
- * by the given [lambda][rememberLazyListState]. All properties from [config] are applied.
- * The viewport has padding around it to make sure scroll distance doesn't include padding.
- */
- private fun createScrollableContent(rememberLazyListState: @Composable () -> TvLazyListState) {
- rule.setContent {
- composeView = LocalView.current
- val lazyContent: TvLazyListScope.() -> Unit = {
- items(100) {
- Box(Modifier.requiredSize(21.dp).background(Color.Yellow)) {
- BasicText("$it", Modifier.align(Alignment.Center))
- }
- }
- }
-
- val state = rememberLazyListState()
-
- Box(Modifier.requiredSize(200.dp).background(Color.White)) {
- val direction = if (config.rtl) LayoutDirection.Rtl else LayoutDirection.Ltr
- CompositionLocalProvider(LocalLayoutDirection provides direction) {
- if (config.horizontal) {
- TvLazyRow(
- Modifier.testTag(scrollerTag).matchParentSize(),
- state = state,
- contentPadding = PaddingValues(50.dp),
- reverseLayout = config.reversed,
- verticalAlignment = Alignment.CenterVertically,
- pivotOffsets =
- PivotOffsets(parentFraction = 0f)
- ) {
- lazyContent()
- }
- } else {
- TvLazyColumn(
- Modifier.testTag(scrollerTag).matchParentSize(),
- state = state,
- contentPadding = PaddingValues(50.dp),
- reverseLayout = config.reversed,
- horizontalAlignment = Alignment.CenterHorizontally,
- pivotOffsets =
- PivotOffsets(parentFraction = 0f)
- ) {
- lazyContent()
- }
- }
- }
- }
- }
- }
-
- private fun <T> SemanticsNodeInteraction.withSemanticsNode(block: SemanticsNode.() -> T): T {
- return block.invoke(fetchSemanticsNode())
- }
-}
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/AutoTestFrameClock.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/AutoTestFrameClock.kt
similarity index 95%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/AutoTestFrameClock.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/AutoTestFrameClock.kt
index 19e2105..ced2983 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/AutoTestFrameClock.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/AutoTestFrameClock.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy
+package androidx.tv.foundation.lazy
import androidx.compose.runtime.MonotonicFrameClock
import java.util.concurrent.atomic.AtomicLong
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/BaseLazyGridTestWithOrientation.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/BaseLazyGridTestWithOrientation.kt
similarity index 97%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/BaseLazyGridTestWithOrientation.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/BaseLazyGridTestWithOrientation.kt
index 5bf00b4..8c90559 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/BaseLazyGridTestWithOrientation.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/BaseLazyGridTestWithOrientation.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.animation.core.snap
import androidx.compose.foundation.gestures.Orientation
@@ -167,7 +167,7 @@
fun LazyGrid(
cells: Int,
modifier: Modifier = Modifier,
- state: TvLazyGridState = rememberLazyGridState(),
+ state: TvLazyGridState = rememberTvLazyGridState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
userScrollEnabled: Boolean = true,
@@ -190,7 +190,7 @@
fun LazyGrid(
cells: TvGridCells,
modifier: Modifier = Modifier,
- state: TvLazyGridState = rememberLazyGridState(),
+ state: TvLazyGridState = rememberTvLazyGridState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
userScrollEnabled: Boolean = true,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyArrangementsTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyArrangementsTest.kt
similarity index 97%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyArrangementsTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyArrangementsTest.kt
index 0ed6481..106d662 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyArrangementsTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyArrangementsTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.scrollBy
@@ -305,7 +305,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyVerticalGrid(
modifier = Modifier.size(itemSize * 3),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
verticalArrangement = Arrangement.spacedBy(spacingSize),
columns = TvGridCells.Fixed(1)
) {
@@ -342,7 +342,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyVerticalGrid(
modifier = Modifier.size(itemSize * 3),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
verticalArrangement = Arrangement.spacedBy(spacingSize),
columns = TvGridCells.Fixed(1)
) {
@@ -381,7 +381,7 @@
TvLazyHorizontalGrid(
TvGridCells.Fixed(1),
Modifier.size(itemSize * 3),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
horizontalArrangement = Arrangement.spacedBy(spacingSize)
) {
items(5) {
@@ -418,7 +418,7 @@
TvLazyHorizontalGrid(
TvGridCells.Fixed(1),
Modifier.size(itemSize * 3),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
horizontalArrangement = Arrangement.spacedBy(spacingSize)
) {
items(5) {
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyCustomKeysTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyCustomKeysTest.kt
similarity index 96%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyCustomKeysTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyCustomKeysTest.kt
index 0f444fa..55a2fe3 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyCustomKeysTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyCustomKeysTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -257,7 +257,7 @@
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(TvGridCells.Fixed(columns), state = state) {
items(list, key = { it.id }) {
Item(remember { "${it.id}" })
@@ -278,7 +278,7 @@
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(columns = TvGridCells.Fixed(columns), state = state) {
items(list, key = { it.id }) {
Item(remember { "${it.id}" })
@@ -303,7 +303,7 @@
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(TvGridCells.Fixed(columns), Modifier.size(itemSize * 2.5f), state) {
items(list) {
Item(remember { "$it" })
@@ -326,7 +326,7 @@
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(TvGridCells.Fixed(columns), Modifier.size(itemSize * 2.5f), state) {
items(list, key = { it }) {
Item(remember { "$it" })
@@ -352,7 +352,7 @@
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState(5)
+ state = rememberTvLazyGridState(5)
TvLazyVerticalGrid(TvGridCells.Fixed(columns), Modifier.size(itemSize * 2.5f), state) {
items(list, key = { it }) {
Item(remember { "$it" })
@@ -378,7 +378,7 @@
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState(10) // key 20 is the first item
+ state = rememberTvLazyGridState(10) // key 20 is the first item
TvLazyVerticalGrid(TvGridCells.Fixed(columns), Modifier.size(itemSize * 2.5f), state) {
items(list, key = { it }) {
Item(remember { "$it" })
@@ -404,7 +404,7 @@
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState(8)
+ state = rememberTvLazyGridState(8)
TvLazyVerticalGrid(TvGridCells.Fixed(columns), Modifier.size(itemSize * 2.5f), state) {
items(list, key = { it }) {
Item(remember { "$it" })
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
similarity index 99%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
index d966436..c0efb11 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridAnimateItemPlacementTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.LinearEasing
@@ -1257,7 +1257,7 @@
endPadding: Dp = 0.dp,
content: TvLazyGridScope.() -> Unit
) {
- state = rememberLazyGridState(startIndex)
+ state = rememberTvLazyGridState(startIndex)
if (isVertical) {
TvLazyVerticalGrid(
TvGridCells.Fixed(columns),
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridPrefetcherTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridPrefetcherTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridPrefetcherTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridPrefetcherTest.kt
index b2e1a5a..a2d095d 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridPrefetcherTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridPrefetcherTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollBy
@@ -308,7 +308,7 @@
) { constraints ->
val placeable = if (emit) {
subcompose(Unit) {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
LazyGrid(
2,
Modifier.mainAxisSize(itemsSizeDp * 1.5f),
@@ -359,7 +359,7 @@
contentPadding: PaddingValues = PaddingValues(0.dp)
) {
rule.setContent {
- state = rememberLazyGridState(
+ state = rememberTvLazyGridState(
initialFirstVisibleItemIndex = firstItem,
initialFirstVisibleItemScrollOffset = itemOffset
)
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
similarity index 95%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
index c9f6943..cd16ae9 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -51,7 +51,7 @@
fun scroll1ItemScrolledOffItemIsKeptForReuse() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * 1.5f),
@@ -83,7 +83,7 @@
fun scroll2ItemsScrolledOffItemsAreKeptForReuse() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * 1.5f),
@@ -120,7 +120,7 @@
fun checkMaxItemsKeptForReuse() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * (DefaultMaxItemsToRetain + 0.5f)),
@@ -153,7 +153,7 @@
fun scroll3Items2OfScrolledOffItemsAreKeptForReuse() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * 1.5f),
@@ -204,7 +204,7 @@
fun doMultipleScrollsOneByOne() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * 1.5f),
@@ -248,7 +248,7 @@
fun scrollBackwardOnce() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState(10)
+ state = rememberTvLazyGridState(10)
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * 1.5f),
@@ -284,7 +284,7 @@
fun scrollBackwardOneByOne() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState(10)
+ state = rememberTvLazyGridState(10)
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * 1.5f),
@@ -323,7 +323,7 @@
var rememberedValue0 = -1
var rememberedValue1 = -1
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * 1.5f),
@@ -373,7 +373,7 @@
val visibleItemsCount = (DefaultMaxItemsToRetain + 1) * 2
val startOfType1 = DefaultMaxItemsToRetain + 1
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * (visibleItemsCount - 0.5f)),
@@ -425,7 +425,7 @@
fun differentTypesFromDifferentItemCalls() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.height(itemsSizeDp * 2.5f),
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridSpanTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSpanTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridSpanTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSpanTest.kt
index 3d4c09e..94d404a 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridSpanTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSpanTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -275,7 +275,7 @@
// regression from b/222530458
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
TvLazyVerticalGrid(
columns = TvGridCells.Fixed(2),
state = state,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridTest.kt
index 4f170ec..5e29562 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import android.os.Build
import androidx.compose.foundation.focusable
@@ -648,7 +648,7 @@
var count by mutableStateOf(100)
val composedIndexes = mutableListOf<Int>()
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
LazyGrid(
cells = 1,
modifier = Modifier.mainAxisSize(10.dp),
@@ -759,7 +759,7 @@
LazyGrid(
cells = 1,
modifier = Modifier.size(itemSize * 3),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
userScrollEnabled = false,
) {
items(5) {
@@ -842,7 +842,7 @@
val itemMainAxisSize = with(rule.density) { 30.toDp() }
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
LazyGrid(
cells = 2,
modifier = Modifier.mainAxisSize(itemMainAxisSize + 1.dp),
@@ -937,7 +937,7 @@
lateinit var state: TvLazyGridState
rule.setContentWithTestViewConfiguration {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
LazyGrid(
cells = 1,
modifier = Modifier.composed {
@@ -1042,7 +1042,7 @@
val items by mutableStateOf((1..20).toList())
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
LazyGrid(
1,
Modifier.requiredSize(100.dp).testTag(LazyGridTag),
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
similarity index 95%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
index 0f06a2b..6fd11c8 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridsContentPaddingTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.animation.core.snap
import androidx.compose.foundation.gestures.animateScrollBy
@@ -82,7 +82,7 @@
columns = TvGridCells.Fixed(1),
modifier = Modifier.requiredSize(containerSize)
.testTag(LazyListTag),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
contentPadding = PaddingValues(
start = smallPaddingSize,
top = largePaddingSize,
@@ -117,7 +117,7 @@
columns = TvGridCells.Fixed(1),
modifier = Modifier.requiredSize(itemSize * 2)
.testTag(LazyListTag),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
contentPadding = PaddingValues(
top = itemSize,
bottom = itemSize
@@ -145,7 +145,7 @@
columns = TvGridCells.Fixed(1),
modifier = Modifier.requiredSize(padding * 2 + itemSize)
.testTag(LazyListTag),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
contentPadding = PaddingValues(
top = padding,
bottom = padding
@@ -187,7 +187,7 @@
columns = TvGridCells.Fixed(1),
modifier = Modifier.requiredSize(itemSize + padding * 2)
.testTag(LazyListTag),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
contentPadding = PaddingValues(
top = padding,
bottom = padding
@@ -223,7 +223,7 @@
columns = TvGridCells.Fixed(1),
modifier = Modifier.requiredSize(padding * 2 + itemSize)
.testTag(LazyListTag),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
contentPadding = PaddingValues(
top = padding,
bottom = padding
@@ -268,7 +268,7 @@
columns = TvGridCells.Fixed(1),
modifier = Modifier.requiredSize(padding * 2 + itemSize)
.testTag(LazyListTag),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
contentPadding = PaddingValues(
top = padding,
bottom = padding
@@ -384,7 +384,7 @@
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
reverseLayout = true,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
modifier = Modifier.size(listSize),
contentPadding = PaddingValues(top = topPadding, bottom = bottomPadding),
) {
@@ -415,7 +415,7 @@
fun column_overscrollWithContentPadding() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize + smallPaddingSize * 2)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -461,7 +461,7 @@
fun totalPaddingLargerParentSize_initialState() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -492,7 +492,7 @@
fun totalPaddingLargerParentSize_scrollByPadding() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -527,7 +527,7 @@
fun totalPaddingLargerParentSize_scrollToLastItem() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -562,7 +562,7 @@
fun totalPaddingLargerParentSize_scrollToLastItemByDelta() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -598,7 +598,7 @@
// the whole end content padding is displayed
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -630,7 +630,7 @@
fun eachPaddingLargerParentSize_initialState() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -658,7 +658,7 @@
fun eachPaddingLargerParentSize_scrollByPadding() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -693,7 +693,7 @@
fun eachPaddingLargerParentSize_scrollToLastItem() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -731,7 +731,7 @@
fun eachPaddingLargerParentSize_scrollToLastItemByDelta() {
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -770,7 +770,7 @@
// only the end content padding is displayed
lateinit var state: TvLazyGridState
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
@@ -808,7 +808,7 @@
// LazyRow(
// modifier = Modifier.requiredSize(containerSize)
// .testTag(LazyListTag),
- // state = rememberLazyGridState().also { state = it },
+ // state = rememberTvLazyGridState().also { state = it },
// contentPadding = PaddingValues(
// top = smallPaddingSize,
// start = largePaddingSize,
@@ -845,7 +845,7 @@
// LazyRow(
// modifier = Modifier.requiredSize(itemSize * 2)
// .testTag(LazyListTag),
- // state = rememberLazyGridState().also { state = it },
+ // state = rememberTvLazyGridState().also { state = it },
// contentPadding = PaddingValues(
// start = itemSize,
// end = itemSize
@@ -872,7 +872,7 @@
// LazyRow(
// modifier = Modifier.requiredSize(padding * 2 + itemSize)
// .testTag(LazyListTag),
- // state = rememberLazyGridState().also { state = it },
+ // state = rememberTvLazyGridState().also { state = it },
// contentPadding = PaddingValues(
// start = padding,
// end = padding
@@ -913,7 +913,7 @@
// LazyRow(
// modifier = Modifier.requiredSize(itemSize + padding * 2)
// .testTag(LazyListTag),
- // state = rememberLazyGridState().also { state = it },
+ // state = rememberTvLazyGridState().also { state = it },
// contentPadding = PaddingValues(
// start = padding,
// end = padding
@@ -948,7 +948,7 @@
// LazyRow(
// modifier = Modifier.requiredSize(padding * 2 + itemSize)
// .testTag(LazyListTag),
- // state = rememberLazyGridState().also { state = it },
+ // state = rememberTvLazyGridState().also { state = it },
// contentPadding = PaddingValues(
// start = padding,
// end = padding
@@ -992,7 +992,7 @@
// LazyRow(
// modifier = Modifier.requiredSize(padding * 2 + itemSize)
// .testTag(LazyListTag),
- // state = rememberLazyGridState().also { state = it },
+ // state = rememberTvLazyGridState().also { state = it },
// contentPadding = PaddingValues(
// start = padding,
// end = padding
@@ -1104,7 +1104,7 @@
// rule.setContentWithTestViewConfiguration {
// LazyRow(
// reverseLayout = true,
- // state = rememberLazyGridState().also { state = it },
+ // state = rememberTvLazyGridState().also { state = it },
// modifier = Modifier.requiredSize(listSize),
// contentPadding = PaddingValues(start = startPadding, end = endPadding),
// ) {
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridsIndexedTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridsIndexedTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridsIndexedTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridsIndexedTest.kt
index e99a386..7bc39fb 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridsIndexedTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridsIndexedTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.requiredHeight
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridsReverseLayoutTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridsReverseLayoutTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridsReverseLayoutTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridsReverseLayoutTest.kt
index c3cd0a9..5cf5c36 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyGridsReverseLayoutTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridsReverseLayoutTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
@@ -132,7 +132,7 @@
TvLazyVerticalGrid(
TvGridCells.Fixed(2),
reverseLayout = true,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
) {
items((0..5).toList()) {
@@ -154,7 +154,7 @@
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
reverseLayout = true,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
modifier = Modifier.size(itemSize * 2).testTag(ContainerTag)
) {
items((0..2).toList()) {
@@ -184,7 +184,7 @@
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
reverseLayout = true,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag)
) {
items((0..2).toList()) {
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyItemStateRestoration.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyItemStateRestoration.kt
similarity index 96%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyItemStateRestoration.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyItemStateRestoration.kt
index f4e519e..d75b265 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyItemStateRestoration.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyItemStateRestoration.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
@@ -89,7 +89,7 @@
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.requiredSize(20.dp),
- state = rememberLazyGridState().also { state = it }
+ state = rememberTvLazyGridState().also { state = it }
) {
items((0..10).toList()) {
if (it == 0) {
@@ -140,7 +140,7 @@
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.requiredSize(20.dp),
- state = rememberLazyGridState().also { state = it }
+ state = rememberTvLazyGridState().also { state = it }
) {
items((0..1).toList()) {
if (it == 0) {
@@ -190,7 +190,7 @@
TvLazyVerticalGrid(
TvGridCells.Fixed(1),
Modifier.requiredSize(20.dp),
- state = rememberLazyGridState().also { state = it }
+ state = rememberTvLazyGridState().also { state = it }
) {
items((0..10).toList()) {
if (it == 0) {
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyNestedScrollingTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyNestedScrollingTest.kt
similarity index 99%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyNestedScrollingTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyNestedScrollingTest.kt
index 404218a..ed9df04 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyNestedScrollingTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyNestedScrollingTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.ScrollableState
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyScrollAccessibilityTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyScrollAccessibilityTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyScrollAccessibilityTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyScrollAccessibilityTest.kt
index 8af2b8d..3f108d0 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyScrollAccessibilityTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyScrollAccessibilityTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import android.R.id.accessibilityActionScrollDown
import android.R.id.accessibilityActionScrollLeft
@@ -230,7 +230,7 @@
}
}
- val actions = nodeInfo.actionList.map { it.id }
+ val actions = nodeInfo?.actionList?.map { it.id }
assertThat(actions).contains(expectForward, ACTION_SCROLL_FORWARD)
assertThat(actions).contains(expectBackward, ACTION_SCROLL_BACKWARD)
@@ -265,7 +265,7 @@
createScrollableContent {
// Start at the start:
// -> pretty basic
- rememberLazyGridState(0, 0)
+ rememberTvLazyGridState(0, 0)
}
}
@@ -279,7 +279,7 @@
// Viewport size: 200dp rect - 50dp padding on both sides = 100dp
// Content outside viewport: 2100dp - 100dp = 2000dp
// -> centered when 1000dp on either side, which is 47 items + 13dp
- rememberLazyGridState(
+ rememberTvLazyGridState(
47,
with(LocalDensity.current) { 13.dp.roundToPx() }
)
@@ -296,7 +296,7 @@
// Viewport size: 200dp rect - 50dp padding on both sides = 100dp
// Content outside viewport: 2100dp - 100dp = 2000dp
// -> at the end when offset at 2000dp, which is 95 items + 5dp
- rememberLazyGridState(
+ rememberTvLazyGridState(
95,
with(LocalDensity.current) { 5.dp.roundToPx() }
)
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyScrollTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyScrollTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyScrollTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyScrollTest.kt
index dc794c5..f7fd6c7 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazyScrollTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyScrollTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.animation.core.FloatSpringSpec
import androidx.compose.foundation.gestures.animateScrollBy
@@ -66,7 +66,7 @@
containerSizeDp = itemSizeDp * 3
}
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberTvLazyGridState()
scope = rememberCoroutineScope()
TestContent()
}
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazySemanticsTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazySemanticsTest.kt
similarity index 97%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazySemanticsTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazySemanticsTest.kt
index 68c75ee..850aa91 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/LazySemanticsTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazySemanticsTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
@@ -82,7 +82,7 @@
@Test
fun itemsSemantics_verticalGrid() {
rule.setContent {
- val state = rememberLazyGridState()
+ val state = rememberTvLazyGridState()
TvLazyVerticalGrid(TvGridCells.Fixed(1), LazyGridModifier, state) {
items(items = List(N) { it }, key = { key(it) }) {
SpacerInColumn(it)
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/TvLazyGridLayoutInfoTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/TvLazyGridLayoutInfoTest.kt
similarity index 94%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/TvLazyGridLayoutInfoTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/TvLazyGridLayoutInfoTest.kt
index d2bee39..c1ef2fb9e 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/grid/TvLazyGridLayoutInfoTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/TvLazyGridLayoutInfoTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.grid
+package androidx.tv.foundation.lazy.grid
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
@@ -77,7 +77,7 @@
rule.setContent {
LazyGrid(
cells = 2,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
reverseLayout = reverseLayout,
modifier = Modifier.axisSize(gridWidthDp, itemSizeDp * 3.5f),
) {
@@ -98,7 +98,7 @@
rule.setContent {
LazyGrid(
cells = 2,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
reverseLayout = reverseLayout,
modifier = Modifier.axisSize(gridWidthDp, itemSizeDp * 3.5f),
) {
@@ -123,7 +123,7 @@
rule.setContent {
LazyGrid(
cells = 1,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
reverseLayout = reverseLayout,
mainAxisSpacedBy = itemSizeDp,
modifier = Modifier.axisSize(itemSizeDp, itemSizeDp * 3.5f),
@@ -150,7 +150,7 @@
rule.setContent {
LazyGrid(
cells = 2,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
reverseLayout = reverseLayout,
modifier = Modifier.axisSize(itemSizeDp * 2f, itemSizeDp * 3.5f),
) {
@@ -190,7 +190,7 @@
cells = 1,
modifier = Modifier.crossAxisSize(itemSizeDp),
reverseLayout = reverseLayout,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
) {
item {
Box(Modifier.size(size))
@@ -232,7 +232,7 @@
LazyGrid(
cells = 2,
reverseLayout = reverseLayout,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
) {
items((0 until count).toList()) {
Box(Modifier.mainAxisSize(10.dp))
@@ -260,7 +260,7 @@
cells = 2,
modifier = Modifier.axisSize(sizeDp * 2, sizeDp),
reverseLayout = reverseLayout,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
) {
items((0..7).toList()) {
Box(Modifier.mainAxisSize(sizeDp))
@@ -305,7 +305,7 @@
afterContentCrossAxis = 2.dp
),
reverseLayout = reverseLayout,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
) {
items((0..7).toList()) {
Box(Modifier.mainAxisSize(sizeDp))
@@ -333,7 +333,7 @@
rule.setContent {
LazyGrid(
cells = 2,
- state = rememberLazyGridState().also { state = it }
+ state = rememberTvLazyGridState().also { state = it }
) {
item { Box(Modifier) }
item { }
@@ -364,7 +364,7 @@
LazyGrid(
cells = 1,
modifier = Modifier.mainAxisSize(sizeDp).crossAxisSize(sizeDp * 2),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
reverseLayout = reverseLayout,
contentPadding = PaddingValues(
beforeContent = beforeContentPaddingDp,
@@ -402,7 +402,7 @@
LazyGrid(
cells = 1,
modifier = Modifier.mainAxisSize(sizeDp).crossAxisSize(sizeDp * 2),
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
reverseLayout = reverseLayout,
contentPadding = PaddingValues(
beforeContent = beforeContentPaddingDp,
@@ -432,7 +432,7 @@
rule.setContent {
LazyGrid(
cells = 2,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
reverseLayout = reverseLayout,
modifier = Modifier.width(gridWidthDp).height(itemSizeDp * 3.5f),
) {
@@ -453,7 +453,7 @@
rule.setContent {
LazyGrid(
cells = 2,
- state = rememberLazyGridState().also { state = it },
+ state = rememberTvLazyGridState().also { state = it },
reverseLayout = reverseLayout,
modifier = Modifier.axisSize(gridWidthDp, itemSizeDp * 3.5f),
) {
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/BaseLazyListTestWithOrientation.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/BaseLazyListTestWithOrientation.kt
similarity index 97%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/BaseLazyListTestWithOrientation.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/BaseLazyListTestWithOrientation.kt
index 9ab4802..d451964 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/BaseLazyListTestWithOrientation.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/BaseLazyListTestWithOrientation.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.animation.core.snap
import androidx.compose.foundation.gestures.Orientation
@@ -75,7 +75,7 @@
this.fillMaxHeight()
}
- fun LazyItemScope.fillParentMaxCrossAxis() =
+ fun TvLazyListItemScope.fillParentMaxCrossAxis() =
if (vertical) {
Modifier.fillParentMaxWidth()
} else {
@@ -178,7 +178,7 @@
@Composable
fun LazyColumnOrRow(
modifier: Modifier = Modifier,
- state: TvLazyListState = rememberLazyListState(),
+ state: TvLazyListState = rememberTvLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
userScrollEnabled: Boolean = true,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyArrangementsTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyArrangementsTest.kt
similarity index 97%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyArrangementsTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyArrangementsTest.kt
index f960ffa..9285bd1 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyArrangementsTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyArrangementsTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.scrollBy
@@ -303,7 +303,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyColumn(
Modifier.size(itemSize * 3),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
verticalArrangement = Arrangement.spacedBy(spacingSize),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
@@ -340,7 +340,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyColumn(
Modifier.size(itemSize * 3),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
verticalArrangement = Arrangement.spacedBy(spacingSize),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
@@ -378,7 +378,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyRow(
Modifier.size(itemSize * 3),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
horizontalArrangement = Arrangement.spacedBy(spacingSize),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
@@ -415,7 +415,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyRow(
Modifier.size(itemSize * 3),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
horizontalArrangement = Arrangement.spacedBy(spacingSize),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyColumnTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyColumnTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyColumnTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyColumnTest.kt
index 8e8bc51..45825f43 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyColumnTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyColumnTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import android.os.Build
import androidx.tv.foundation.lazy.AutoTestFrameClock
@@ -218,7 +218,7 @@
var count by mutableStateOf(100)
val composedIndexes = mutableListOf<Int>()
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.fillMaxWidth().height(10.dp),
state,
@@ -410,7 +410,7 @@
fun scrolledAwayItemIsNotDisplayedAnymore() {
lateinit var state: TvLazyListState
rule.setContentWithTestViewConfiguration {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier
.requiredSize(10.dp)
@@ -456,7 +456,7 @@
fun wrappedNestedLazyRowDisplayCorrectContent() {
lateinit var state: TvLazyListState
rule.setContentWithTestViewConfiguration {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.size(20.dp),
state = state,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyCustomKeysTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyCustomKeysTest.kt
similarity index 96%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyCustomKeysTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyCustomKeysTest.kt
index 69d0123..b3c8a3b 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyCustomKeysTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyCustomKeysTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Spacer
@@ -262,7 +262,7 @@
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
state = state,
pivotOffsets = PivotOffsets(parentFraction = 0f)) {
@@ -285,7 +285,7 @@
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
state = state,
pivotOffsets = PivotOffsets(parentFraction = 0f)
@@ -313,7 +313,7 @@
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.size(itemSize * 2.5f),
state,
@@ -340,7 +340,7 @@
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.size(itemSize * 2.5f),
state,
@@ -370,7 +370,7 @@
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState(5)
+ state = rememberTvLazyListState(5)
TvLazyColumn(
Modifier.size(itemSize * 2.5f),
state,
@@ -400,7 +400,7 @@
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState(10) // key 20 is the first item
+ state = rememberTvLazyListState(10) // key 20 is the first item
TvLazyColumn(
Modifier.size(itemSize * 2.5f),
state,
@@ -430,7 +430,7 @@
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState(5)
+ state = rememberTvLazyListState(5)
TvLazyColumn(
Modifier.size(itemSize * 2.5f),
state,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyItemStateRestoration.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyItemStateRestoration.kt
similarity index 96%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyItemStateRestoration.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyItemStateRestoration.kt
index 6a037fb..15c520a 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyItemStateRestoration.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyItemStateRestoration.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
@@ -89,7 +89,7 @@
restorationTester.setContent {
TvLazyColumn(
Modifier.requiredSize(20.dp),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
items((0..10).toList()) {
@@ -140,7 +140,7 @@
restorationTester.setContent {
TvLazyColumn(
Modifier.requiredSize(20.dp),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
items((0..1).toList()) {
@@ -190,7 +190,7 @@
restorationTester.setContent {
TvLazyColumn(
Modifier.requiredSize(20.dp),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
items((0..10).toList()) {
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListAnimateItemPlacementTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListAnimateItemPlacementTest.kt
similarity index 96%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListAnimateItemPlacementTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListAnimateItemPlacementTest.kt
index 351bf6f..07b5201 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListAnimateItemPlacementTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListAnimateItemPlacementTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.LinearEasing
@@ -111,7 +111,7 @@
rule.setContent {
LazyList {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -137,7 +137,7 @@
rule.setContent {
LazyList {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -160,7 +160,7 @@
rule.setContent {
LazyList {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -195,7 +195,7 @@
rule.setContent {
LazyList {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -233,7 +233,7 @@
maxSize = itemSizeDp * 5
) {
items(listOf(0, 1, 2, 3), key = { it }) {
- Item(it, size = if (it == 1) size else itemSizeDp)
+ item(it, size = if (it == 1) size else itemSizeDp)
}
}
}
@@ -275,7 +275,7 @@
rule.setContent {
LazyList {
items(list, key = { it }) {
- Item(it, animSpec = if (it == 1 || it == 3) AnimSpec else null)
+ item(it, animSpec = if (it == 1 || it == 3) AnimSpec else null)
}
}
}
@@ -303,7 +303,7 @@
LazyList {
items(list, key = { it }) {
val duration = if (it == 1 || it == 3) Duration * 2 else Duration
- Item(it, animSpec = tween(duration.toInt(), easing = LinearEasing))
+ item(it, animSpec = tween(duration.toInt(), easing = LinearEasing))
}
}
}
@@ -331,8 +331,8 @@
rule.setContent {
LazyList {
items(list, key = { it }) {
- Item(it)
- Item(it + 1)
+ item(it)
+ item(it + 1)
}
}
}
@@ -365,8 +365,8 @@
rule.setContent {
LazyList {
items(list, key = { it }) {
- Item(it)
- Item(it + 1, animSpec = null)
+ item(it)
+ item(it + 1, animSpec = null)
}
}
}
@@ -396,7 +396,7 @@
maxSize = itemSizeDp * 5
) {
items(listOf(1, 2, 3), key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -428,7 +428,7 @@
rule.setContent {
LazyList(maxSize = itemSizeDp * 3) {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -473,7 +473,7 @@
rule.setContent {
LazyList(maxSize = itemSizeDp * 3f, startIndex = 3) {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -518,7 +518,7 @@
rule.setContent {
LazyList(arrangement = Arrangement.spacedBy(spacingDp)) {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -547,7 +547,7 @@
arrangement = Arrangement.spacedBy(spacingDp)
) {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -599,7 +599,7 @@
arrangement = Arrangement.spacedBy(spacingDp)
) {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -648,7 +648,7 @@
items(list, key = { it }) {
val size =
if (it == 3) itemSize2Dp else if (it == 1) itemSize3Dp else itemSizeDp
- Item(it, size = size)
+ item(it, size = size)
}
}
}
@@ -708,7 +708,7 @@
items(list, key = { it }) {
val size =
if (it == 0) itemSize2Dp else if (it == 4) itemSize3Dp else itemSizeDp
- Item(it, size = size)
+ item(it, size = size)
}
}
}
@@ -769,7 +769,7 @@
items(listOf(1, 2, 3), key = { it }) {
val crossAxisSize =
if (it == 1) itemSizeDp else if (it == 2) itemSize2Dp else itemSize3Dp
- Item(it, crossAxisSize = crossAxisSize)
+ item(it, crossAxisSize = crossAxisSize)
}
}
}
@@ -821,7 +821,7 @@
listOf(1, 2, 3).forEach {
val crossAxisSize =
if (it == 1) itemSizeDp else if (it == 2) itemSize2Dp else itemSize3Dp
- Item(it, crossAxisSize = crossAxisSize)
+ item(it, crossAxisSize = crossAxisSize)
}
}
}
@@ -863,7 +863,7 @@
items(listOf(1, 2, 3), key = { it }) {
val crossAxisSize =
if (it == 1) itemSizeDp else if (it == 2) itemSize2Dp else itemSize3Dp
- Item(it, crossAxisSize = crossAxisSize)
+ item(it, crossAxisSize = crossAxisSize)
}
}
}
@@ -911,7 +911,7 @@
rule.setContent {
LazyList(startPadding = startPaddingDp, endPadding = endPaddingDp) {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -949,7 +949,7 @@
rule.setContent {
LazyList {
items(list, key = { it }) {
- Item(it)
+ item(it)
}
}
LaunchedEffect(Unit) {
@@ -982,7 +982,7 @@
rule.setContent {
LazyList(maxSize = itemSizeDp * 3) {
items(listOf(0, 1, 2, 3, 4, 5, 6, 7), key = { it }) {
- Item(it)
+ item(it)
}
}
}
@@ -1098,7 +1098,7 @@
endPadding: Dp = 0.dp,
content: TvLazyListScope.() -> Unit
) {
- state = rememberLazyListState(startIndex)
+ state = rememberTvLazyListState(startIndex)
if (isVertical) {
val verticalArrangement =
arrangement ?: if (!reverseLayout) Arrangement.Top else Arrangement.Bottom
@@ -1161,7 +1161,7 @@
}
@Composable
- private fun LazyItemScope.Item(
+ private fun TvLazyListItemScope.item(
tag: Int,
size: Dp = itemSizeDp,
crossAxisSize: Dp = size,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListLayoutInfoTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListLayoutInfoTest.kt
similarity index 92%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListLayoutInfoTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListLayoutInfoTest.kt
index 5e39177..cf2e225 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListLayoutInfoTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListLayoutInfoTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
@@ -71,7 +71,7 @@
lateinit var state: TvLazyListState
rule.setContent {
LazyColumnOrRow(
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
reverseLayout = reverseLayout,
modifier = Modifier.requiredSize(itemSizeDp * 3.5f)
) {
@@ -91,7 +91,7 @@
lateinit var state: TvLazyListState
rule.setContent {
LazyColumnOrRow(
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
reverseLayout = reverseLayout,
modifier = Modifier.requiredSize(itemSizeDp * 3.5f)
) {
@@ -114,7 +114,7 @@
lateinit var state: TvLazyListState
rule.setContent {
LazyColumnOrRow(
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
reverseLayout = reverseLayout,
spacedBy = itemSizeDp,
modifier = Modifier.requiredSize(itemSizeDp * 3.5f)
@@ -131,16 +131,16 @@
}
@Composable
- fun ObservingFun(state: TvLazyListState, currentInfo: StableRef<LazyListLayoutInfo?>) {
+ fun ObservingFun(state: TvLazyListState, currentInfo: StableRef<TvLazyListLayoutInfo?>) {
currentInfo.value = state.layoutInfo
}
@Test
fun visibleItemsAreObservableWhenWeScroll() {
lateinit var state: TvLazyListState
- val currentInfo = StableRef<LazyListLayoutInfo?>(null)
+ val currentInfo = StableRef<TvLazyListLayoutInfo?>(null)
rule.setContent {
LazyColumnOrRow(
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
reverseLayout = reverseLayout,
modifier = Modifier.requiredSize(itemSizeDp * 3.5f)
) {
@@ -169,7 +169,7 @@
fun visibleItemsAreObservableWhenResize() {
lateinit var state: TvLazyListState
var size by mutableStateOf(itemSizeDp * 2)
- var currentInfo: LazyListLayoutInfo? = null
+ var currentInfo: TvLazyListLayoutInfo? = null
@Composable
fun observingFun() {
currentInfo = state.layoutInfo
@@ -177,7 +177,7 @@
rule.setContent {
LazyColumnOrRow(
reverseLayout = reverseLayout,
- state = rememberLazyListState().also { state = it }
+ state = rememberTvLazyListState().also { state = it }
) {
item {
Box(Modifier.requiredSize(size))
@@ -206,7 +206,7 @@
rule.setContent {
LazyColumnOrRow(
reverseLayout = reverseLayout,
- state = rememberLazyListState().also { state = it }
+ state = rememberTvLazyListState().also { state = it }
) {
items((0 until count).toList()) {
Box(Modifier.requiredSize(10.dp))
@@ -233,7 +233,7 @@
LazyColumnOrRow(
Modifier.mainAxisSize(sizeDp).crossAxisSize(sizeDp * 2),
reverseLayout = reverseLayout,
- state = rememberLazyListState().also { state = it }
+ state = rememberTvLazyListState().also { state = it }
) {
items((0..3).toList()) {
Box(Modifier.requiredSize(sizeDp))
@@ -273,7 +273,7 @@
afterContentCrossAxis = 2.dp
),
reverseLayout = reverseLayout,
- state = rememberLazyListState().also { state = it }
+ state = rememberTvLazyListState().also { state = it }
) {
items((0..3).toList()) {
Box(Modifier.requiredSize(sizeDp))
@@ -296,7 +296,7 @@
lateinit var state: TvLazyListState
rule.setContent {
LazyColumnOrRow(
- state = rememberLazyListState().also { state = it }
+ state = rememberTvLazyListState().also { state = it }
) {
item { Box(Modifier) }
item { }
@@ -326,7 +326,7 @@
rule.setContent {
LazyColumnOrRow(
Modifier.mainAxisSize(sizeDp).crossAxisSize(sizeDp * 2),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
reverseLayout = reverseLayout,
contentPadding = PaddingValues(
beforeContent = beforeContentPaddingDp,
@@ -363,7 +363,7 @@
rule.setContent {
LazyColumnOrRow(
Modifier.mainAxisSize(sizeDp).crossAxisSize(sizeDp * 2),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
reverseLayout = reverseLayout,
contentPadding = PaddingValues(
beforeContent = beforeContentPaddingDp,
@@ -392,7 +392,7 @@
lateinit var state: TvLazyListState
rule.setContent {
LazyColumnOrRow(
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
reverseLayout = reverseLayout,
modifier = Modifier.requiredSize(itemSizeDp * 3.5f)
) {
@@ -412,7 +412,7 @@
lateinit var state: TvLazyListState
rule.setContent {
LazyColumnOrRow(
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSizeDp * 3.5f)
) {
items((0..5).toList()) {
@@ -427,7 +427,7 @@
}
}
- fun LazyListLayoutInfo.assertVisibleItems(
+ private fun TvLazyListLayoutInfo.assertVisibleItems(
count: Int,
startIndex: Int = 0,
startOffset: Int = 0,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListPrefetcherTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListPrefetcherTest.kt
similarity index 97%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListPrefetcherTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListPrefetcherTest.kt
index db1d248..14401a4 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListPrefetcherTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListPrefetcherTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.tv.foundation.lazy.AutoTestFrameClock
import androidx.compose.foundation.gestures.Orientation
@@ -290,7 +290,7 @@
) { constraints ->
val placeable = if (emit) {
subcompose(Unit) {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
LazyColumnOrRow(
Modifier.mainAxisSize(itemsSizeDp * 1.5f),
state,
@@ -342,7 +342,7 @@
contentPadding: PaddingValues = PaddingValues(0.dp)
) {
rule.setContent {
- state = rememberLazyListState(
+ state = rememberTvLazyListState(
initialFirstVisibleItemIndex = firstItem,
initialFirstVisibleItemScrollOffset = itemOffset
)
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListSlotsReuseTest.kt
similarity index 96%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListSlotsReuseTest.kt
index 7e0f810..3083be7 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListSlotsReuseTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
@@ -54,7 +54,7 @@
fun scroll1ItemScrolledOffItemIsKeptForReuse() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.height(itemsSizeDp * 1.5f),
state,
@@ -88,7 +88,7 @@
fun scroll2ItemsScrolledOffItemsAreKeptForReuse() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.height(itemsSizeDp * 1.5f),
state,
@@ -127,7 +127,7 @@
fun checkMaxItemsKeptForReuse() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.height(itemsSizeDp * (DefaultMaxItemsToRetain + 0.5f)),
state,
@@ -162,7 +162,7 @@
fun scroll3Items2OfScrolledOffItemsAreKeptForReuse() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.height(itemsSizeDp * 1.5f),
state,
@@ -215,7 +215,7 @@
fun doMultipleScrollsOneByOne() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.height(itemsSizeDp * 1.5f),
state,
@@ -261,7 +261,7 @@
fun scrollBackwardOnce() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState(10)
+ state = rememberTvLazyListState(10)
TvLazyColumn(
Modifier.height(itemsSizeDp * 1.5f),
state,
@@ -299,7 +299,7 @@
fun scrollBackwardOneByOne() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState(10)
+ state = rememberTvLazyListState(10)
TvLazyColumn(
Modifier.height(itemsSizeDp * 1.5f),
state,
@@ -340,7 +340,7 @@
var rememberedValue0 = -1
var rememberedValue1 = -1
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.height(itemsSizeDp * 1.5f),
state,
@@ -392,7 +392,7 @@
val visibleItemsCount = (DefaultMaxItemsToRetain + 1) * 2
val startOfType1 = DefaultMaxItemsToRetain + 1
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.height(itemsSizeDp * (visibleItemsCount - 0.5f)),
state,
@@ -445,7 +445,7 @@
fun differentTypesFromDifferentItemCalls() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyColumn(
Modifier.height(itemsSizeDp * 2.5f),
state,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt
similarity index 97%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt
index 6b61bf4..5438a7a 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import android.os.Build
import androidx.compose.foundation.background
@@ -762,7 +762,7 @@
val items by mutableStateOf((1..20).toList())
lateinit var state: TvLazyListState
rule.setContentWithTestViewConfiguration {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
state.prefetchingEnabled = false
LazyColumnOrRow(
Modifier.requiredSize(100.dp).testTag(LazyListTag),
@@ -792,7 +792,7 @@
val items by mutableStateOf((1..20).toList())
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
LazyColumnOrRow(
Modifier.requiredSize(100.dp).testTag(LazyListTag),
state = state
@@ -837,7 +837,7 @@
lateinit var state: TvLazyListState
val expectedOffset = with(rule.density) { 10.dp.roundToPx() }
rule.setContentWithTestViewConfiguration {
- state = rememberLazyListState(2, expectedOffset)
+ state = rememberTvLazyListState(2, expectedOffset)
LazyColumnOrRow(
Modifier.requiredSize(100.dp).testTag(LazyListTag),
state = state
@@ -862,7 +862,7 @@
val restorationTester = StateRestorationTester(rule)
var state: TvLazyListState? = null
restorationTester.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
LazyColumnOrRow(
Modifier.requiredSize(100.dp).testTag(LazyListTag),
state = state!!
@@ -893,7 +893,7 @@
fun snapToItemIndex() {
lateinit var state: TvLazyListState
rule.setContentWithTestViewConfiguration {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
LazyColumnOrRow(
Modifier.requiredSize(100.dp).testTag(LazyListTag),
state = state
@@ -991,7 +991,7 @@
rule.setContentWithTestViewConfiguration {
LazyColumnOrRow(
Modifier.mainAxisSize(itemSizeMinusOne).testTag(LazyListTag),
- state = rememberLazyListState().also { state = it }
+ state = rememberTvLazyListState().also { state = it }
) {
items(2) {
Spacer(
@@ -1019,7 +1019,7 @@
rule.setContentWithTestViewConfiguration {
LazyColumnOrRow(
Modifier.mainAxisSize(itemSize * 1.75f).testTag(LazyListTag),
- state = rememberLazyListState().also { state = it }
+ state = rememberTvLazyListState().also { state = it }
) {
items(items) {
Spacer(
@@ -1095,7 +1095,7 @@
var count by mutableStateOf(100)
val composedIndexes = mutableListOf<Int>()
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
LazyColumnOrRow(Modifier.fillMaxCrossAxis().mainAxisSize(10.dp), state) {
items(count) { index ->
composedIndexes.add(index)
@@ -1133,7 +1133,7 @@
Modifier
.testTag(LazyListTag)
.background(Color.Blue),
- state = rememberLazyListState(2, 5)
+ state = rememberTvLazyListState(2, 5)
) {
items(100) {
Box(
@@ -1171,7 +1171,7 @@
Modifier
.padding(20.dp)
.fillMaxSize(),
- rememberLazyListState(1)
+ rememberTvLazyListState(1)
) {
items(4) {
Box(Modifier.size(20.dp).drawOutsideOfBounds())
@@ -1200,7 +1200,7 @@
lateinit var state: TvLazyListState
var itemsCount by mutableStateOf(0)
rule.setContent {
- state = rememberLazyListState(2, 10)
+ state = rememberTvLazyListState(2, 10)
LazyColumnOrRow(Modifier.fillMaxSize(), state) {
items(itemsCount) {
Box(Modifier.size(20.dp))
@@ -1225,7 +1225,7 @@
val recomposeCounter = mutableStateOf(0)
val tester = StateRestorationTester(rule)
tester.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
LazyColumnOrRow(Modifier.fillMaxSize(), state) {
recomposeCounter.value
items(itemsCount) {
@@ -1260,7 +1260,7 @@
var target = 0
var reverse = false
rule.setContent {
- val listState = rememberLazyListState()
+ val listState = rememberTvLazyListState()
SideEffect {
state = listState
}
@@ -1313,7 +1313,7 @@
fun animateScrollToTheLastItemWhenItemsAreLargerThenTheScreen() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
LazyColumnOrRow(Modifier.crossAxisSize(150.dp).mainAxisSize(100.dp), state) {
items(20) {
Box(Modifier.size(150.dp))
@@ -1435,7 +1435,7 @@
rule.setContentWithTestViewConfiguration {
LazyColumnOrRow(
Modifier.mainAxisSize(itemSize * 3),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
) {
items(5) {
Spacer(
@@ -1503,7 +1503,7 @@
rule.setContentWithTestViewConfiguration {
LazyColumnOrRow(
Modifier.mainAxisSize(itemSize * 3),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
userScrollEnabled = false,
) {
items(5) {
@@ -1552,7 +1552,7 @@
val itemSize = with(rule.density) { 30.toDp() }
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
LazyColumnOrRow(
modifier = Modifier.mainAxisSize(itemSize + 1.dp),
state = state
@@ -1640,7 +1640,7 @@
lateinit var state: TvLazyListState
rule.setContentWithTestViewConfiguration {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
LazyColumnOrRow(
Modifier.composed {
recomposeCount++
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListsContentPaddingTest.kt
similarity index 95%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListsContentPaddingTest.kt
index eccaaed..cfead8b 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListsContentPaddingTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollBy
@@ -67,7 +67,7 @@
LazyColumnOrRow(
modifier = Modifier.requiredSize(containerSize)
.testTag(LazyListTag),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
contentPadding = PaddingValues(
mainAxis = largePaddingSize,
crossAxis = smallPaddingSize
@@ -104,7 +104,7 @@
LazyColumnOrRow(
modifier = Modifier.requiredSize(itemSize * 2)
.testTag(LazyListTag),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
contentPadding = PaddingValues(mainAxis = itemSize)
) {
items(listOf(1)) {
@@ -132,7 +132,7 @@
LazyColumnOrRow(
modifier = Modifier.requiredSize(padding * 2 + itemSize)
.testTag(LazyListTag),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
contentPadding = PaddingValues(mainAxis = padding)
) {
items((0..3).toList()) {
@@ -170,7 +170,7 @@
LazyColumnOrRow(
modifier = Modifier.requiredSize(itemSize + padding * 2)
.testTag(LazyListTag),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
contentPadding = PaddingValues(mainAxis = padding)
) {
items((0..3).toList()) {
@@ -202,7 +202,7 @@
LazyColumnOrRow(
modifier = Modifier.requiredSize(padding * 2 + itemSize)
.testTag(LazyListTag),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
contentPadding = PaddingValues(mainAxis = padding)
) {
items((0..3).toList()) {
@@ -243,7 +243,7 @@
LazyColumnOrRow(
modifier = Modifier.requiredSize(padding * 2 + itemSize)
.testTag(LazyListTag),
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
contentPadding = PaddingValues(mainAxis = padding)
) {
items((0..3).toList()) {
@@ -352,7 +352,7 @@
rule.setContentWithTestViewConfiguration {
LazyColumnOrRow(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(listSize),
contentPadding = PaddingValues(
beforeContent = topPadding,
@@ -386,7 +386,7 @@
fun overscrollWithContentPadding() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize + smallPaddingSize * 2)) {
LazyColumnOrRow(
state = state,
@@ -429,7 +429,7 @@
fun totalPaddingLargerParentSize_initialState() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
@@ -459,7 +459,7 @@
fun totalPaddingLargerParentSize_scrollByPadding() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
@@ -493,7 +493,7 @@
fun totalPaddingLargerParentSize_scrollToLastItem() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
@@ -527,7 +527,7 @@
fun totalPaddingLargerParentSize_scrollToLastItemByDelta() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
@@ -562,7 +562,7 @@
// the whole end content padding is displayed
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
@@ -593,7 +593,7 @@
fun eachPaddingLargerParentSize_initialState() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
@@ -620,7 +620,7 @@
fun eachPaddingLargerParentSize_scrollByPadding() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
@@ -654,7 +654,7 @@
fun eachPaddingLargerParentSize_scrollToLastItem() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
@@ -691,7 +691,7 @@
fun eachPaddingLargerParentSize_scrollToLastItemByDelta() {
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
@@ -729,7 +729,7 @@
// only the end content padding is displayed
lateinit var state: TvLazyListState
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) {
LazyColumnOrRow(
state = state,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListsIndexedTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListsIndexedTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListsIndexedTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListsIndexedTest.kt
index a868e08..9eab54b 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListsIndexedTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListsIndexedTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Spacer
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListsReverseLayoutTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListsReverseLayoutTest.kt
similarity index 95%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListsReverseLayoutTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListsReverseLayoutTest.kt
index 1798212..4ec7ab7 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyListsReverseLayoutTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListsReverseLayoutTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
@@ -105,7 +105,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyColumn(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
@@ -127,7 +127,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyColumn(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
@@ -157,7 +157,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyColumn(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag),
pivotOffsets = PivotOffsets(parentFraction = 0.3f)
) {
@@ -189,7 +189,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyColumn(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
@@ -264,7 +264,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyRow(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
@@ -286,7 +286,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyRow(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
@@ -316,7 +316,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyRow(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag),
pivotOffsets = PivotOffsets(parentFraction = 0.3f)
) {
@@ -348,7 +348,7 @@
rule.setContentWithTestViewConfiguration {
TvLazyRow(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag),
pivotOffsets = PivotOffsets(parentFraction = 0f)
) {
@@ -427,7 +427,7 @@
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
TvLazyRow(
reverseLayout = true,
- state = rememberLazyListState().also { state = it },
+ state = rememberTvLazyListState().also { state = it },
modifier = Modifier.requiredSize(itemSize * 2).testTag(ContainerTag),
pivotOffsets = PivotOffsets(parentFraction = 0.3f)
) {
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyNestedScrollingTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyNestedScrollingTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyNestedScrollingTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyNestedScrollingTest.kt
index c79c0f8..02ba39f 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyNestedScrollingTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyNestedScrollingTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.ScrollableState
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyRowTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyRowTest.kt
similarity index 96%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyRowTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyRowTest.kt
index 13bfd51..0817a21 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyRowTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyRowTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
@@ -126,7 +126,7 @@
rule.setContentWithTestViewConfiguration {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
Box(Modifier.width(100.dp)) {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
TvLazyRow(
Modifier.testTag(LazyListTag),
state,
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyScrollTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyScrollTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyScrollTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyScrollTest.kt
index e8416f6..f5b44f4 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazyScrollTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyScrollTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.animation.core.FloatSpringSpec
import androidx.tv.foundation.lazy.AutoTestFrameClock
@@ -70,7 +70,7 @@
containerSizeDp = itemSizeDp * 3
}
rule.setContent {
- state = rememberLazyListState()
+ state = rememberTvLazyListState()
scope = rememberCoroutineScope()
TestContent()
}
diff --git a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazySemanticsTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazySemanticsTest.kt
similarity index 98%
rename from tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazySemanticsTest.kt
rename to tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazySemanticsTest.kt
index 2ac1492..328d9ca 100644
--- a/tv/tv-foundation/src/androidAndroidTest/kotlin/androidx/tv/compose/foundation/lazy/list/LazySemanticsTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazySemanticsTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.tv.compose.foundation.lazy.list
+package androidx.tv.foundation.lazy.list
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
diff --git a/wear/watchface/watchface-client-guava/lint-baseline.xml b/wear/watchface/watchface-client-guava/lint-baseline.xml
deleted file mode 100644
index a4e3c85..0000000
--- a/wear/watchface/watchface-client-guava/lint-baseline.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.4.0-alpha08" type="baseline" client="gradle" dependencies="false" name="AGP (7.4.0-alpha08)" variant="all" version="7.4.0-alpha08">
-
- <issue
- id="NewApi"
- message="Call requires API level 27 (current min is 26): `WatchFaceControlService`"
- errorLine1=" private val realService = object : WatchFaceControlService() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Extending WatchFaceControlService requires API level 27 (current min is 26): `WatchFaceControlService`"
- errorLine1=" private val realService = object : WatchFaceControlService() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt"/>
- </issue>
-
-</issues>
diff --git a/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt b/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt
index 05be0f5..43b28e3 100644
--- a/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt
+++ b/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt
@@ -22,7 +22,9 @@
import android.content.Context
import android.content.Intent
import android.content.res.XmlResourceParser
+import android.os.Build
import android.os.IBinder
+import androidx.annotation.RequiresApi
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -43,6 +45,7 @@
* [ListenableWatchFaceMetadataClientTest] and to optionally override the reported API version.
*/
public class WatchFaceControlTestService : Service() {
+ @RequiresApi(Build.VERSION_CODES.O_MR1)
private val realService = object : WatchFaceControlService() {
@SuppressLint("NewApi")
override fun createServiceStub(): IWatchFaceInstanceServiceStub =
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt
index 263c5bc..905b056a 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Type.kt
@@ -85,6 +85,9 @@
PHOTO_IMAGE.wireType -> PHOTO_IMAGE
NO_PERMISSION.wireType -> NO_PERMISSION
PROTO_LAYOUT.wireType -> PROTO_LAYOUT
+ GOAL_PROGRESS.wireType -> GOAL_PROGRESS
+ DISCRETE_RANGED_VALUE.wireType -> DISCRETE_RANGED_VALUE
+ WEIGHTED_ELEMENTS.wireType -> WEIGHTED_ELEMENTS
LIST.wireType -> LIST
else -> EMPTY
}
diff --git a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
index 666a11c..52b49d5 100644
--- a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
+++ b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
@@ -38,8 +38,6 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
-import org.junit.Assume.assumeTrue
-import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -81,11 +79,6 @@
watchFaceEditorData = WatchFaceEditorData(wfIcon4)
)
- @Before
- public fun setUp() {
- assumeTrue("These tests require API 28", Build.VERSION.SDK_INT >= 28)
- }
-
@Test
public fun parcelAndUnparcelStyleSettingAndOption() {
val settingIcon = Icon.createWithContentUri("settingIcon")
diff --git a/wear/watchface/watchface/build.gradle b/wear/watchface/watchface/build.gradle
index b755288..ccdc3b9 100644
--- a/wear/watchface/watchface/build.gradle
+++ b/wear/watchface/watchface/build.gradle
@@ -25,7 +25,7 @@
}
dependencies {
- api(project(":annotation:annotation"))
+ api("androidx.annotation:annotation:1.5.0-alpha01")
api("androidx.fragment:fragment:1.2.0")
api(project(":wear:watchface:watchface-complications-data"))
api(project(":wear:watchface:watchface-data"))
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java
index 3055080..271594a 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java
@@ -17,6 +17,7 @@
package androidx.work.impl.foreground;
import android.Manifest;
+import android.app.ForegroundServiceStartNotAllowedException;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
@@ -125,7 +126,10 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ Api31Impl.startForeground(SystemForegroundService.this, notificationId,
+ notification, notificationType);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Api29Impl.startForeground(SystemForegroundService.this, notificationId,
notification, notificationType);
} else {
@@ -176,4 +180,24 @@
service.startForeground(id, notification, foregroundServiceType);
}
}
+
+ @RequiresApi(31)
+ static class Api31Impl {
+ private Api31Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void startForeground(Service service, int id, Notification notification,
+ int foregroundServiceType) {
+ try {
+ service.startForeground(id, notification, foregroundServiceType);
+ } catch (ForegroundServiceStartNotAllowedException exception) {
+ // This should ideally never happen. But there a chance that this method
+ // is called, and the app is no longer in a state where it's possible to start a
+ // foreground service. WorkManager will eventually call stop() to clean up.
+ Logger.get().warning(TAG, "Unable to start foreground service", exception);
+ }
+ }
+ }
}