Merge "Revert "Fix Text alignment issue when ellipsize, letter spacing ..."" into androidx-main
diff --git a/activity/activity-compose/build.gradle b/activity/activity-compose/build.gradle
index b7f49ecc..4dd01f4 100644
--- a/activity/activity-compose/build.gradle
+++ b/activity/activity-compose/build.gradle
@@ -54,7 +54,7 @@
androidx {
name = "Activity Compose"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose integration with Activity"
metalavaK2UastEnabled = true
diff --git a/activity/activity-ktx/build.gradle b/activity/activity-ktx/build.gradle
index 2e41cd9..6cc0206 100644
--- a/activity/activity-ktx/build.gradle
+++ b/activity/activity-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
@@ -59,7 +59,7 @@
androidx {
name = "Activity Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'activity' artifact"
metalavaK2UastEnabled = true
diff --git a/activity/activity/lint-baseline.xml b/activity/activity/lint-baseline.xml
new file mode 100644
index 0000000..242db20
--- /dev/null
+++ b/activity/activity/lint-baseline.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/activity/result/contract/ActivityResultContracts.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT > 19) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/activity/ComponentActivity.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT == 19 && ContextCompat.checkSelfPermission("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/activity/ComponentActivity.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/activity/EdgeToEdge.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/activity/EdgeToEdge.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/activity/EdgeToEdge.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/activity/EdgeToEdge.kt"/>
+ </issue>
+
+</issues>
diff --git a/activity/integration-tests/testapp/lint-baseline.xml b/activity/integration-tests/testapp/lint-baseline.xml
new file mode 100644
index 0000000..8174ad3
--- /dev/null
+++ b/activity/integration-tests/testapp/lint-baseline.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/activity/integration/testapp/EdgeToEdgeActivity.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="src/main/res/values-v21"/>
+ </issue>
+
+</issues>
diff --git a/appactions/interaction/interaction-proto/build.gradle b/appactions/interaction/interaction-proto/build.gradle
index 72a6b56..72ed0dc 100644
--- a/appactions/interaction/interaction-proto/build.gradle
+++ b/appactions/interaction/interaction-proto/build.gradle
@@ -77,7 +77,6 @@
android {
namespace "androidx.appactions.interaction.proto"
defaultConfig {
- minSdkVersion 19
}
libraryVariants.all { variant ->
// Replace the default jar with the shadow jar in the AAR.
diff --git a/appcompat/appcompat-lint/integration-tests/lint-baseline.xml b/appcompat/appcompat-lint/integration-tests/lint-baseline.xml
index a248bc9..f568c6a 100644
--- a/appcompat/appcompat-lint/integration-tests/lint-baseline.xml
+++ b/appcompat/appcompat-lint/integration-tests/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="ClassVerificationFailure"
@@ -20,60 +20,6 @@
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class com.example.android.appcompat.AppCompatLintDemo is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setActionBar(new Toolbar(this));"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/com/example/android/appcompat/AppCompatLintDemo.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class com.example.android.appcompat.AppCompatLintDemo is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setActionBar(new Toolbar(this));"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/com/example/android/appcompat/AppCompatLintDemo.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class com.example.android.appcompat.AppCompatLintDemoExt is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setActionBar(new Toolbar(this));"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/com/example/android/appcompat/AppCompatLintDemoExt.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class com.example.android.appcompat.AppCompatLintDemoExt is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setActionBar(new Toolbar(this));"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/com/example/android/appcompat/AppCompatLintDemoExt.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class com.example.android.appcompat.CoreActivityExt is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setActionBar(new Toolbar(this));"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/com/example/android/appcompat/CoreActivityExt.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class com.example.android.appcompat.CoreActivityExt is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setActionBar(new Toolbar(this));"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/com/example/android/appcompat/CoreActivityExt.java"/>
- </issue>
-
- <issue
id="UseAndroidAlpha"
message="Must use `android:alpha` if `app:alpha` is used"
errorLine1=" <item app:alpha="?android:disabledAlpha""
@@ -101,6 +47,33 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/android/appcompat/AppCompatLintDemo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/android/appcompat/AppCompatLintDemoExt.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/android/appcompat/CoreActivityExt.java"/>
+ </issue>
+
+ <issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public void myButtonClick(View view) {"
diff --git a/appcompat/appcompat-resources/api/1.7.0-beta01.txt b/appcompat/appcompat-resources/api/1.7.0-beta01.txt
new file mode 100644
index 0000000..58fa6a3
--- /dev/null
+++ b/appcompat/appcompat-resources/api/1.7.0-beta01.txt
@@ -0,0 +1,57 @@
+// Signature format: 4.0
+package androidx.appcompat.content.res {
+
+ public final class AppCompatResources {
+ method public static android.content.res.ColorStateList! getColorStateList(android.content.Context, @ColorRes int);
+ method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+ }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+ public class AnimatedStateListDrawableCompat extends androidx.appcompat.graphics.drawable.StateListDrawableCompat {
+ ctor public AnimatedStateListDrawableCompat();
+ method public void addState(int[], android.graphics.drawable.Drawable, int);
+ method public <T extends android.graphics.drawable.Drawable & android.graphics.drawable.Animatable> void addTransition(int, int, T, boolean);
+ method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat? create(android.content.Context, @DrawableRes int, android.content.res.Resources.Theme?);
+ method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public class DrawableContainerCompat extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+ ctor public DrawableContainerCompat();
+ method public void draw(android.graphics.Canvas);
+ method public final android.graphics.drawable.Drawable.ConstantState! getConstantState();
+ method public int getOpacity();
+ method public void invalidateDrawable(android.graphics.drawable.Drawable);
+ method public void scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long);
+ method public void setAlpha(int);
+ method public void setColorFilter(android.graphics.ColorFilter!);
+ method public void setDither(boolean);
+ method public void setEnterFadeDuration(int);
+ method public void setExitFadeDuration(int);
+ method public void unscheduleDrawable(android.graphics.drawable.Drawable, Runnable);
+ }
+
+ public class DrawableWrapperCompat extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+ ctor public DrawableWrapperCompat(android.graphics.drawable.Drawable!);
+ method public void draw(android.graphics.Canvas);
+ method public android.graphics.drawable.Drawable? getDrawable();
+ method public int getOpacity();
+ method public void invalidateDrawable(android.graphics.drawable.Drawable!);
+ method public void scheduleDrawable(android.graphics.drawable.Drawable!, Runnable!, long);
+ method public void setAlpha(int);
+ method public void setColorFilter(android.graphics.ColorFilter!);
+ method public void setDither(boolean);
+ method public void setDrawable(android.graphics.drawable.Drawable?);
+ method public void unscheduleDrawable(android.graphics.drawable.Drawable!, Runnable!);
+ }
+
+ public class StateListDrawableCompat extends androidx.appcompat.graphics.drawable.DrawableContainerCompat {
+ ctor public StateListDrawableCompat();
+ method public void addState(int[]!, android.graphics.drawable.Drawable!);
+ method public void inflate(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+}
+
diff --git a/graphics/graphics-shapes/api/res-1.0.0-beta02.txt b/appcompat/appcompat-resources/api/res-1.7.0-beta01.txt
similarity index 100%
copy from graphics/graphics-shapes/api/res-1.0.0-beta02.txt
copy to appcompat/appcompat-resources/api/res-1.7.0-beta01.txt
diff --git a/appcompat/appcompat-resources/api/restricted_1.7.0-beta01.txt b/appcompat/appcompat-resources/api/restricted_1.7.0-beta01.txt
new file mode 100644
index 0000000..4b180e0
--- /dev/null
+++ b/appcompat/appcompat-resources/api/restricted_1.7.0-beta01.txt
@@ -0,0 +1,111 @@
+// Signature format: 4.0
+package androidx.appcompat.content.res {
+
+ public final class AppCompatResources {
+ method public static android.content.res.ColorStateList! getColorStateList(android.content.Context, @ColorRes int);
+ method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+ }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+ public class AnimatedStateListDrawableCompat extends androidx.appcompat.graphics.drawable.StateListDrawableCompat implements androidx.core.graphics.drawable.TintAwareDrawable {
+ ctor public AnimatedStateListDrawableCompat();
+ method public void addState(int[], android.graphics.drawable.Drawable, int);
+ method public <T extends android.graphics.drawable.Drawable & android.graphics.drawable.Animatable> void addTransition(int, int, T, boolean);
+ method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat? create(android.content.Context, @DrawableRes int, android.content.res.Resources.Theme?);
+ method public static androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public class DrawableContainerCompat extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+ ctor public DrawableContainerCompat();
+ method public void draw(android.graphics.Canvas);
+ method public final android.graphics.drawable.Drawable.ConstantState! getConstantState();
+ method public int getOpacity();
+ method public void invalidateDrawable(android.graphics.drawable.Drawable);
+ method public void scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long);
+ method public void setAlpha(int);
+ method public void setColorFilter(android.graphics.ColorFilter!);
+ method public void setDither(boolean);
+ method public void setEnterFadeDuration(int);
+ method public void setExitFadeDuration(int);
+ method public void unscheduleDrawable(android.graphics.drawable.Drawable, Runnable);
+ }
+
+ public class DrawableWrapperCompat extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+ ctor public DrawableWrapperCompat(android.graphics.drawable.Drawable!);
+ method public void draw(android.graphics.Canvas);
+ method public android.graphics.drawable.Drawable? getDrawable();
+ method public int getOpacity();
+ method public void invalidateDrawable(android.graphics.drawable.Drawable!);
+ method public void scheduleDrawable(android.graphics.drawable.Drawable!, Runnable!, long);
+ method public void setAlpha(int);
+ method public void setColorFilter(android.graphics.ColorFilter!);
+ method public void setDither(boolean);
+ method public void setDrawable(android.graphics.drawable.Drawable?);
+ method public void unscheduleDrawable(android.graphics.drawable.Drawable!, Runnable!);
+ }
+
+ public class StateListDrawableCompat extends androidx.appcompat.graphics.drawable.DrawableContainerCompat {
+ ctor public StateListDrawableCompat();
+ method public void addState(int[]!, android.graphics.drawable.Drawable!);
+ method public void inflate(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+}
+
+package androidx.appcompat.widget {
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DrawableUtils {
+ method @Deprecated public static boolean canSafelyMutateDrawable(android.graphics.drawable.Drawable);
+ method public static android.graphics.Rect getOpticalBounds(android.graphics.drawable.Drawable);
+ method public static android.graphics.PorterDuff.Mode! parseTintMode(int, android.graphics.PorterDuff.Mode!);
+ field public static final android.graphics.Rect! INSETS_NONE;
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ResourceManagerInternal {
+ ctor public ResourceManagerInternal();
+ method public static androidx.appcompat.widget.ResourceManagerInternal! get();
+ method public android.graphics.drawable.Drawable! getDrawable(android.content.Context, @DrawableRes int);
+ method public static android.graphics.PorterDuffColorFilter! getPorterDuffColorFilter(int, android.graphics.PorterDuff.Mode!);
+ method public void onConfigurationChanged(android.content.Context);
+ method public void setHooks(androidx.appcompat.widget.ResourceManagerInternal.ResourceManagerHooks!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface ResourceManagerInternal.ResourceManagerHooks {
+ method public android.graphics.drawable.Drawable? createDrawableFor(androidx.appcompat.widget.ResourceManagerInternal, android.content.Context, @DrawableRes int);
+ method public android.content.res.ColorStateList? getTintListForDrawableRes(android.content.Context, @DrawableRes int);
+ method public android.graphics.PorterDuff.Mode? getTintModeForDrawableRes(int);
+ method public boolean tintDrawable(android.content.Context, @DrawableRes int, android.graphics.drawable.Drawable);
+ method public boolean tintDrawableUsingColorFilter(android.content.Context, @DrawableRes int, android.graphics.drawable.Drawable);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TintContextWrapper extends android.content.ContextWrapper {
+ method public static android.content.Context! wrap(android.content.Context);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TintInfo {
+ ctor public TintInfo();
+ field public boolean mHasTintList;
+ field public boolean mHasTintMode;
+ field public android.content.res.ColorStateList! mTintList;
+ field public android.graphics.PorterDuff.Mode! mTintMode;
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class VectorEnabledTintResources extends android.content.res.Resources {
+ ctor public VectorEnabledTintResources(android.content.Context, android.content.res.Resources);
+ method public int getColor(int) throws android.content.res.Resources.NotFoundException;
+ method public android.content.res.ColorStateList! getColorStateList(int) throws android.content.res.Resources.NotFoundException;
+ method public android.graphics.drawable.Drawable! getDrawable(int) throws android.content.res.Resources.NotFoundException;
+ method public android.graphics.drawable.Drawable! getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
+ method public android.graphics.Movie! getMovie(int) throws android.content.res.Resources.NotFoundException;
+ method public static boolean isCompatVectorFromResourcesEnabled();
+ method public static void setCompatVectorFromResourcesEnabled(boolean);
+ method public static boolean shouldBeUsed();
+ method public void updateConfiguration(android.content.res.Configuration!, android.util.DisplayMetrics!);
+ field public static final int MAX_SDK_WHERE_REQUIRED = 20; // 0x14
+ }
+
+}
+
diff --git a/appcompat/appcompat-resources/lint-baseline.xml b/appcompat/appcompat-resources/lint-baseline.xml
index 0447131..89fcbb1 100644
--- a/appcompat/appcompat-resources/lint-baseline.xml
+++ b/appcompat/appcompat-resources/lint-baseline.xml
@@ -1,14 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
-
- <issue
- id="NewApi"
- message="Call requires API level 21 (current min is 19): `android.graphics.drawable.Drawable#getColorFilter`"
- errorLine1=" assertNotNull(ld.getDrawable(0).getColorFilter());"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/appcompat/widget/TintResourcesTest.java"/>
- </issue>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanSynchronizedMethods"
@@ -101,6 +92,177 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/AnimatedStateListDrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/resources/Compatibility.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/DrawableContainerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21 && hotspotBounds != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/DrawableContainerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/DrawableContainerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/DrawableContainerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/DrawableContainerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/DrawableContainerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/DrawableContainerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ResourceManagerInternal.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ResourcesWrapper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ResourcesWrapper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/StateListDrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/StateListDrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/graphics/drawable/StateListDrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" return Build.VERSION.SDK_INT < 21 || VectorEnabledTintResources.shouldBeUsed();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/TintContextWrapper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" && Build.VERSION.SDK_INT <= MAX_SDK_WHERE_REQUIRED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/VectorEnabledTintResources.java"/>
+ </issue>
+
+ <issue
id="KotlinPropertyAccess"
message="The getter return type (`Theme`) and setter parameter type (`int`) getter and setter methods for property `theme` should have exactly the same type to allow be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
errorLine1=" public Resources.Theme getTheme() {"
diff --git a/appcompat/appcompat/api/1.7.0-beta01.txt b/appcompat/appcompat/api/1.7.0-beta01.txt
new file mode 100644
index 0000000..c7fd678
--- /dev/null
+++ b/appcompat/appcompat/api/1.7.0-beta01.txt
@@ -0,0 +1,1063 @@
+// Signature format: 4.0
+package androidx.appcompat.app {
+
+ public abstract class ActionBar {
+ ctor public ActionBar();
+ method public abstract void addOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+ method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!);
+ method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, boolean);
+ method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int);
+ method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int, boolean);
+ method public abstract android.view.View! getCustomView();
+ method public abstract int getDisplayOptions();
+ method public float getElevation();
+ method public abstract int getHeight();
+ method public int getHideOffset();
+ method @Deprecated public abstract int getNavigationItemCount();
+ method @Deprecated public abstract int getNavigationMode();
+ method @Deprecated public abstract int getSelectedNavigationIndex();
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab? getSelectedTab();
+ method public abstract CharSequence? getSubtitle();
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! getTabAt(int);
+ method @Deprecated public abstract int getTabCount();
+ method public android.content.Context! getThemedContext();
+ method public abstract CharSequence? getTitle();
+ method public abstract void hide();
+ method public boolean isHideOnContentScrollEnabled();
+ method public abstract boolean isShowing();
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! newTab();
+ method @Deprecated public abstract void removeAllTabs();
+ method public abstract void removeOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+ method @Deprecated public abstract void removeTab(androidx.appcompat.app.ActionBar.Tab!);
+ method @Deprecated public abstract void removeTabAt(int);
+ method @Deprecated public abstract void selectTab(androidx.appcompat.app.ActionBar.Tab!);
+ method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public abstract void setCustomView(android.view.View!);
+ method public abstract void setCustomView(android.view.View!, androidx.appcompat.app.ActionBar.LayoutParams!);
+ method public abstract void setCustomView(int);
+ method public abstract void setDisplayHomeAsUpEnabled(boolean);
+ method public abstract void setDisplayOptions(int);
+ method public abstract void setDisplayOptions(int, int);
+ method public abstract void setDisplayShowCustomEnabled(boolean);
+ method public abstract void setDisplayShowHomeEnabled(boolean);
+ method public abstract void setDisplayShowTitleEnabled(boolean);
+ method public abstract void setDisplayUseLogoEnabled(boolean);
+ method public void setElevation(float);
+ method public void setHideOffset(int);
+ method public void setHideOnContentScrollEnabled(boolean);
+ method public void setHomeActionContentDescription(@StringRes int);
+ method public void setHomeActionContentDescription(CharSequence?);
+ method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable?);
+ method public void setHomeAsUpIndicator(@DrawableRes int);
+ method public void setHomeButtonEnabled(boolean);
+ method public abstract void setIcon(android.graphics.drawable.Drawable!);
+ method public abstract void setIcon(@DrawableRes int);
+ method @Deprecated public abstract void setListNavigationCallbacks(android.widget.SpinnerAdapter!, androidx.appcompat.app.ActionBar.OnNavigationListener!);
+ method public abstract void setLogo(android.graphics.drawable.Drawable!);
+ method public abstract void setLogo(@DrawableRes int);
+ method @Deprecated public abstract void setNavigationMode(int);
+ method @Deprecated public abstract void setSelectedNavigationItem(int);
+ method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable!);
+ method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable!);
+ method public abstract void setSubtitle(int);
+ method public abstract void setSubtitle(CharSequence!);
+ method public abstract void setTitle(@StringRes int);
+ method public abstract void setTitle(CharSequence!);
+ method public abstract void show();
+ field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+ field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+ field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+ field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+ field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+ field @Deprecated public static final int NAVIGATION_MODE_LIST = 1; // 0x1
+ field @Deprecated public static final int NAVIGATION_MODE_STANDARD = 0; // 0x0
+ field @Deprecated public static final int NAVIGATION_MODE_TABS = 2; // 0x2
+ }
+
+ public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+ ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+ ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public ActionBar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+ ctor public ActionBar.LayoutParams(int);
+ ctor public ActionBar.LayoutParams(int, int);
+ ctor public ActionBar.LayoutParams(int, int, int);
+ field public int gravity;
+ }
+
+ public static interface ActionBar.OnMenuVisibilityListener {
+ method public void onMenuVisibilityChanged(boolean);
+ }
+
+ @Deprecated public static interface ActionBar.OnNavigationListener {
+ method @Deprecated public boolean onNavigationItemSelected(int, long);
+ }
+
+ @Deprecated public abstract static class ActionBar.Tab {
+ ctor @Deprecated public ActionBar.Tab();
+ method @Deprecated public abstract CharSequence! getContentDescription();
+ method @Deprecated public abstract android.view.View! getCustomView();
+ method @Deprecated public abstract android.graphics.drawable.Drawable! getIcon();
+ method @Deprecated public abstract int getPosition();
+ method @Deprecated public abstract Object! getTag();
+ method @Deprecated public abstract CharSequence! getText();
+ method @Deprecated public abstract void select();
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(@StringRes int);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(CharSequence!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(android.view.View!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(int);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(android.graphics.drawable.Drawable!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(@DrawableRes int);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTabListener(androidx.appcompat.app.ActionBar.TabListener!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTag(Object!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(int);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(CharSequence!);
+ field @Deprecated public static final int INVALID_POSITION = -1; // 0xffffffff
+ }
+
+ @Deprecated public static interface ActionBar.TabListener {
+ method @Deprecated public void onTabReselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+ method @Deprecated public void onTabSelected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+ method @Deprecated public void onTabUnselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+ }
+
+ public class ActionBarDrawerToggle implements androidx.drawerlayout.widget.DrawerLayout.DrawerListener {
+ ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, androidx.appcompat.widget.Toolbar!, @StringRes int, @StringRes int);
+ ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, @StringRes int, @StringRes int);
+ method public androidx.appcompat.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+ method public android.view.View.OnClickListener! getToolbarNavigationClickListener();
+ method public boolean isDrawerIndicatorEnabled();
+ method public boolean isDrawerSlideAnimationEnabled();
+ method public void onConfigurationChanged(android.content.res.Configuration!);
+ method public void onDrawerClosed(android.view.View!);
+ method public void onDrawerOpened(android.view.View!);
+ method public void onDrawerSlide(android.view.View!, float);
+ method public void onDrawerStateChanged(int);
+ method public boolean onOptionsItemSelected(android.view.MenuItem!);
+ method public void setDrawerArrowDrawable(androidx.appcompat.graphics.drawable.DrawerArrowDrawable);
+ method public void setDrawerIndicatorEnabled(boolean);
+ method public void setDrawerSlideAnimationEnabled(boolean);
+ method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable!);
+ method public void setHomeAsUpIndicator(int);
+ method public void setToolbarNavigationClickListener(android.view.View.OnClickListener!);
+ method public void syncState();
+ }
+
+ public static interface ActionBarDrawerToggle.Delegate {
+ method public android.content.Context! getActionBarThemedContext();
+ method public android.graphics.drawable.Drawable! getThemeUpIndicator();
+ method public boolean isNavigationVisible();
+ method public void setActionBarDescription(@StringRes int);
+ method public void setActionBarUpIndicator(android.graphics.drawable.Drawable!, @StringRes int);
+ }
+
+ public static interface ActionBarDrawerToggle.DelegateProvider {
+ method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+ }
+
+ public class AlertDialog extends androidx.appcompat.app.AppCompatDialog implements android.content.DialogInterface {
+ ctor protected AlertDialog(android.content.Context);
+ ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener?);
+ ctor protected AlertDialog(android.content.Context, @StyleRes int);
+ method public android.widget.Button! getButton(int);
+ method public android.widget.ListView! getListView();
+ method public void setButton(int, CharSequence!, android.content.DialogInterface.OnClickListener!);
+ method public void setButton(int, CharSequence!, android.graphics.drawable.Drawable!, android.content.DialogInterface.OnClickListener!);
+ method public void setButton(int, CharSequence!, android.os.Message!);
+ method public void setCustomTitle(android.view.View!);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setIcon(int);
+ method public void setIconAttribute(int);
+ method public void setMessage(CharSequence!);
+ method public void setView(android.view.View!);
+ method public void setView(android.view.View!, int, int, int, int);
+ }
+
+ public static class AlertDialog.Builder {
+ ctor public AlertDialog.Builder(android.content.Context);
+ ctor public AlertDialog.Builder(android.content.Context, @StyleRes int);
+ method public androidx.appcompat.app.AlertDialog create();
+ method public android.content.Context getContext();
+ method public androidx.appcompat.app.AlertDialog.Builder! setAdapter(android.widget.ListAdapter!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setCancelable(boolean);
+ method public androidx.appcompat.app.AlertDialog.Builder! setCursor(android.database.Cursor!, android.content.DialogInterface.OnClickListener!, String!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setCustomTitle(android.view.View?);
+ method public androidx.appcompat.app.AlertDialog.Builder! setIcon(android.graphics.drawable.Drawable?);
+ method public androidx.appcompat.app.AlertDialog.Builder! setIcon(@DrawableRes int);
+ method public androidx.appcompat.app.AlertDialog.Builder! setIconAttribute(@AttrRes int);
+ method @Deprecated public androidx.appcompat.app.AlertDialog.Builder! setInverseBackgroundForced(boolean);
+ method public androidx.appcompat.app.AlertDialog.Builder! setItems(@ArrayRes int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setItems(CharSequence![]!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMessage(@StringRes int);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMessage(CharSequence?);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(android.database.Cursor!, String!, String!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(@ArrayRes int, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(CharSequence![]!, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButtonIcon(android.graphics.drawable.Drawable!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButtonIcon(android.graphics.drawable.Drawable!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setOnCancelListener(android.content.DialogInterface.OnCancelListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setOnDismissListener(android.content.DialogInterface.OnDismissListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setOnKeyListener(android.content.DialogInterface.OnKeyListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButtonIcon(android.graphics.drawable.Drawable!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.database.Cursor!, int, String!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.widget.ListAdapter!, int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(@ArrayRes int, int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(CharSequence![]!, int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setTitle(@StringRes int);
+ method public androidx.appcompat.app.AlertDialog.Builder! setTitle(CharSequence?);
+ method public androidx.appcompat.app.AlertDialog.Builder! setView(android.view.View!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setView(int);
+ method public androidx.appcompat.app.AlertDialog! show();
+ }
+
+ public class AppCompatActivity extends androidx.fragment.app.FragmentActivity implements androidx.appcompat.app.ActionBarDrawerToggle.DelegateProvider androidx.appcompat.app.AppCompatCallback androidx.lifecycle.LifecycleOwner androidx.core.app.TaskStackBuilder.SupportParentable {
+ ctor public AppCompatActivity();
+ ctor @ContentView public AppCompatActivity(@LayoutRes int);
+ method public androidx.appcompat.app.AppCompatDelegate getDelegate();
+ method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+ method public androidx.appcompat.app.ActionBar? getSupportActionBar();
+ method public android.content.Intent? getSupportParentActivityIntent();
+ method public void onCreateSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+ method protected void onLocalesChanged(androidx.core.os.LocaleListCompat);
+ method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+ method protected void onNightModeChanged(int);
+ method public void onPrepareSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+ method @CallSuper public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode);
+ method @CallSuper public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode);
+ method @Deprecated public void onSupportContentChanged();
+ method public boolean onSupportNavigateUp();
+ method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+ method public void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+ method @Deprecated public void setSupportProgress(int);
+ method @Deprecated public void setSupportProgressBarIndeterminate(boolean);
+ method @Deprecated public void setSupportProgressBarIndeterminateVisibility(boolean);
+ method @Deprecated public void setSupportProgressBarVisibility(boolean);
+ method public androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+ method public void supportInvalidateOptionsMenu();
+ method public void supportNavigateUpTo(android.content.Intent);
+ method public boolean supportRequestWindowFeature(int);
+ method public boolean supportShouldUpRecreateTask(android.content.Intent);
+ }
+
+ public interface AppCompatCallback {
+ method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+ method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+ method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+ }
+
+ public abstract class AppCompatDelegate {
+ method public abstract void addContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+ method public abstract boolean applyDayNight();
+ method @Deprecated public void attachBaseContext(android.content.Context!);
+ method @CallSuper public android.content.Context attachBaseContext2(android.content.Context);
+ method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+ method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Dialog, androidx.appcompat.app.AppCompatCallback?);
+ method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+ method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.view.Window, androidx.appcompat.app.AppCompatCallback?);
+ method public abstract android.view.View! createView(android.view.View?, String!, android.content.Context, android.util.AttributeSet);
+ method public abstract <T extends android.view.View> T? findViewById(@IdRes int);
+ method @AnyThread public static androidx.core.os.LocaleListCompat getApplicationLocales();
+ method public android.content.Context? getContextForDelegate();
+ method public static int getDefaultNightMode();
+ method public abstract androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+ method public int getLocalNightMode();
+ method public abstract android.view.MenuInflater! getMenuInflater();
+ method public abstract androidx.appcompat.app.ActionBar? getSupportActionBar();
+ method public abstract boolean hasWindowFeature(int);
+ method public abstract void installViewFactory();
+ method public abstract void invalidateOptionsMenu();
+ method public static boolean isCompatVectorFromResourcesEnabled();
+ method public abstract boolean isHandleNativeActionModesEnabled();
+ method public abstract void onConfigurationChanged(android.content.res.Configuration!);
+ method public abstract void onCreate(android.os.Bundle!);
+ method public abstract void onDestroy();
+ method public abstract void onPostCreate(android.os.Bundle!);
+ method public abstract void onPostResume();
+ method public abstract void onSaveInstanceState(android.os.Bundle!);
+ method public abstract void onStart();
+ method public abstract void onStop();
+ method public abstract boolean requestWindowFeature(int);
+ method public static void setApplicationLocales(androidx.core.os.LocaleListCompat);
+ method public static void setCompatVectorFromResourcesEnabled(boolean);
+ method public abstract void setContentView(android.view.View!);
+ method public abstract void setContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+ method public abstract void setContentView(@LayoutRes int);
+ method public static void setDefaultNightMode(int);
+ method public abstract void setHandleNativeActionModesEnabled(boolean);
+ method public abstract void setLocalNightMode(int);
+ method @CallSuper @RequiresApi(33) public void setOnBackInvokedDispatcher(android.window.OnBackInvokedDispatcher?);
+ method public abstract void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+ method public void setTheme(@StyleRes int);
+ method public abstract void setTitle(CharSequence?);
+ method public abstract androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+ field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+ field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+ field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+ field @Deprecated public static final int MODE_NIGHT_AUTO = 0; // 0x0
+ field public static final int MODE_NIGHT_AUTO_BATTERY = 3; // 0x3
+ field @Deprecated public static final int MODE_NIGHT_AUTO_TIME = 0; // 0x0
+ field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+ field public static final int MODE_NIGHT_NO = 1; // 0x1
+ field public static final int MODE_NIGHT_UNSPECIFIED = -100; // 0xffffff9c
+ field public static final int MODE_NIGHT_YES = 2; // 0x2
+ }
+
+ public class AppCompatDialog extends androidx.activity.ComponentDialog implements androidx.appcompat.app.AppCompatCallback {
+ ctor public AppCompatDialog(android.content.Context);
+ ctor protected AppCompatDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener?);
+ ctor public AppCompatDialog(android.content.Context, int);
+ method public androidx.appcompat.app.AppCompatDelegate getDelegate();
+ method public androidx.appcompat.app.ActionBar! getSupportActionBar();
+ method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+ method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+ method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+ method public boolean supportRequestWindowFeature(int);
+ }
+
+ public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
+ ctor public AppCompatDialogFragment();
+ ctor public AppCompatDialogFragment(@LayoutRes int);
+ }
+
+ public class AppCompatViewInflater {
+ ctor public AppCompatViewInflater();
+ method protected androidx.appcompat.widget.AppCompatAutoCompleteTextView createAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatButton createButton(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatCheckBox createCheckBox(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatCheckedTextView createCheckedTextView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatEditText createEditText(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatImageButton createImageButton(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatImageView createImageView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView createMultiAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatRadioButton createRadioButton(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatRatingBar createRatingBar(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatSeekBar createSeekBar(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatSpinner createSpinner(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatTextView createTextView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatToggleButton createToggleButton(android.content.Context!, android.util.AttributeSet!);
+ method protected android.view.View? createView(android.content.Context!, String!, android.util.AttributeSet!);
+ method public final android.view.View? createView(android.view.View?, String, android.content.Context, android.util.AttributeSet, boolean, boolean, boolean, boolean);
+ }
+
+ public final class AppLocalesMetadataHolderService extends android.app.Service {
+ ctor public AppLocalesMetadataHolderService();
+ method public static android.content.pm.ServiceInfo getServiceInfo(android.content.Context) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public android.os.IBinder onBind(android.content.Intent);
+ }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+ public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+ ctor public DrawerArrowDrawable(android.content.Context!);
+ method public void draw(android.graphics.Canvas);
+ method public float getArrowHeadLength();
+ method public float getArrowShaftLength();
+ method public float getBarLength();
+ method public float getBarThickness();
+ method @ColorInt public int getColor();
+ method public int getDirection();
+ method public float getGapSize();
+ method public int getOpacity();
+ method public final android.graphics.Paint! getPaint();
+ method @FloatRange(from=0.0, to=1.0) public float getProgress();
+ method public boolean isSpinEnabled();
+ method public void setAlpha(int);
+ method public void setArrowHeadLength(float);
+ method public void setArrowShaftLength(float);
+ method public void setBarLength(float);
+ method public void setBarThickness(float);
+ method public void setColor(@ColorInt int);
+ method public void setColorFilter(android.graphics.ColorFilter!);
+ method public void setDirection(int);
+ method public void setGapSize(float);
+ method public void setProgress(@FloatRange(from=0.0, to=1.0) float);
+ method public void setSpinEnabled(boolean);
+ method public void setVerticalMirror(boolean);
+ field public static final int ARROW_DIRECTION_END = 3; // 0x3
+ field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+ field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+ field public static final int ARROW_DIRECTION_START = 2; // 0x2
+ }
+
+}
+
+package androidx.appcompat.view {
+
+ public abstract class ActionMode {
+ ctor public ActionMode();
+ method public abstract void finish();
+ method public abstract android.view.View! getCustomView();
+ method public abstract android.view.Menu! getMenu();
+ method public abstract android.view.MenuInflater! getMenuInflater();
+ method public abstract CharSequence! getSubtitle();
+ method public Object! getTag();
+ method public abstract CharSequence! getTitle();
+ method public boolean getTitleOptionalHint();
+ method public abstract void invalidate();
+ method public boolean isTitleOptional();
+ method public abstract void setCustomView(android.view.View!);
+ method public abstract void setSubtitle(int);
+ method public abstract void setSubtitle(CharSequence!);
+ method public void setTag(Object!);
+ method public abstract void setTitle(int);
+ method public abstract void setTitle(CharSequence!);
+ method public void setTitleOptionalHint(boolean);
+ }
+
+ public static interface ActionMode.Callback {
+ method public boolean onActionItemClicked(androidx.appcompat.view.ActionMode!, android.view.MenuItem!);
+ method public boolean onCreateActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+ method public void onDestroyActionMode(androidx.appcompat.view.ActionMode!);
+ method public boolean onPrepareActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+ }
+
+ @Deprecated public interface CollapsibleActionView {
+ method @Deprecated public void onActionViewCollapsed();
+ method @Deprecated public void onActionViewExpanded();
+ }
+
+ public class ContextThemeWrapper extends android.content.ContextWrapper {
+ ctor public ContextThemeWrapper();
+ ctor public ContextThemeWrapper(android.content.Context!, android.content.res.Resources.Theme!);
+ ctor public ContextThemeWrapper(android.content.Context!, @StyleRes int);
+ method public void applyOverrideConfiguration(android.content.res.Configuration!);
+ method public int getThemeResId();
+ method protected void onApplyThemeResource(android.content.res.Resources.Theme!, int, boolean);
+ }
+
+}
+
+package androidx.appcompat.widget {
+
+ public class ActionMenuView extends androidx.appcompat.widget.LinearLayoutCompat {
+ ctor public ActionMenuView(android.content.Context);
+ ctor public ActionMenuView(android.content.Context, android.util.AttributeSet?);
+ method public void dismissPopupMenus();
+ method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateDefaultLayoutParams();
+ method public androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+ method public android.view.Menu! getMenu();
+ method public android.graphics.drawable.Drawable? getOverflowIcon();
+ method public int getPopupTheme();
+ method public boolean hideOverflowMenu();
+ method public boolean isOverflowMenuShowing();
+ method public void onConfigurationChanged(android.content.res.Configuration!);
+ method public void onDetachedFromWindow();
+ method public void setOnMenuItemClickListener(androidx.appcompat.widget.ActionMenuView.OnMenuItemClickListener!);
+ method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+ method public void setPopupTheme(@StyleRes int);
+ method public boolean showOverflowMenu();
+ }
+
+ public static class ActionMenuView.LayoutParams extends androidx.appcompat.widget.LinearLayoutCompat.LayoutParams {
+ ctor public ActionMenuView.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+ ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public ActionMenuView.LayoutParams(androidx.appcompat.widget.ActionMenuView.LayoutParams!);
+ ctor public ActionMenuView.LayoutParams(int, int);
+ field public int cellsUsed;
+ field public boolean expandable;
+ field public int extraPixels;
+ field public boolean isOverflowButton;
+ field public boolean preventEdgeOffset;
+ }
+
+ public static interface ActionMenuView.OnMenuItemClickListener {
+ method public boolean onMenuItemClick(android.view.MenuItem!);
+ }
+
+ public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatAutoCompleteTextView(android.content.Context);
+ ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ }
+
+ public class AppCompatButton extends android.widget.Button implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatButton(android.content.Context);
+ ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method public void setSupportAllCaps(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ }
+
+ public class AppCompatCheckBox extends android.widget.CheckBox implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatCheckBox(android.content.Context);
+ ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public class AppCompatCheckedTextView extends android.widget.CheckedTextView implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatCheckedTextView(android.content.Context);
+ ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context, int);
+ }
+
+ public class AppCompatEditText extends android.widget.EditText implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.OnReceiveContentViewBehavior androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatEditText(android.content.Context);
+ ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ }
+
+ public class AppCompatImageButton extends android.widget.ImageButton implements androidx.core.view.TintableBackgroundView {
+ ctor public AppCompatImageButton(android.content.Context);
+ ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public class AppCompatImageView extends android.widget.ImageView implements androidx.core.view.TintableBackgroundView {
+ ctor public AppCompatImageView(android.content.Context);
+ ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+ ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ }
+
+ public class AppCompatRadioButton extends android.widget.RadioButton implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatRadioButton(android.content.Context!);
+ ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?);
+ ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public class AppCompatRatingBar extends android.widget.RatingBar {
+ ctor public AppCompatRatingBar(android.content.Context);
+ ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+ public class AppCompatSeekBar extends android.widget.SeekBar {
+ ctor public AppCompatSeekBar(android.content.Context);
+ ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+ public class AppCompatSpinner extends android.widget.Spinner implements androidx.core.view.TintableBackgroundView {
+ ctor public AppCompatSpinner(android.content.Context);
+ ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int);
+ ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int);
+ ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int, android.content.res.Resources.Theme!);
+ ctor public AppCompatSpinner(android.content.Context, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public class AppCompatTextView extends android.widget.TextView implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatTextView(android.content.Context);
+ ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParamsCompat();
+ method public boolean isEmojiCompatEnabled();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method public void setPrecomputedText(androidx.core.text.PrecomputedTextCompat);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ method public void setTextFuture(java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>?);
+ method public void setTextMetricsParamsCompat(androidx.core.text.PrecomputedTextCompat.Params);
+ }
+
+ public class AppCompatToggleButton extends android.widget.ToggleButton implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatToggleButton(android.content.Context);
+ ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public interface EmojiCompatConfigurationView {
+ method public boolean isEmojiCompatEnabled();
+ method public void setEmojiCompatEnabled(boolean);
+ }
+
+ public class LinearLayoutCompat extends android.view.ViewGroup {
+ ctor public LinearLayoutCompat(android.content.Context);
+ ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?);
+ ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?, int);
+ method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateDefaultLayoutParams();
+ method public androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+ method @androidx.resourceinspection.annotation.Attribute("android:baselineAlignedChildIndex") public int getBaselineAlignedChildIndex();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:divider") public android.graphics.drawable.Drawable! getDividerDrawable();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:dividerPadding") public int getDividerPadding();
+ method @GravityInt @androidx.resourceinspection.annotation.Attribute("android:gravity") public int getGravity();
+ method @androidx.resourceinspection.annotation.Attribute(value="android:orientation", intMapping={@androidx.resourceinspection.annotation.Attribute.IntMap(name="horizontal", value=0), @androidx.resourceinspection.annotation.Attribute.IntMap(name="vertical", value=1)}) public int getOrientation();
+ method @androidx.resourceinspection.annotation.Attribute(value="androidx.appcompat:showDividers", intMapping={@androidx.resourceinspection.annotation.Attribute.IntMap(name="none", value=0), @androidx.resourceinspection.annotation.Attribute.IntMap(name="beginning", value=1, mask=1), @androidx.resourceinspection.annotation.Attribute.IntMap(name="middle", value=2, mask=2), @androidx.resourceinspection.annotation.Attribute.IntMap(name="end", value=4, mask=4)}) public int getShowDividers();
+ method @androidx.resourceinspection.annotation.Attribute("android:weightSum") public float getWeightSum();
+ method @androidx.resourceinspection.annotation.Attribute("android:baselineAligned") public boolean isBaselineAligned();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:measureWithLargestChild") public boolean isMeasureWithLargestChildEnabled();
+ method public void setBaselineAligned(boolean);
+ method public void setBaselineAlignedChildIndex(int);
+ method public void setDividerDrawable(android.graphics.drawable.Drawable!);
+ method public void setDividerPadding(int);
+ method public void setGravity(@GravityInt int);
+ method public void setHorizontalGravity(int);
+ method public void setMeasureWithLargestChildEnabled(boolean);
+ method public void setOrientation(int);
+ method public void setShowDividers(int);
+ method public void setVerticalGravity(int);
+ method public void setWeightSum(float);
+ field public static final int HORIZONTAL = 0; // 0x0
+ field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+ field public static final int SHOW_DIVIDER_END = 4; // 0x4
+ field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+ field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+ field public static final int VERTICAL = 1; // 0x1
+ }
+
+ public static class LinearLayoutCompat.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+ ctor public LinearLayoutCompat.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+ ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+ ctor public LinearLayoutCompat.LayoutParams(int, int);
+ ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+ }
+
+ public class ListPopupWindow {
+ ctor public ListPopupWindow(android.content.Context);
+ ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?);
+ ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int);
+ ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int, @StyleRes int);
+ method public void clearListSelection();
+ method public android.view.View.OnTouchListener! createDragToOpenListener(android.view.View!);
+ method public void dismiss();
+ method public android.view.View? getAnchorView();
+ method @StyleRes public int getAnimationStyle();
+ method public android.graphics.drawable.Drawable? getBackground();
+ method public android.graphics.Rect? getEpicenterBounds();
+ method public int getHeight();
+ method public int getHorizontalOffset();
+ method public int getInputMethodMode();
+ method public android.widget.ListView? getListView();
+ method public int getPromptPosition();
+ method public Object? getSelectedItem();
+ method public long getSelectedItemId();
+ method public int getSelectedItemPosition();
+ method public android.view.View? getSelectedView();
+ method public int getSoftInputMode();
+ method public int getVerticalOffset();
+ method public int getWidth();
+ method public boolean isInputMethodNotNeeded();
+ method public boolean isModal();
+ method public boolean isShowing();
+ method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyPreIme(int, android.view.KeyEvent);
+ method public boolean onKeyUp(int, android.view.KeyEvent);
+ method public boolean performItemClick(int);
+ method public void postShow();
+ method public void setAdapter(android.widget.ListAdapter?);
+ method public void setAnchorView(android.view.View?);
+ method public void setAnimationStyle(@StyleRes int);
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setContentWidth(int);
+ method public void setDropDownGravity(int);
+ method public void setEpicenterBounds(android.graphics.Rect?);
+ method public void setHeight(int);
+ method public void setHorizontalOffset(int);
+ method public void setInputMethodMode(int);
+ method public void setListSelector(android.graphics.drawable.Drawable!);
+ method public void setModal(boolean);
+ method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener?);
+ method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener?);
+ method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener?);
+ method public void setPromptPosition(int);
+ method public void setPromptView(android.view.View?);
+ method public void setSelection(int);
+ method public void setSoftInputMode(int);
+ method public void setVerticalOffset(int);
+ method public void setWidth(int);
+ method public void setWindowLayoutType(int);
+ method public void show();
+ field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+ field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+ field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+ field public static final int MATCH_PARENT = -1; // 0xffffffff
+ field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+ field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+ field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+ }
+
+ public class PopupMenu {
+ ctor public PopupMenu(android.content.Context, android.view.View);
+ ctor public PopupMenu(android.content.Context, android.view.View, int);
+ ctor public PopupMenu(android.content.Context, android.view.View, int, @AttrRes int, @StyleRes int);
+ method public void dismiss();
+ method public android.view.View.OnTouchListener getDragToOpenListener();
+ method public int getGravity();
+ method public android.view.Menu getMenu();
+ method public android.view.MenuInflater getMenuInflater();
+ method public void inflate(@MenuRes int);
+ method public void setForceShowIcon(boolean);
+ method public void setGravity(int);
+ method public void setOnDismissListener(androidx.appcompat.widget.PopupMenu.OnDismissListener?);
+ method public void setOnMenuItemClickListener(androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener?);
+ method public void show();
+ }
+
+ public static interface PopupMenu.OnDismissListener {
+ method public void onDismiss(androidx.appcompat.widget.PopupMenu!);
+ }
+
+ public static interface PopupMenu.OnMenuItemClickListener {
+ method public boolean onMenuItemClick(android.view.MenuItem!);
+ }
+
+ public class SearchView extends androidx.appcompat.widget.LinearLayoutCompat implements androidx.appcompat.view.CollapsibleActionView {
+ ctor public SearchView(android.content.Context);
+ ctor public SearchView(android.content.Context, android.util.AttributeSet?);
+ ctor public SearchView(android.content.Context, android.util.AttributeSet?, int);
+ method @androidx.resourceinspection.annotation.Attribute("android:imeOptions") public int getImeOptions();
+ method public int getInputType();
+ method @androidx.resourceinspection.annotation.Attribute("android:maxWidth") public int getMaxWidth();
+ method public CharSequence! getQuery();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:queryHint") public CharSequence? getQueryHint();
+ method public androidx.cursoradapter.widget.CursorAdapter! getSuggestionsAdapter();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:iconifiedByDefault") public boolean isIconfiedByDefault();
+ method public boolean isIconified();
+ method public boolean isQueryRefinementEnabled();
+ method public boolean isSubmitButtonEnabled();
+ method public void onActionViewCollapsed();
+ method public void onActionViewExpanded();
+ method protected void onQueryRefine(CharSequence?);
+ method public void setIconified(boolean);
+ method public void setIconifiedByDefault(boolean);
+ method public void setImeOptions(int);
+ method public void setInputType(int);
+ method public void setMaxWidth(int);
+ method public void setOnCloseListener(androidx.appcompat.widget.SearchView.OnCloseListener!);
+ method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener!);
+ method public void setOnQueryTextListener(androidx.appcompat.widget.SearchView.OnQueryTextListener!);
+ method public void setOnSearchClickListener(android.view.View.OnClickListener!);
+ method public void setOnSuggestionListener(androidx.appcompat.widget.SearchView.OnSuggestionListener!);
+ method public void setQuery(CharSequence!, boolean);
+ method public void setQueryHint(CharSequence?);
+ method public void setQueryRefinementEnabled(boolean);
+ method public void setSearchableInfo(android.app.SearchableInfo!);
+ method public void setSubmitButtonEnabled(boolean);
+ method public void setSuggestionsAdapter(androidx.cursoradapter.widget.CursorAdapter!);
+ }
+
+ public static interface SearchView.OnCloseListener {
+ method public boolean onClose();
+ }
+
+ public static interface SearchView.OnQueryTextListener {
+ method public boolean onQueryTextChange(String!);
+ method public boolean onQueryTextSubmit(String!);
+ }
+
+ public static interface SearchView.OnSuggestionListener {
+ method public boolean onSuggestionClick(int);
+ method public boolean onSuggestionSelect(int);
+ }
+
+ public class ShareActionProvider extends androidx.core.view.ActionProvider {
+ ctor public ShareActionProvider(android.content.Context!);
+ method public android.view.View! onCreateActionView();
+ method public void setOnShareTargetSelectedListener(androidx.appcompat.widget.ShareActionProvider.OnShareTargetSelectedListener!);
+ method public void setShareHistoryFileName(String!);
+ method public void setShareIntent(android.content.Intent!);
+ field public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+ }
+
+ public static interface ShareActionProvider.OnShareTargetSelectedListener {
+ method public boolean onShareTargetSelected(androidx.appcompat.widget.ShareActionProvider!, android.content.Intent!);
+ }
+
+ public class SwitchCompat extends android.widget.CompoundButton implements androidx.appcompat.widget.EmojiCompatConfigurationView {
+ ctor public SwitchCompat(android.content.Context);
+ ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?);
+ ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?, int);
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:showText") public boolean getShowText();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:splitTrack") public boolean getSplitTrack();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:switchMinWidth") public int getSwitchMinWidth();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:switchPadding") public int getSwitchPadding();
+ method @androidx.resourceinspection.annotation.Attribute("android:textOff") public CharSequence! getTextOff();
+ method @androidx.resourceinspection.annotation.Attribute("android:textOn") public CharSequence! getTextOn();
+ method @androidx.resourceinspection.annotation.Attribute("android:thumb") public android.graphics.drawable.Drawable! getThumbDrawable();
+ method @FloatRange(from=0.0, to=1.0) protected final float getThumbPosition();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:thumbTextPadding") public int getThumbTextPadding();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:thumbTint") public android.content.res.ColorStateList? getThumbTintList();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:thumbTintMode") public android.graphics.PorterDuff.Mode? getThumbTintMode();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:track") public android.graphics.drawable.Drawable! getTrackDrawable();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:trackTint") public android.content.res.ColorStateList? getTrackTintList();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:trackTintMode") public android.graphics.PorterDuff.Mode? getTrackTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void onMeasure(int, int);
+ method public void setEmojiCompatEnabled(boolean);
+ method protected final void setEnforceSwitchWidth(boolean);
+ method public void setShowText(boolean);
+ method public void setSplitTrack(boolean);
+ method public void setSwitchMinWidth(int);
+ method public void setSwitchPadding(int);
+ method public void setSwitchTextAppearance(android.content.Context!, int);
+ method public void setSwitchTypeface(android.graphics.Typeface!);
+ method public void setSwitchTypeface(android.graphics.Typeface!, int);
+ method public void setTextOff(CharSequence!);
+ method public void setTextOn(CharSequence!);
+ method public void setThumbDrawable(android.graphics.drawable.Drawable!);
+ method public void setThumbResource(int);
+ method public void setThumbTextPadding(int);
+ method public void setThumbTintList(android.content.res.ColorStateList?);
+ method public void setThumbTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTrackDrawable(android.graphics.drawable.Drawable!);
+ method public void setTrackResource(int);
+ method public void setTrackTintList(android.content.res.ColorStateList?);
+ method public void setTrackTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
+ method public android.content.res.Resources.Theme? getDropDownViewTheme();
+ method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+ }
+
+ public static final class ThemedSpinnerAdapter.Helper {
+ ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+ method public android.view.LayoutInflater getDropDownViewInflater();
+ method public android.content.res.Resources.Theme? getDropDownViewTheme();
+ method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+ }
+
+ public class Toolbar extends android.view.ViewGroup implements androidx.core.view.MenuHost {
+ ctor public Toolbar(android.content.Context);
+ ctor public Toolbar(android.content.Context, android.util.AttributeSet?);
+ ctor public Toolbar(android.content.Context, android.util.AttributeSet?, int);
+ method @MainThread public void addMenuProvider(androidx.core.view.MenuProvider);
+ method @MainThread public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner);
+ method @MainThread public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State);
+ method public void collapseActionView();
+ method public void dismissPopupMenus();
+ method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateDefaultLayoutParams();
+ method public androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:collapseContentDescription") public CharSequence? getCollapseContentDescription();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:collapseIcon") public android.graphics.drawable.Drawable? getCollapseIcon();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetEnd") public int getContentInsetEnd();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetEndWithActions") public int getContentInsetEndWithActions();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetLeft") public int getContentInsetLeft();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetRight") public int getContentInsetRight();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetStart") public int getContentInsetStart();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetStartWithNavigation") public int getContentInsetStartWithNavigation();
+ method public int getCurrentContentInsetEnd();
+ method public int getCurrentContentInsetLeft();
+ method public int getCurrentContentInsetRight();
+ method public int getCurrentContentInsetStart();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:logo") public android.graphics.drawable.Drawable! getLogo();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:logoDescription") public CharSequence! getLogoDescription();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:menu") public android.view.Menu! getMenu();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:navigationContentDescription") public CharSequence? getNavigationContentDescription();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:navigationIcon") public android.graphics.drawable.Drawable? getNavigationIcon();
+ method public android.graphics.drawable.Drawable? getOverflowIcon();
+ method @StyleRes @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:popupTheme") public int getPopupTheme();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:subtitle") public CharSequence! getSubtitle();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:title") public CharSequence! getTitle();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:titleMarginBottom") public int getTitleMarginBottom();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:titleMarginEnd") public int getTitleMarginEnd();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:titleMarginStart") public int getTitleMarginStart();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:titleMarginTop") public int getTitleMarginTop();
+ method public boolean hasExpandedActionView();
+ method public boolean hideOverflowMenu();
+ method public void inflateMenu(@MenuRes int);
+ method @MainThread public void invalidateMenu();
+ method public boolean isBackInvokedCallbackEnabled();
+ method public boolean isOverflowMenuShowing();
+ method @MainThread public void removeMenuProvider(androidx.core.view.MenuProvider);
+ method public void setBackInvokedCallbackEnabled(boolean);
+ method public void setCollapseContentDescription(@StringRes int);
+ method public void setCollapseContentDescription(CharSequence?);
+ method public void setCollapseIcon(android.graphics.drawable.Drawable?);
+ method public void setCollapseIcon(@DrawableRes int);
+ method public void setContentInsetEndWithActions(int);
+ method public void setContentInsetStartWithNavigation(int);
+ method public void setContentInsetsAbsolute(int, int);
+ method public void setContentInsetsRelative(int, int);
+ method public void setLogo(android.graphics.drawable.Drawable!);
+ method public void setLogo(@DrawableRes int);
+ method public void setLogoDescription(@StringRes int);
+ method public void setLogoDescription(CharSequence!);
+ method public void setNavigationContentDescription(@StringRes int);
+ method public void setNavigationContentDescription(CharSequence?);
+ method public void setNavigationIcon(android.graphics.drawable.Drawable?);
+ method public void setNavigationIcon(@DrawableRes int);
+ method public void setNavigationOnClickListener(android.view.View.OnClickListener!);
+ method public void setOnMenuItemClickListener(androidx.appcompat.widget.Toolbar.OnMenuItemClickListener!);
+ method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+ method public void setPopupTheme(@StyleRes int);
+ method public void setSubtitle(@StringRes int);
+ method public void setSubtitle(CharSequence!);
+ method public void setSubtitleTextAppearance(android.content.Context!, @StyleRes int);
+ method public void setSubtitleTextColor(android.content.res.ColorStateList);
+ method public void setSubtitleTextColor(@ColorInt int);
+ method public void setTitle(@StringRes int);
+ method public void setTitle(CharSequence!);
+ method public void setTitleMargin(int, int, int, int);
+ method public void setTitleMarginBottom(int);
+ method public void setTitleMarginEnd(int);
+ method public void setTitleMarginStart(int);
+ method public void setTitleMarginTop(int);
+ method public void setTitleTextAppearance(android.content.Context!, @StyleRes int);
+ method public void setTitleTextColor(android.content.res.ColorStateList);
+ method public void setTitleTextColor(@ColorInt int);
+ method public boolean showOverflowMenu();
+ }
+
+ public static class Toolbar.LayoutParams extends androidx.appcompat.app.ActionBar.LayoutParams {
+ ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+ ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+ ctor public Toolbar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+ ctor public Toolbar.LayoutParams(androidx.appcompat.widget.Toolbar.LayoutParams!);
+ ctor public Toolbar.LayoutParams(int);
+ ctor public Toolbar.LayoutParams(int, int);
+ ctor public Toolbar.LayoutParams(int, int, int);
+ }
+
+ public static interface Toolbar.OnMenuItemClickListener {
+ method public boolean onMenuItemClick(android.view.MenuItem!);
+ }
+
+ public static class Toolbar.SavedState extends androidx.customview.view.AbsSavedState {
+ ctor public Toolbar.SavedState(android.os.Parcel!);
+ ctor public Toolbar.SavedState(android.os.Parcel!, ClassLoader!);
+ ctor public Toolbar.SavedState(android.os.Parcelable!);
+ field public static final android.os.Parcelable.Creator<androidx.appcompat.widget.Toolbar.SavedState!>! CREATOR;
+ }
+
+ public class TooltipCompat {
+ method public static void setTooltipText(android.view.View, CharSequence?);
+ }
+
+}
+
diff --git a/appcompat/appcompat/api/res-1.7.0-beta01.txt b/appcompat/appcompat/api/res-1.7.0-beta01.txt
new file mode 100644
index 0000000..b9f58a8
--- /dev/null
+++ b/appcompat/appcompat/api/res-1.7.0-beta01.txt
@@ -0,0 +1,369 @@
+attr actionBarDivider
+attr actionBarItemBackground
+attr actionBarPopupTheme
+attr actionBarSize
+attr actionBarSplitStyle
+attr actionBarStyle
+attr actionBarTabBarStyle
+attr actionBarTabStyle
+attr actionBarTabTextStyle
+attr actionBarTheme
+attr actionBarWidgetTheme
+attr actionButtonStyle
+attr actionDropDownStyle
+attr actionLayout
+attr actionMenuTextAppearance
+attr actionMenuTextColor
+attr actionModeBackground
+attr actionModeCloseButtonStyle
+attr actionModeCloseContentDescription
+attr actionModeCloseDrawable
+attr actionModeCopyDrawable
+attr actionModeCutDrawable
+attr actionModeFindDrawable
+attr actionModePasteDrawable
+attr actionModeSelectAllDrawable
+attr actionModeShareDrawable
+attr actionModeSplitBackground
+attr actionModeStyle
+attr actionModeTheme
+attr actionModeWebSearchDrawable
+attr actionOverflowButtonStyle
+attr actionOverflowMenuStyle
+attr actionProviderClass
+attr actionViewClass
+attr alertDialogStyle
+attr alertDialogTheme
+attr arrowHeadLength
+attr arrowShaftLength
+attr autoCompleteTextViewStyle
+attr autoSizeMaxTextSize
+attr autoSizeMinTextSize
+attr autoSizePresetSizes
+attr autoSizeStepGranularity
+attr autoSizeTextType
+attr background
+attr backgroundSplit
+attr backgroundStacked
+attr backgroundTint
+attr backgroundTintMode
+attr barLength
+attr borderlessButtonStyle
+attr buttonBarButtonStyle
+attr buttonBarNegativeButtonStyle
+attr buttonBarNeutralButtonStyle
+attr buttonBarPositiveButtonStyle
+attr buttonBarStyle
+attr buttonGravity
+attr buttonStyle
+attr buttonStyleSmall
+attr buttonTint
+attr buttonTintMode
+attr checkboxStyle
+attr checkedTextViewStyle
+attr closeIcon
+attr closeItemLayout
+attr collapseContentDescription
+attr collapseIcon
+attr color
+attr colorAccent
+attr colorBackgroundFloating
+attr colorButtonNormal
+attr colorControlActivated
+attr colorControlHighlight
+attr colorControlNormal
+attr colorError
+attr colorPrimary
+attr colorPrimaryDark
+attr commitIcon
+attr contentInsetEnd
+attr contentInsetEndWithActions
+attr contentInsetLeft
+attr contentInsetRight
+attr contentInsetStart
+attr contentInsetStartWithNavigation
+attr customNavigationLayout
+attr dialogCornerRadius
+attr dialogPreferredPadding
+attr dialogTheme
+attr displayOptions
+attr divider
+attr dividerHorizontal
+attr dividerPadding
+attr dividerVertical
+attr drawableSize
+attr drawerArrowStyle
+attr dropDownListViewStyle
+attr editTextBackground
+attr editTextColor
+attr editTextStyle
+attr elevation
+attr emojiCompatEnabled
+attr firstBaselineToTopHeight
+attr fontFamily
+attr fontVariationSettings
+attr gapBetweenBars
+attr goIcon
+attr height
+attr hideOnContentScroll
+attr homeAsUpIndicator
+attr homeLayout
+attr icon
+attr iconTint
+attr iconTintMode
+attr iconifiedByDefault
+attr imageButtonStyle
+attr indeterminateProgressStyle
+attr isLightTheme
+attr itemPadding
+attr lastBaselineToBottomHeight
+attr layout
+attr lineHeight
+attr listChoiceBackgroundIndicator
+attr listChoiceIndicatorMultipleAnimated
+attr listChoiceIndicatorSingleAnimated
+attr listDividerAlertDialog
+attr listPopupWindowStyle
+attr listPreferredItemHeight
+attr listPreferredItemHeightLarge
+attr listPreferredItemHeightSmall
+attr listPreferredItemPaddingEnd
+attr listPreferredItemPaddingLeft
+attr listPreferredItemPaddingRight
+attr listPreferredItemPaddingStart
+attr logo
+attr logoDescription
+attr maxButtonHeight
+attr measureWithLargestChild
+attr navigationContentDescription
+attr navigationIcon
+attr navigationMode
+attr overlapAnchor
+attr paddingEnd
+attr paddingStart
+attr panelBackground
+attr popupMenuStyle
+attr popupTheme
+attr popupWindowStyle
+attr preserveIconSpacing
+attr progressBarPadding
+attr progressBarStyle
+attr queryBackground
+attr queryHint
+attr radioButtonStyle
+attr ratingBarStyle
+attr ratingBarStyleIndicator
+attr ratingBarStyleSmall
+attr searchHintIcon
+attr searchIcon
+attr searchViewStyle
+attr seekBarStyle
+attr selectableItemBackground
+attr selectableItemBackgroundBorderless
+attr showAsAction
+attr showDividers
+attr showText
+attr spinBars
+attr spinnerDropDownItemStyle
+attr spinnerStyle
+attr splitTrack
+attr srcCompat
+attr state_above_anchor
+attr submitBackground
+attr subtitle
+attr subtitleTextAppearance
+attr subtitleTextColor
+attr subtitleTextStyle
+attr suggestionRowLayout
+attr switchMinWidth
+attr switchPadding
+attr switchStyle
+attr switchTextAppearance
+attr textAllCaps
+attr textAppearanceLargePopupMenu
+attr textAppearanceListItem
+attr textAppearanceListItemSecondary
+attr textAppearanceListItemSmall
+attr textAppearancePopupMenuHeader
+attr textAppearanceSearchResultSubtitle
+attr textAppearanceSearchResultTitle
+attr textAppearanceSmallPopupMenu
+attr textColorAlertDialogListItem
+attr textLocale
+attr theme
+attr thickness
+attr thumbTextPadding
+attr thumbTint
+attr thumbTintMode
+attr tickMark
+attr tickMarkTint
+attr tickMarkTintMode
+attr tint
+attr tintMode
+attr title
+attr titleMargin
+attr titleMarginBottom
+attr titleMarginEnd
+attr titleMarginStart
+attr titleMarginTop
+attr titleMargins
+attr titleTextAppearance
+attr titleTextColor
+attr titleTextStyle
+attr toolbarNavigationButtonStyle
+attr toolbarStyle
+attr track
+attr trackTint
+attr trackTintMode
+attr voiceIcon
+attr windowActionBar
+attr windowActionBarOverlay
+attr windowActionModeOverlay
+attr windowNoTitle
+layout support_simple_spinner_dropdown_item
+style TextAppearance_AppCompat
+style TextAppearance_AppCompat_Body1
+style TextAppearance_AppCompat_Body2
+style TextAppearance_AppCompat_Button
+style TextAppearance_AppCompat_Caption
+style TextAppearance_AppCompat_Display1
+style TextAppearance_AppCompat_Display2
+style TextAppearance_AppCompat_Display3
+style TextAppearance_AppCompat_Display4
+style TextAppearance_AppCompat_Headline
+style TextAppearance_AppCompat_Inverse
+style TextAppearance_AppCompat_Large
+style TextAppearance_AppCompat_Large_Inverse
+style TextAppearance_AppCompat_Light_SearchResult_Subtitle
+style TextAppearance_AppCompat_Light_SearchResult_Title
+style TextAppearance_AppCompat_Light_Widget_PopupMenu_Large
+style TextAppearance_AppCompat_Light_Widget_PopupMenu_Small
+style TextAppearance_AppCompat_Medium
+style TextAppearance_AppCompat_Medium_Inverse
+style TextAppearance_AppCompat_Menu
+style TextAppearance_AppCompat_SearchResult_Subtitle
+style TextAppearance_AppCompat_SearchResult_Title
+style TextAppearance_AppCompat_Small
+style TextAppearance_AppCompat_Small_Inverse
+style TextAppearance_AppCompat_Subhead
+style TextAppearance_AppCompat_Subhead_Inverse
+style TextAppearance_AppCompat_Title
+style TextAppearance_AppCompat_Title_Inverse
+style TextAppearance_AppCompat_Widget_ActionBar_Menu
+style TextAppearance_AppCompat_Widget_ActionBar_Subtitle
+style TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse
+style TextAppearance_AppCompat_Widget_ActionBar_Title
+style TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse
+style TextAppearance_AppCompat_Widget_ActionMode_Subtitle
+style TextAppearance_AppCompat_Widget_ActionMode_Subtitle_Inverse
+style TextAppearance_AppCompat_Widget_ActionMode_Title
+style TextAppearance_AppCompat_Widget_ActionMode_Title_Inverse
+style TextAppearance_AppCompat_Widget_Button
+style TextAppearance_AppCompat_Widget_Button_Borderless_Colored
+style TextAppearance_AppCompat_Widget_Button_Colored
+style TextAppearance_AppCompat_Widget_Button_Inverse
+style TextAppearance_AppCompat_Widget_DropDownItem
+style TextAppearance_AppCompat_Widget_PopupMenu_Header
+style TextAppearance_AppCompat_Widget_PopupMenu_Large
+style TextAppearance_AppCompat_Widget_PopupMenu_Small
+style TextAppearance_AppCompat_Widget_Switch
+style TextAppearance_AppCompat_Widget_TextView_SpinnerItem
+style ThemeOverlay_AppCompat
+style ThemeOverlay_AppCompat_ActionBar
+style ThemeOverlay_AppCompat_Dark
+style ThemeOverlay_AppCompat_Dark_ActionBar
+style ThemeOverlay_AppCompat_DayNight
+style ThemeOverlay_AppCompat_DayNight_ActionBar
+style ThemeOverlay_AppCompat_Dialog
+style ThemeOverlay_AppCompat_Dialog_Alert
+style ThemeOverlay_AppCompat_Light
+style Theme_AppCompat
+style Theme_AppCompat_DayNight
+style Theme_AppCompat_DayNight_DarkActionBar
+style Theme_AppCompat_DayNight_Dialog
+style Theme_AppCompat_DayNight_DialogWhenLarge
+style Theme_AppCompat_DayNight_Dialog_Alert
+style Theme_AppCompat_DayNight_Dialog_MinWidth
+style Theme_AppCompat_DayNight_NoActionBar
+style Theme_AppCompat_Dialog
+style Theme_AppCompat_DialogWhenLarge
+style Theme_AppCompat_Dialog_Alert
+style Theme_AppCompat_Dialog_MinWidth
+style Theme_AppCompat_Light
+style Theme_AppCompat_Light_DarkActionBar
+style Theme_AppCompat_Light_Dialog
+style Theme_AppCompat_Light_DialogWhenLarge
+style Theme_AppCompat_Light_Dialog_Alert
+style Theme_AppCompat_Light_Dialog_MinWidth
+style Theme_AppCompat_Light_NoActionBar
+style Theme_AppCompat_NoActionBar
+style Widget_AppCompat_ActionBar
+style Widget_AppCompat_ActionBar_Solid
+style Widget_AppCompat_ActionBar_TabBar
+style Widget_AppCompat_ActionBar_TabText
+style Widget_AppCompat_ActionBar_TabView
+style Widget_AppCompat_ActionButton
+style Widget_AppCompat_ActionButton_CloseMode
+style Widget_AppCompat_ActionButton_Overflow
+style Widget_AppCompat_ActionMode
+style Widget_AppCompat_AutoCompleteTextView
+style Widget_AppCompat_Button
+style Widget_AppCompat_ButtonBar
+style Widget_AppCompat_ButtonBar_AlertDialog
+style Widget_AppCompat_Button_Borderless
+style Widget_AppCompat_Button_Borderless_Colored
+style Widget_AppCompat_Button_ButtonBar_AlertDialog
+style Widget_AppCompat_Button_Colored
+style Widget_AppCompat_Button_Small
+style Widget_AppCompat_CompoundButton_CheckBox
+style Widget_AppCompat_CompoundButton_RadioButton
+style Widget_AppCompat_CompoundButton_Switch
+style Widget_AppCompat_DrawerArrowToggle
+style Widget_AppCompat_DropDownItem_Spinner
+style Widget_AppCompat_EditText
+style Widget_AppCompat_ImageButton
+style Widget_AppCompat_Light_ActionBar
+style Widget_AppCompat_Light_ActionBar_Solid
+style Widget_AppCompat_Light_ActionBar_Solid_Inverse
+style Widget_AppCompat_Light_ActionBar_TabBar
+style Widget_AppCompat_Light_ActionBar_TabBar_Inverse
+style Widget_AppCompat_Light_ActionBar_TabText
+style Widget_AppCompat_Light_ActionBar_TabText_Inverse
+style Widget_AppCompat_Light_ActionBar_TabView
+style Widget_AppCompat_Light_ActionBar_TabView_Inverse
+style Widget_AppCompat_Light_ActionButton
+style Widget_AppCompat_Light_ActionButton_CloseMode
+style Widget_AppCompat_Light_ActionButton_Overflow
+style Widget_AppCompat_Light_ActionMode_Inverse
+style Widget_AppCompat_Light_AutoCompleteTextView
+style Widget_AppCompat_Light_DropDownItem_Spinner
+style Widget_AppCompat_Light_ListPopupWindow
+style Widget_AppCompat_Light_ListView_DropDown
+style Widget_AppCompat_Light_PopupMenu
+style Widget_AppCompat_Light_PopupMenu_Overflow
+style Widget_AppCompat_Light_SearchView
+style Widget_AppCompat_Light_Spinner_DropDown_ActionBar
+style Widget_AppCompat_ListPopupWindow
+style Widget_AppCompat_ListView
+style Widget_AppCompat_ListView_DropDown
+style Widget_AppCompat_ListView_Menu
+style Widget_AppCompat_PopupMenu
+style Widget_AppCompat_PopupMenu_Overflow
+style Widget_AppCompat_PopupWindow
+style Widget_AppCompat_ProgressBar
+style Widget_AppCompat_ProgressBar_Horizontal
+style Widget_AppCompat_RatingBar
+style Widget_AppCompat_RatingBar_Indicator
+style Widget_AppCompat_RatingBar_Small
+style Widget_AppCompat_SearchView
+style Widget_AppCompat_SearchView_ActionBar
+style Widget_AppCompat_SeekBar
+style Widget_AppCompat_SeekBar_Discrete
+style Widget_AppCompat_Spinner
+style Widget_AppCompat_Spinner_DropDown
+style Widget_AppCompat_Spinner_DropDown_ActionBar
+style Widget_AppCompat_Spinner_Underlined
+style Widget_AppCompat_TextView
+style Widget_AppCompat_TextView_SpinnerItem
+style Widget_AppCompat_Toolbar
+style Widget_AppCompat_Toolbar_Button_Navigation
diff --git a/appcompat/appcompat/api/restricted_1.7.0-beta01.txt b/appcompat/appcompat/api/restricted_1.7.0-beta01.txt
new file mode 100644
index 0000000..3afd9d3
--- /dev/null
+++ b/appcompat/appcompat/api/restricted_1.7.0-beta01.txt
@@ -0,0 +1,2288 @@
+// Signature format: 4.0
+package androidx.appcompat.app {
+
+ public abstract class ActionBar {
+ ctor public ActionBar();
+ method public abstract void addOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+ method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!);
+ method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, boolean);
+ method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int);
+ method @Deprecated public abstract void addTab(androidx.appcompat.app.ActionBar.Tab!, int, boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean closeOptionsMenu();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean collapseActionView();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void dispatchMenuVisibilityChanged(boolean);
+ method public abstract android.view.View! getCustomView();
+ method @androidx.appcompat.app.ActionBar.DisplayOptions public abstract int getDisplayOptions();
+ method public float getElevation();
+ method public abstract int getHeight();
+ method public int getHideOffset();
+ method @Deprecated public abstract int getNavigationItemCount();
+ method @Deprecated @androidx.appcompat.app.ActionBar.NavigationMode public abstract int getNavigationMode();
+ method @Deprecated public abstract int getSelectedNavigationIndex();
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab? getSelectedTab();
+ method public abstract CharSequence? getSubtitle();
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! getTabAt(int);
+ method @Deprecated public abstract int getTabCount();
+ method public android.content.Context! getThemedContext();
+ method public abstract CharSequence? getTitle();
+ method public abstract void hide();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean invalidateOptionsMenu();
+ method public boolean isHideOnContentScrollEnabled();
+ method public abstract boolean isShowing();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isTitleTruncated();
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! newTab();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void onConfigurationChanged(android.content.res.Configuration!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean onKeyShortcut(int, android.view.KeyEvent!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean onMenuKeyEvent(android.view.KeyEvent!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean openOptionsMenu();
+ method @Deprecated public abstract void removeAllTabs();
+ method public abstract void removeOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+ method @Deprecated public abstract void removeTab(androidx.appcompat.app.ActionBar.Tab!);
+ method @Deprecated public abstract void removeTabAt(int);
+ method @Deprecated public abstract void selectTab(androidx.appcompat.app.ActionBar.Tab!);
+ method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public abstract void setCustomView(android.view.View!);
+ method public abstract void setCustomView(android.view.View!, androidx.appcompat.app.ActionBar.LayoutParams!);
+ method public abstract void setCustomView(int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setDefaultDisplayHomeAsUpEnabled(boolean);
+ method public abstract void setDisplayHomeAsUpEnabled(boolean);
+ method public abstract void setDisplayOptions(@androidx.appcompat.app.ActionBar.DisplayOptions int);
+ method public abstract void setDisplayOptions(@androidx.appcompat.app.ActionBar.DisplayOptions int, @androidx.appcompat.app.ActionBar.DisplayOptions int);
+ method public abstract void setDisplayShowCustomEnabled(boolean);
+ method public abstract void setDisplayShowHomeEnabled(boolean);
+ method public abstract void setDisplayShowTitleEnabled(boolean);
+ method public abstract void setDisplayUseLogoEnabled(boolean);
+ method public void setElevation(float);
+ method public void setHideOffset(int);
+ method public void setHideOnContentScrollEnabled(boolean);
+ method public void setHomeActionContentDescription(@StringRes int);
+ method public void setHomeActionContentDescription(CharSequence?);
+ method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable?);
+ method public void setHomeAsUpIndicator(@DrawableRes int);
+ method public void setHomeButtonEnabled(boolean);
+ method public abstract void setIcon(android.graphics.drawable.Drawable!);
+ method public abstract void setIcon(@DrawableRes int);
+ method @Deprecated public abstract void setListNavigationCallbacks(android.widget.SpinnerAdapter!, androidx.appcompat.app.ActionBar.OnNavigationListener!);
+ method public abstract void setLogo(android.graphics.drawable.Drawable!);
+ method public abstract void setLogo(@DrawableRes int);
+ method @Deprecated public abstract void setNavigationMode(@androidx.appcompat.app.ActionBar.NavigationMode int);
+ method @Deprecated public abstract void setSelectedNavigationItem(int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setShowHideAnimationEnabled(boolean);
+ method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable!);
+ method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable!);
+ method public abstract void setSubtitle(int);
+ method public abstract void setSubtitle(CharSequence!);
+ method public abstract void setTitle(@StringRes int);
+ method public abstract void setTitle(CharSequence!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setWindowTitle(CharSequence!);
+ method public abstract void show();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.view.ActionMode! startActionMode(androidx.appcompat.view.ActionMode.Callback!);
+ field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4
+ field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10
+ field public static final int DISPLAY_SHOW_HOME = 2; // 0x2
+ field public static final int DISPLAY_SHOW_TITLE = 8; // 0x8
+ field public static final int DISPLAY_USE_LOGO = 1; // 0x1
+ field @Deprecated public static final int NAVIGATION_MODE_LIST = 1; // 0x1
+ field @Deprecated public static final int NAVIGATION_MODE_STANDARD = 0; // 0x0
+ field @Deprecated public static final int NAVIGATION_MODE_TABS = 2; // 0x2
+ }
+
+ @IntDef(flag=true, value={androidx.appcompat.app.ActionBar.DISPLAY_USE_LOGO, androidx.appcompat.app.ActionBar.DISPLAY_SHOW_HOME, androidx.appcompat.app.ActionBar.DISPLAY_HOME_AS_UP, androidx.appcompat.app.ActionBar.DISPLAY_SHOW_TITLE, androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ActionBar.DisplayOptions {
+ }
+
+ public static class ActionBar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+ ctor public ActionBar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+ ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public ActionBar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+ ctor public ActionBar.LayoutParams(int);
+ ctor public ActionBar.LayoutParams(int, int);
+ ctor public ActionBar.LayoutParams(int, int, int);
+ field public int gravity;
+ }
+
+ @IntDef({androidx.appcompat.app.ActionBar.NAVIGATION_MODE_STANDARD, androidx.appcompat.app.ActionBar.NAVIGATION_MODE_LIST, androidx.appcompat.app.ActionBar.NAVIGATION_MODE_TABS}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ActionBar.NavigationMode {
+ }
+
+ public static interface ActionBar.OnMenuVisibilityListener {
+ method public void onMenuVisibilityChanged(boolean);
+ }
+
+ @Deprecated public static interface ActionBar.OnNavigationListener {
+ method @Deprecated public boolean onNavigationItemSelected(int, long);
+ }
+
+ @Deprecated public abstract static class ActionBar.Tab {
+ ctor @Deprecated public ActionBar.Tab();
+ method @Deprecated public abstract CharSequence! getContentDescription();
+ method @Deprecated public abstract android.view.View! getCustomView();
+ method @Deprecated public abstract android.graphics.drawable.Drawable! getIcon();
+ method @Deprecated public abstract int getPosition();
+ method @Deprecated public abstract Object! getTag();
+ method @Deprecated public abstract CharSequence! getText();
+ method @Deprecated public abstract void select();
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(@StringRes int);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setContentDescription(CharSequence!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(android.view.View!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setCustomView(int);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(android.graphics.drawable.Drawable!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setIcon(@DrawableRes int);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTabListener(androidx.appcompat.app.ActionBar.TabListener!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setTag(Object!);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(int);
+ method @Deprecated public abstract androidx.appcompat.app.ActionBar.Tab! setText(CharSequence!);
+ field @Deprecated public static final int INVALID_POSITION = -1; // 0xffffffff
+ }
+
+ @Deprecated public static interface ActionBar.TabListener {
+ method @Deprecated public void onTabReselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+ method @Deprecated public void onTabSelected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+ method @Deprecated public void onTabUnselected(androidx.appcompat.app.ActionBar.Tab!, androidx.fragment.app.FragmentTransaction!);
+ }
+
+ public class ActionBarDrawerToggle implements androidx.drawerlayout.widget.DrawerLayout.DrawerListener {
+ ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, androidx.appcompat.widget.Toolbar!, @StringRes int, @StringRes int);
+ ctor public ActionBarDrawerToggle(android.app.Activity!, androidx.drawerlayout.widget.DrawerLayout!, @StringRes int, @StringRes int);
+ method public androidx.appcompat.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
+ method public android.view.View.OnClickListener! getToolbarNavigationClickListener();
+ method public boolean isDrawerIndicatorEnabled();
+ method public boolean isDrawerSlideAnimationEnabled();
+ method public void onConfigurationChanged(android.content.res.Configuration!);
+ method public void onDrawerClosed(android.view.View!);
+ method public void onDrawerOpened(android.view.View!);
+ method public void onDrawerSlide(android.view.View!, float);
+ method public void onDrawerStateChanged(int);
+ method public boolean onOptionsItemSelected(android.view.MenuItem!);
+ method public void setDrawerArrowDrawable(androidx.appcompat.graphics.drawable.DrawerArrowDrawable);
+ method public void setDrawerIndicatorEnabled(boolean);
+ method public void setDrawerSlideAnimationEnabled(boolean);
+ method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable!);
+ method public void setHomeAsUpIndicator(int);
+ method public void setToolbarNavigationClickListener(android.view.View.OnClickListener!);
+ method public void syncState();
+ }
+
+ public static interface ActionBarDrawerToggle.Delegate {
+ method public android.content.Context! getActionBarThemedContext();
+ method public android.graphics.drawable.Drawable! getThemeUpIndicator();
+ method public boolean isNavigationVisible();
+ method public void setActionBarDescription(@StringRes int);
+ method public void setActionBarUpIndicator(android.graphics.drawable.Drawable!, @StringRes int);
+ }
+
+ public static interface ActionBarDrawerToggle.DelegateProvider {
+ method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+ }
+
+ public class AlertDialog extends androidx.appcompat.app.AppCompatDialog implements android.content.DialogInterface {
+ ctor protected AlertDialog(android.content.Context);
+ ctor protected AlertDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener?);
+ ctor protected AlertDialog(android.content.Context, @StyleRes int);
+ method public android.widget.Button! getButton(int);
+ method public android.widget.ListView! getListView();
+ method public void setButton(int, CharSequence!, android.content.DialogInterface.OnClickListener!);
+ method public void setButton(int, CharSequence!, android.graphics.drawable.Drawable!, android.content.DialogInterface.OnClickListener!);
+ method public void setButton(int, CharSequence!, android.os.Message!);
+ method public void setCustomTitle(android.view.View!);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setIcon(int);
+ method public void setIconAttribute(int);
+ method public void setMessage(CharSequence!);
+ method public void setView(android.view.View!);
+ method public void setView(android.view.View!, int, int, int, int);
+ }
+
+ public static class AlertDialog.Builder {
+ ctor public AlertDialog.Builder(android.content.Context);
+ ctor public AlertDialog.Builder(android.content.Context, @StyleRes int);
+ method public androidx.appcompat.app.AlertDialog create();
+ method public android.content.Context getContext();
+ method public androidx.appcompat.app.AlertDialog.Builder! setAdapter(android.widget.ListAdapter!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setCancelable(boolean);
+ method public androidx.appcompat.app.AlertDialog.Builder! setCursor(android.database.Cursor!, android.content.DialogInterface.OnClickListener!, String!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setCustomTitle(android.view.View?);
+ method public androidx.appcompat.app.AlertDialog.Builder! setIcon(android.graphics.drawable.Drawable?);
+ method public androidx.appcompat.app.AlertDialog.Builder! setIcon(@DrawableRes int);
+ method public androidx.appcompat.app.AlertDialog.Builder! setIconAttribute(@AttrRes int);
+ method @Deprecated public androidx.appcompat.app.AlertDialog.Builder! setInverseBackgroundForced(boolean);
+ method public androidx.appcompat.app.AlertDialog.Builder! setItems(@ArrayRes int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setItems(CharSequence![]!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMessage(@StringRes int);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMessage(CharSequence?);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(android.database.Cursor!, String!, String!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(@ArrayRes int, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setMultiChoiceItems(CharSequence![]!, boolean[]!, android.content.DialogInterface.OnMultiChoiceClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNegativeButtonIcon(android.graphics.drawable.Drawable!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setNeutralButtonIcon(android.graphics.drawable.Drawable!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setOnCancelListener(android.content.DialogInterface.OnCancelListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setOnDismissListener(android.content.DialogInterface.OnDismissListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setOnKeyListener(android.content.DialogInterface.OnKeyListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(@StringRes int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButton(CharSequence!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setPositiveButtonIcon(android.graphics.drawable.Drawable!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.app.AlertDialog.Builder! setRecycleOnMeasureEnabled(boolean);
+ method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.database.Cursor!, int, String!, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(android.widget.ListAdapter!, int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(@ArrayRes int, int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setSingleChoiceItems(CharSequence![]!, int, android.content.DialogInterface.OnClickListener!);
+ method public androidx.appcompat.app.AlertDialog.Builder! setTitle(@StringRes int);
+ method public androidx.appcompat.app.AlertDialog.Builder! setTitle(CharSequence?);
+ method public androidx.appcompat.app.AlertDialog.Builder! setView(android.view.View!);
+ method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.app.AlertDialog.Builder! setView(android.view.View!, int, int, int, int);
+ method public androidx.appcompat.app.AlertDialog.Builder! setView(int);
+ method public androidx.appcompat.app.AlertDialog! show();
+ }
+
+ public class AppCompatActivity extends androidx.fragment.app.FragmentActivity implements androidx.appcompat.app.ActionBarDrawerToggle.DelegateProvider androidx.appcompat.app.AppCompatCallback androidx.core.app.TaskStackBuilder.SupportParentable {
+ ctor public AppCompatActivity();
+ ctor @ContentView public AppCompatActivity(@LayoutRes int);
+ method public androidx.appcompat.app.AppCompatDelegate getDelegate();
+ method public androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+ method public androidx.appcompat.app.ActionBar? getSupportActionBar();
+ method public android.content.Intent? getSupportParentActivityIntent();
+ method public void onCreateSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+ method protected void onLocalesChanged(androidx.core.os.LocaleListCompat);
+ method public final boolean onMenuItemSelected(int, android.view.MenuItem);
+ method protected void onNightModeChanged(@androidx.appcompat.app.AppCompatDelegate.NightMode int);
+ method public void onPrepareSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder);
+ method @CallSuper public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode);
+ method @CallSuper public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode);
+ method @Deprecated public void onSupportContentChanged();
+ method public boolean onSupportNavigateUp();
+ method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+ method public void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+ method @Deprecated public void setSupportProgress(int);
+ method @Deprecated public void setSupportProgressBarIndeterminate(boolean);
+ method @Deprecated public void setSupportProgressBarIndeterminateVisibility(boolean);
+ method @Deprecated public void setSupportProgressBarVisibility(boolean);
+ method public androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+ method public void supportInvalidateOptionsMenu();
+ method public void supportNavigateUpTo(android.content.Intent);
+ method public boolean supportRequestWindowFeature(int);
+ method public boolean supportShouldUpRecreateTask(android.content.Intent);
+ }
+
+ public interface AppCompatCallback {
+ method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+ method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+ method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+ }
+
+ public abstract class AppCompatDelegate {
+ method public abstract void addContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+ method public abstract boolean applyDayNight();
+ method @Deprecated public void attachBaseContext(android.content.Context!);
+ method @CallSuper public android.content.Context attachBaseContext2(android.content.Context);
+ method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+ method public static androidx.appcompat.app.AppCompatDelegate create(android.app.Dialog, androidx.appcompat.app.AppCompatCallback?);
+ method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.app.Activity, androidx.appcompat.app.AppCompatCallback?);
+ method public static androidx.appcompat.app.AppCompatDelegate create(android.content.Context, android.view.Window, androidx.appcompat.app.AppCompatCallback?);
+ method public abstract android.view.View! createView(android.view.View?, String!, android.content.Context, android.util.AttributeSet);
+ method public abstract <T extends android.view.View> T? findViewById(@IdRes int);
+ method @AnyThread public static androidx.core.os.LocaleListCompat getApplicationLocales();
+ method public android.content.Context? getContextForDelegate();
+ method @androidx.appcompat.app.AppCompatDelegate.NightMode public static int getDefaultNightMode();
+ method public abstract androidx.appcompat.app.ActionBarDrawerToggle.Delegate? getDrawerToggleDelegate();
+ method @androidx.appcompat.app.AppCompatDelegate.NightMode public int getLocalNightMode();
+ method public abstract android.view.MenuInflater! getMenuInflater();
+ method public abstract androidx.appcompat.app.ActionBar? getSupportActionBar();
+ method public abstract boolean hasWindowFeature(int);
+ method public abstract void installViewFactory();
+ method public abstract void invalidateOptionsMenu();
+ method public static boolean isCompatVectorFromResourcesEnabled();
+ method public abstract boolean isHandleNativeActionModesEnabled();
+ method public abstract void onConfigurationChanged(android.content.res.Configuration!);
+ method public abstract void onCreate(android.os.Bundle!);
+ method public abstract void onDestroy();
+ method public abstract void onPostCreate(android.os.Bundle!);
+ method public abstract void onPostResume();
+ method public abstract void onSaveInstanceState(android.os.Bundle!);
+ method public abstract void onStart();
+ method public abstract void onStop();
+ method public abstract boolean requestWindowFeature(int);
+ method public static void setApplicationLocales(androidx.core.os.LocaleListCompat);
+ method public static void setCompatVectorFromResourcesEnabled(boolean);
+ method public abstract void setContentView(android.view.View!);
+ method public abstract void setContentView(android.view.View!, android.view.ViewGroup.LayoutParams!);
+ method public abstract void setContentView(@LayoutRes int);
+ method public static void setDefaultNightMode(@androidx.appcompat.app.AppCompatDelegate.NightMode int);
+ method public abstract void setHandleNativeActionModesEnabled(boolean);
+ method public abstract void setLocalNightMode(@androidx.appcompat.app.AppCompatDelegate.NightMode int);
+ method @CallSuper @RequiresApi(33) public void setOnBackInvokedDispatcher(android.window.OnBackInvokedDispatcher?);
+ method public abstract void setSupportActionBar(androidx.appcompat.widget.Toolbar?);
+ method public void setTheme(@StyleRes int);
+ method public abstract void setTitle(CharSequence?);
+ method public abstract androidx.appcompat.view.ActionMode? startSupportActionMode(androidx.appcompat.view.ActionMode.Callback);
+ field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+ field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
+ field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+ field @Deprecated public static final int MODE_NIGHT_AUTO = 0; // 0x0
+ field public static final int MODE_NIGHT_AUTO_BATTERY = 3; // 0x3
+ field @Deprecated public static final int MODE_NIGHT_AUTO_TIME = 0; // 0x0
+ field public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; // 0xffffffff
+ field public static final int MODE_NIGHT_NO = 1; // 0x1
+ field public static final int MODE_NIGHT_UNSPECIFIED = -100; // 0xffffff9c
+ field public static final int MODE_NIGHT_YES = 2; // 0x2
+ }
+
+ @IntDef({androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_AUTO_TIME, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_UNSPECIFIED, androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AppCompatDelegate.NightMode {
+ }
+
+ public class AppCompatDialog extends androidx.activity.ComponentDialog implements androidx.appcompat.app.AppCompatCallback {
+ ctor public AppCompatDialog(android.content.Context);
+ ctor protected AppCompatDialog(android.content.Context, boolean, android.content.DialogInterface.OnCancelListener?);
+ ctor public AppCompatDialog(android.content.Context, int);
+ method public androidx.appcompat.app.AppCompatDelegate getDelegate();
+ method public androidx.appcompat.app.ActionBar! getSupportActionBar();
+ method public void onSupportActionModeFinished(androidx.appcompat.view.ActionMode!);
+ method public void onSupportActionModeStarted(androidx.appcompat.view.ActionMode!);
+ method public androidx.appcompat.view.ActionMode? onWindowStartingSupportActionMode(androidx.appcompat.view.ActionMode.Callback!);
+ method public boolean supportRequestWindowFeature(int);
+ }
+
+ public class AppCompatDialogFragment extends androidx.fragment.app.DialogFragment {
+ ctor public AppCompatDialogFragment();
+ ctor public AppCompatDialogFragment(@LayoutRes int);
+ }
+
+ public class AppCompatViewInflater {
+ ctor public AppCompatViewInflater();
+ method protected androidx.appcompat.widget.AppCompatAutoCompleteTextView createAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatButton createButton(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatCheckBox createCheckBox(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatCheckedTextView createCheckedTextView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatEditText createEditText(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatImageButton createImageButton(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatImageView createImageView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView createMultiAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatRadioButton createRadioButton(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatRatingBar createRatingBar(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatSeekBar createSeekBar(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatSpinner createSpinner(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatTextView createTextView(android.content.Context!, android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.AppCompatToggleButton createToggleButton(android.content.Context!, android.util.AttributeSet!);
+ method protected android.view.View? createView(android.content.Context!, String!, android.util.AttributeSet!);
+ method public final android.view.View? createView(android.view.View?, String, android.content.Context, android.util.AttributeSet, boolean, boolean, boolean, boolean);
+ }
+
+ public final class AppLocalesMetadataHolderService extends android.app.Service {
+ ctor public AppLocalesMetadataHolderService();
+ method public static android.content.pm.ServiceInfo getServiceInfo(android.content.Context) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public android.os.IBinder onBind(android.content.Intent);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class WindowDecorActionBar extends androidx.appcompat.app.ActionBar implements androidx.appcompat.widget.ActionBarOverlayLayout.ActionBarVisibilityCallback {
+ ctor public WindowDecorActionBar(android.app.Activity!, boolean);
+ ctor public WindowDecorActionBar(android.app.Dialog!);
+ ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public WindowDecorActionBar(android.view.View!);
+ method public void addOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+ method public void addTab(androidx.appcompat.app.ActionBar.Tab!);
+ method public void addTab(androidx.appcompat.app.ActionBar.Tab!, boolean);
+ method public void addTab(androidx.appcompat.app.ActionBar.Tab!, int);
+ method public void addTab(androidx.appcompat.app.ActionBar.Tab!, int, boolean);
+ method public void animateToMode(boolean);
+ method public void doHide(boolean);
+ method public void doShow(boolean);
+ method public void enableContentAnimations(boolean);
+ method public android.view.View! getCustomView();
+ method public int getDisplayOptions();
+ method public int getHeight();
+ method public int getNavigationItemCount();
+ method public int getNavigationMode();
+ method public int getSelectedNavigationIndex();
+ method public androidx.appcompat.app.ActionBar.Tab! getSelectedTab();
+ method public CharSequence! getSubtitle();
+ method public androidx.appcompat.app.ActionBar.Tab! getTabAt(int);
+ method public int getTabCount();
+ method public CharSequence! getTitle();
+ method public boolean hasIcon();
+ method public boolean hasLogo();
+ method public void hide();
+ method public void hideForSystem();
+ method public boolean isShowing();
+ method public androidx.appcompat.app.ActionBar.Tab! newTab();
+ method public void onContentScrollStarted();
+ method public void onContentScrollStopped();
+ method public void onWindowVisibilityChanged(int);
+ method public void removeAllTabs();
+ method public void removeOnMenuVisibilityListener(androidx.appcompat.app.ActionBar.OnMenuVisibilityListener!);
+ method public void removeTab(androidx.appcompat.app.ActionBar.Tab!);
+ method public void removeTabAt(int);
+ method public boolean requestFocus();
+ method public void selectTab(androidx.appcompat.app.ActionBar.Tab!);
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
+ method public void setCustomView(android.view.View!);
+ method public void setCustomView(android.view.View!, androidx.appcompat.app.ActionBar.LayoutParams!);
+ method public void setCustomView(int);
+ method public void setDisplayHomeAsUpEnabled(boolean);
+ method public void setDisplayOptions(int);
+ method public void setDisplayOptions(int, int);
+ method public void setDisplayShowCustomEnabled(boolean);
+ method public void setDisplayShowHomeEnabled(boolean);
+ method public void setDisplayShowTitleEnabled(boolean);
+ method public void setDisplayUseLogoEnabled(boolean);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setIcon(int);
+ method public void setListNavigationCallbacks(android.widget.SpinnerAdapter!, androidx.appcompat.app.ActionBar.OnNavigationListener!);
+ method public void setLogo(android.graphics.drawable.Drawable!);
+ method public void setLogo(int);
+ method public void setNavigationMode(int);
+ method public void setSelectedNavigationItem(int);
+ method public void setSubtitle(int);
+ method public void setSubtitle(CharSequence!);
+ method public void setTitle(int);
+ method public void setTitle(CharSequence!);
+ method public void show();
+ method public void showForSystem();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class WindowDecorActionBar.ActionModeImpl extends androidx.appcompat.view.ActionMode implements androidx.appcompat.view.menu.MenuBuilder.Callback {
+ ctor public WindowDecorActionBar.ActionModeImpl(android.content.Context!, androidx.appcompat.view.ActionMode.Callback!);
+ method public boolean dispatchOnCreate();
+ method public void finish();
+ method public android.view.View! getCustomView();
+ method public android.view.Menu! getMenu();
+ method public android.view.MenuInflater! getMenuInflater();
+ method public CharSequence! getSubtitle();
+ method public CharSequence! getTitle();
+ method public void invalidate();
+ method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+ method public void onCloseSubMenu(androidx.appcompat.view.menu.SubMenuBuilder!);
+ method public boolean onMenuItemSelected(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+ method public void onMenuModeChange(androidx.appcompat.view.menu.MenuBuilder);
+ method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+ method public void setCustomView(android.view.View!);
+ method public void setSubtitle(int);
+ method public void setSubtitle(CharSequence!);
+ method public void setTitle(int);
+ method public void setTitle(CharSequence!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class WindowDecorActionBar.TabImpl extends androidx.appcompat.app.ActionBar.Tab {
+ ctor public WindowDecorActionBar.TabImpl();
+ method public androidx.appcompat.app.ActionBar.TabListener! getCallback();
+ method public CharSequence! getContentDescription();
+ method public android.view.View! getCustomView();
+ method public android.graphics.drawable.Drawable! getIcon();
+ method public int getPosition();
+ method public Object! getTag();
+ method public CharSequence! getText();
+ method public void select();
+ method public androidx.appcompat.app.ActionBar.Tab! setContentDescription(int);
+ method public androidx.appcompat.app.ActionBar.Tab! setContentDescription(CharSequence!);
+ method public androidx.appcompat.app.ActionBar.Tab! setCustomView(android.view.View!);
+ method public androidx.appcompat.app.ActionBar.Tab! setCustomView(int);
+ method public androidx.appcompat.app.ActionBar.Tab! setIcon(android.graphics.drawable.Drawable!);
+ method public androidx.appcompat.app.ActionBar.Tab! setIcon(int);
+ method public void setPosition(int);
+ method public androidx.appcompat.app.ActionBar.Tab! setTabListener(androidx.appcompat.app.ActionBar.TabListener!);
+ method public androidx.appcompat.app.ActionBar.Tab! setTag(Object!);
+ method public androidx.appcompat.app.ActionBar.Tab! setText(int);
+ method public androidx.appcompat.app.ActionBar.Tab! setText(CharSequence!);
+ }
+
+}
+
+package androidx.appcompat.graphics.drawable {
+
+ public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
+ ctor public DrawerArrowDrawable(android.content.Context!);
+ method public void draw(android.graphics.Canvas);
+ method public float getArrowHeadLength();
+ method public float getArrowShaftLength();
+ method public float getBarLength();
+ method public float getBarThickness();
+ method @ColorInt public int getColor();
+ method @androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ArrowDirection public int getDirection();
+ method public float getGapSize();
+ method public int getOpacity();
+ method public final android.graphics.Paint! getPaint();
+ method @FloatRange(from=0.0, to=1.0) public float getProgress();
+ method public boolean isSpinEnabled();
+ method public void setAlpha(int);
+ method public void setArrowHeadLength(float);
+ method public void setArrowShaftLength(float);
+ method public void setBarLength(float);
+ method public void setBarThickness(float);
+ method public void setColor(@ColorInt int);
+ method public void setColorFilter(android.graphics.ColorFilter!);
+ method public void setDirection(@androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ArrowDirection int);
+ method public void setGapSize(float);
+ method public void setProgress(@FloatRange(from=0.0, to=1.0) float);
+ method public void setSpinEnabled(boolean);
+ method public void setVerticalMirror(boolean);
+ field public static final int ARROW_DIRECTION_END = 3; // 0x3
+ field public static final int ARROW_DIRECTION_LEFT = 0; // 0x0
+ field public static final int ARROW_DIRECTION_RIGHT = 1; // 0x1
+ field public static final int ARROW_DIRECTION_START = 2; // 0x2
+ }
+
+ @IntDef({androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ARROW_DIRECTION_LEFT, androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ARROW_DIRECTION_RIGHT, androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ARROW_DIRECTION_START, androidx.appcompat.graphics.drawable.DrawerArrowDrawable.ARROW_DIRECTION_END}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface DrawerArrowDrawable.ArrowDirection {
+ }
+
+}
+
+package androidx.appcompat.text {
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AllCapsTransformationMethod implements android.text.method.TransformationMethod {
+ ctor public AllCapsTransformationMethod(android.content.Context!);
+ method public CharSequence! getTransformation(CharSequence!, android.view.View!);
+ method public void onFocusChanged(android.view.View!, CharSequence!, boolean, int, android.graphics.Rect!);
+ }
+
+}
+
+package androidx.appcompat.view {
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionBarPolicy {
+ method public boolean enableHomeButtonByDefault();
+ method public static androidx.appcompat.view.ActionBarPolicy! get(android.content.Context!);
+ method public int getEmbeddedMenuWidthLimit();
+ method public int getMaxActionButtons();
+ method public int getStackedTabMaxWidth();
+ method public int getTabContainerHeight();
+ method public boolean hasEmbeddedTabs();
+ method public boolean showsOverflowMenuButton();
+ }
+
+ public abstract class ActionMode {
+ ctor public ActionMode();
+ method public abstract void finish();
+ method public abstract android.view.View! getCustomView();
+ method public abstract android.view.Menu! getMenu();
+ method public abstract android.view.MenuInflater! getMenuInflater();
+ method public abstract CharSequence! getSubtitle();
+ method public Object! getTag();
+ method public abstract CharSequence! getTitle();
+ method public boolean getTitleOptionalHint();
+ method public abstract void invalidate();
+ method public boolean isTitleOptional();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isUiFocusable();
+ method public abstract void setCustomView(android.view.View!);
+ method public abstract void setSubtitle(int);
+ method public abstract void setSubtitle(CharSequence!);
+ method public void setTag(Object!);
+ method public abstract void setTitle(int);
+ method public abstract void setTitle(CharSequence!);
+ method public void setTitleOptionalHint(boolean);
+ }
+
+ public static interface ActionMode.Callback {
+ method public boolean onActionItemClicked(androidx.appcompat.view.ActionMode!, android.view.MenuItem!);
+ method public boolean onCreateActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+ method public void onDestroyActionMode(androidx.appcompat.view.ActionMode!);
+ method public boolean onPrepareActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+ }
+
+ @Deprecated public interface CollapsibleActionView {
+ method @Deprecated public void onActionViewCollapsed();
+ method @Deprecated public void onActionViewExpanded();
+ }
+
+ public class ContextThemeWrapper extends android.content.ContextWrapper {
+ ctor public ContextThemeWrapper();
+ ctor public ContextThemeWrapper(android.content.Context!, android.content.res.Resources.Theme!);
+ ctor public ContextThemeWrapper(android.content.Context!, @StyleRes int);
+ method public void applyOverrideConfiguration(android.content.res.Configuration!);
+ method public int getThemeResId();
+ method protected void onApplyThemeResource(android.content.res.Resources.Theme!, int, boolean);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class StandaloneActionMode extends androidx.appcompat.view.ActionMode implements androidx.appcompat.view.menu.MenuBuilder.Callback {
+ ctor public StandaloneActionMode(android.content.Context!, androidx.appcompat.widget.ActionBarContextView!, androidx.appcompat.view.ActionMode.Callback!, boolean);
+ method public void finish();
+ method public android.view.View! getCustomView();
+ method public android.view.Menu! getMenu();
+ method public android.view.MenuInflater! getMenuInflater();
+ method public CharSequence! getSubtitle();
+ method public CharSequence! getTitle();
+ method public void invalidate();
+ method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+ method public void onCloseSubMenu(androidx.appcompat.view.menu.SubMenuBuilder!);
+ method public boolean onMenuItemSelected(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+ method public void onMenuModeChange(androidx.appcompat.view.menu.MenuBuilder);
+ method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+ method public void setCustomView(android.view.View!);
+ method public void setSubtitle(int);
+ method public void setSubtitle(CharSequence!);
+ method public void setTitle(int);
+ method public void setTitle(CharSequence!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class SupportActionModeWrapper extends android.view.ActionMode {
+ ctor public SupportActionModeWrapper(android.content.Context!, androidx.appcompat.view.ActionMode!);
+ method public void finish();
+ method public android.view.View! getCustomView();
+ method public android.view.Menu! getMenu();
+ method public android.view.MenuInflater! getMenuInflater();
+ method public CharSequence! getSubtitle();
+ method public CharSequence! getTitle();
+ method public void invalidate();
+ method public void setCustomView(android.view.View!);
+ method public void setSubtitle(int);
+ method public void setSubtitle(CharSequence!);
+ method public void setTitle(int);
+ method public void setTitle(CharSequence!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class SupportActionModeWrapper.CallbackWrapper implements androidx.appcompat.view.ActionMode.Callback {
+ ctor public SupportActionModeWrapper.CallbackWrapper(android.content.Context!, android.view.ActionMode.Callback!);
+ method public android.view.ActionMode! getActionModeWrapper(androidx.appcompat.view.ActionMode!);
+ method public boolean onActionItemClicked(androidx.appcompat.view.ActionMode!, android.view.MenuItem!);
+ method public boolean onCreateActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+ method public void onDestroyActionMode(androidx.appcompat.view.ActionMode!);
+ method public boolean onPrepareActionMode(androidx.appcompat.view.ActionMode!, android.view.Menu!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class SupportMenuInflater extends android.view.MenuInflater {
+ ctor public SupportMenuInflater(android.content.Context!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ViewPropertyAnimatorCompatSet {
+ ctor public ViewPropertyAnimatorCompatSet();
+ method public void cancel();
+ method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! play(androidx.core.view.ViewPropertyAnimatorCompat!);
+ method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! playSequentially(androidx.core.view.ViewPropertyAnimatorCompat!, androidx.core.view.ViewPropertyAnimatorCompat!);
+ method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! setDuration(long);
+ method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! setInterpolator(android.view.animation.Interpolator!);
+ method public androidx.appcompat.view.ViewPropertyAnimatorCompatSet! setListener(androidx.core.view.ViewPropertyAnimatorListener!);
+ method public void start();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class WindowCallbackWrapper implements android.view.Window.Callback {
+ ctor public WindowCallbackWrapper(android.view.Window.Callback!);
+ method public boolean dispatchGenericMotionEvent(android.view.MotionEvent!);
+ method public boolean dispatchKeyEvent(android.view.KeyEvent!);
+ method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent!);
+ method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent!);
+ method public boolean dispatchTouchEvent(android.view.MotionEvent!);
+ method public boolean dispatchTrackballEvent(android.view.MotionEvent!);
+ method public final android.view.Window.Callback! getWrapped();
+ method public void onActionModeFinished(android.view.ActionMode!);
+ method public void onActionModeStarted(android.view.ActionMode!);
+ method public void onAttachedToWindow();
+ method public void onContentChanged();
+ method public boolean onCreatePanelMenu(int, android.view.Menu!);
+ method public android.view.View! onCreatePanelView(int);
+ method public void onDetachedFromWindow();
+ method public boolean onMenuItemSelected(int, android.view.MenuItem!);
+ method public boolean onMenuOpened(int, android.view.Menu!);
+ method public void onPanelClosed(int, android.view.Menu!);
+ method public boolean onPreparePanel(int, android.view.View!, android.view.Menu!);
+ method public boolean onSearchRequested();
+ method @RequiresApi(23) public boolean onSearchRequested(android.view.SearchEvent!);
+ method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams!);
+ method public void onWindowFocusChanged(boolean);
+ method public android.view.ActionMode! onWindowStartingActionMode(android.view.ActionMode.Callback!);
+ method @RequiresApi(23) public android.view.ActionMode! onWindowStartingActionMode(android.view.ActionMode.Callback!, int);
+ }
+
+}
+
+package androidx.appcompat.view.menu {
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionMenuItem implements androidx.core.internal.view.SupportMenuItem {
+ ctor public ActionMenuItem(android.content.Context!, int, int, int, int, CharSequence!);
+ method public boolean collapseActionView();
+ method public boolean expandActionView();
+ method public android.view.ActionProvider! getActionProvider();
+ method public android.view.View! getActionView();
+ method public char getAlphabeticShortcut();
+ method public int getGroupId();
+ method public android.graphics.drawable.Drawable! getIcon();
+ method public android.content.Intent! getIntent();
+ method public int getItemId();
+ method public android.view.ContextMenu.ContextMenuInfo! getMenuInfo();
+ method public char getNumericShortcut();
+ method public int getOrder();
+ method public android.view.SubMenu! getSubMenu();
+ method public androidx.core.view.ActionProvider! getSupportActionProvider();
+ method public CharSequence! getTitle();
+ method public CharSequence! getTitleCondensed();
+ method public boolean hasSubMenu();
+ method public boolean invoke();
+ method public boolean isActionViewExpanded();
+ method public boolean isCheckable();
+ method public boolean isChecked();
+ method public boolean isEnabled();
+ method public boolean isVisible();
+ method public boolean requiresActionButton();
+ method public boolean requiresOverflow();
+ method public android.view.MenuItem! setActionProvider(android.view.ActionProvider!);
+ method public androidx.core.internal.view.SupportMenuItem setActionView(android.view.View!);
+ method public androidx.core.internal.view.SupportMenuItem setActionView(int);
+ method public android.view.MenuItem! setAlphabeticShortcut(char);
+ method public android.view.MenuItem! setCheckable(boolean);
+ method public android.view.MenuItem! setChecked(boolean);
+ method public androidx.core.internal.view.SupportMenuItem setContentDescription(CharSequence!);
+ method public android.view.MenuItem! setEnabled(boolean);
+ method public androidx.appcompat.view.menu.ActionMenuItem! setExclusiveCheckable(boolean);
+ method public android.view.MenuItem! setIcon(android.graphics.drawable.Drawable!);
+ method public android.view.MenuItem! setIcon(int);
+ method public android.view.MenuItem! setIntent(android.content.Intent!);
+ method public android.view.MenuItem! setNumericShortcut(char);
+ method public android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem.OnActionExpandListener!);
+ method public android.view.MenuItem! setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener!);
+ method public android.view.MenuItem! setShortcut(char, char);
+ method public void setShowAsAction(int);
+ method public androidx.core.internal.view.SupportMenuItem setShowAsActionFlags(int);
+ method public androidx.core.internal.view.SupportMenuItem setSupportActionProvider(androidx.core.view.ActionProvider!);
+ method public android.view.MenuItem! setTitle(int);
+ method public android.view.MenuItem! setTitle(CharSequence!);
+ method public android.view.MenuItem! setTitleCondensed(CharSequence!);
+ method public androidx.core.internal.view.SupportMenuItem setTooltipText(CharSequence!);
+ method public android.view.MenuItem! setVisible(boolean);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionMenuItemView extends androidx.appcompat.widget.AppCompatTextView implements androidx.appcompat.widget.ActionMenuView.ActionMenuChildView androidx.appcompat.view.menu.MenuView.ItemView android.view.View.OnClickListener {
+ ctor public ActionMenuItemView(android.content.Context!);
+ ctor public ActionMenuItemView(android.content.Context!, android.util.AttributeSet!);
+ ctor public ActionMenuItemView(android.content.Context!, android.util.AttributeSet!, int);
+ method public androidx.appcompat.view.menu.MenuItemImpl! getItemData();
+ method public boolean hasText();
+ method public void initialize(androidx.appcompat.view.menu.MenuItemImpl!, int);
+ method public boolean needsDividerAfter();
+ method public boolean needsDividerBefore();
+ method public void onClick(android.view.View!);
+ method public void onConfigurationChanged(android.content.res.Configuration!);
+ method public boolean prefersCondensedTitle();
+ method public void setCheckable(boolean);
+ method public void setChecked(boolean);
+ method public void setExpandedFormat(boolean);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setItemInvoker(androidx.appcompat.view.menu.MenuBuilder.ItemInvoker!);
+ method public void setPopupCallback(androidx.appcompat.view.menu.ActionMenuItemView.PopupCallback!);
+ method public void setShortcut(boolean, char);
+ method public void setTitle(CharSequence!);
+ method public boolean showsIcon();
+ }
+
+ public abstract static class ActionMenuItemView.PopupCallback {
+ ctor public ActionMenuItemView.PopupCallback();
+ method public abstract androidx.appcompat.view.menu.ShowableListMenu! getPopup();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class BaseMenuPresenter implements androidx.appcompat.view.menu.MenuPresenter {
+ ctor public BaseMenuPresenter(android.content.Context!, int, int);
+ method protected void addItemView(android.view.View!, int);
+ method public abstract void bindItemView(androidx.appcompat.view.menu.MenuItemImpl!, androidx.appcompat.view.menu.MenuView.ItemView!);
+ method public boolean collapseItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+ method public androidx.appcompat.view.menu.MenuView.ItemView! createItemView(android.view.ViewGroup!);
+ method public boolean expandItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+ method protected boolean filterLeftoverView(android.view.ViewGroup!, int);
+ method public boolean flagActionItems();
+ method public androidx.appcompat.view.menu.MenuPresenter.Callback! getCallback();
+ method public int getId();
+ method public android.view.View! getItemView(androidx.appcompat.view.menu.MenuItemImpl!, android.view.View!, android.view.ViewGroup!);
+ method public androidx.appcompat.view.menu.MenuView! getMenuView(android.view.ViewGroup!);
+ method public void initForMenu(android.content.Context!, androidx.appcompat.view.menu.MenuBuilder!);
+ method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+ method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+ method public void setCallback(androidx.appcompat.view.menu.MenuPresenter.Callback!);
+ method public void setId(int);
+ method public boolean shouldIncludeItem(int, androidx.appcompat.view.menu.MenuItemImpl!);
+ method public void updateMenuView(boolean);
+ field protected android.content.Context! mContext;
+ field protected android.view.LayoutInflater! mInflater;
+ field protected androidx.appcompat.view.menu.MenuBuilder! mMenu;
+ field protected androidx.appcompat.view.menu.MenuView! mMenuView;
+ field protected android.content.Context! mSystemContext;
+ field protected android.view.LayoutInflater! mSystemInflater;
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ExpandedMenuView extends android.widget.ListView implements android.widget.AdapterView.OnItemClickListener androidx.appcompat.view.menu.MenuBuilder.ItemInvoker androidx.appcompat.view.menu.MenuView {
+ ctor public ExpandedMenuView(android.content.Context!, android.util.AttributeSet!);
+ ctor public ExpandedMenuView(android.content.Context!, android.util.AttributeSet!, int);
+ method public int getWindowAnimations();
+ method public void initialize(androidx.appcompat.view.menu.MenuBuilder!);
+ method public boolean invokeItem(androidx.appcompat.view.menu.MenuItemImpl!);
+ method public void onItemClick(android.widget.AdapterView!, android.view.View!, int, long);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ListMenuItemView extends android.widget.LinearLayout implements android.widget.AbsListView.SelectionBoundsAdjuster androidx.appcompat.view.menu.MenuView.ItemView {
+ ctor public ListMenuItemView(android.content.Context!, android.util.AttributeSet!);
+ ctor public ListMenuItemView(android.content.Context!, android.util.AttributeSet!, int);
+ method public void adjustListItemSelectionBounds(android.graphics.Rect!);
+ method public androidx.appcompat.view.menu.MenuItemImpl! getItemData();
+ method public void initialize(androidx.appcompat.view.menu.MenuItemImpl!, int);
+ method public boolean prefersCondensedTitle();
+ method public void setCheckable(boolean);
+ method public void setChecked(boolean);
+ method public void setForceShowIcon(boolean);
+ method public void setGroupDividerEnabled(boolean);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setShortcut(boolean, char);
+ method public void setTitle(CharSequence!);
+ method public boolean showsIcon();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ListMenuPresenter implements android.widget.AdapterView.OnItemClickListener androidx.appcompat.view.menu.MenuPresenter {
+ ctor public ListMenuPresenter(android.content.Context!, int);
+ ctor public ListMenuPresenter(int, int);
+ method public boolean collapseItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+ method public boolean expandItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+ method public boolean flagActionItems();
+ method public android.widget.ListAdapter! getAdapter();
+ method public int getId();
+ method public androidx.appcompat.view.menu.MenuView! getMenuView(android.view.ViewGroup!);
+ method public void initForMenu(android.content.Context!, androidx.appcompat.view.menu.MenuBuilder!);
+ method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+ method public void onItemClick(android.widget.AdapterView<?>!, android.view.View!, int, long);
+ method public void onRestoreInstanceState(android.os.Parcelable!);
+ method public android.os.Parcelable! onSaveInstanceState();
+ method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+ method public void restoreHierarchyState(android.os.Bundle!);
+ method public void saveHierarchyState(android.os.Bundle!);
+ method public void setCallback(androidx.appcompat.view.menu.MenuPresenter.Callback!);
+ method public void setId(int);
+ method public void setItemIndexOffset(int);
+ method public void updateMenuView(boolean);
+ field public static final String VIEWS_TAG = "android:menu:list";
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuAdapter extends android.widget.BaseAdapter {
+ ctor public MenuAdapter(androidx.appcompat.view.menu.MenuBuilder!, android.view.LayoutInflater!, boolean, int);
+ method public androidx.appcompat.view.menu.MenuBuilder! getAdapterMenu();
+ method public int getCount();
+ method public boolean getForceShowIcon();
+ method public androidx.appcompat.view.menu.MenuItemImpl! getItem(int);
+ method public long getItemId(int);
+ method public android.view.View! getView(int, android.view.View!, android.view.ViewGroup!);
+ method public void setForceShowIcon(boolean);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuBuilder implements androidx.core.internal.view.SupportMenu {
+ ctor public MenuBuilder(android.content.Context!);
+ method public android.view.MenuItem! add(int);
+ method public android.view.MenuItem! add(int, int, int, int);
+ method public android.view.MenuItem! add(int, int, int, CharSequence!);
+ method public android.view.MenuItem! add(CharSequence!);
+ method public int addIntentOptions(int, int, int, android.content.ComponentName!, android.content.Intent![]!, android.content.Intent!, int, android.view.MenuItem![]!);
+ method protected android.view.MenuItem! addInternal(int, int, int, CharSequence!);
+ method public void addMenuPresenter(androidx.appcompat.view.menu.MenuPresenter!);
+ method public void addMenuPresenter(androidx.appcompat.view.menu.MenuPresenter!, android.content.Context!);
+ method public android.view.SubMenu! addSubMenu(int);
+ method public android.view.SubMenu! addSubMenu(int, int, int, int);
+ method public android.view.SubMenu! addSubMenu(int, int, int, CharSequence!);
+ method public android.view.SubMenu! addSubMenu(CharSequence!);
+ method public void changeMenuMode();
+ method public void clear();
+ method public void clearAll();
+ method public void clearHeader();
+ method public void close();
+ method public final void close(boolean);
+ method public boolean collapseItemActionView(androidx.appcompat.view.menu.MenuItemImpl!);
+ method public boolean expandItemActionView(androidx.appcompat.view.menu.MenuItemImpl!);
+ method public int findGroupIndex(int);
+ method public int findGroupIndex(int, int);
+ method public android.view.MenuItem! findItem(int);
+ method public int findItemIndex(int);
+ method public void flagActionItems();
+ method public java.util.ArrayList<androidx.appcompat.view.menu.MenuItemImpl!>! getActionItems();
+ method protected String! getActionViewStatesKey();
+ method public android.content.Context! getContext();
+ method public androidx.appcompat.view.menu.MenuItemImpl! getExpandedItem();
+ method public android.graphics.drawable.Drawable! getHeaderIcon();
+ method public CharSequence! getHeaderTitle();
+ method public android.view.View! getHeaderView();
+ method public android.view.MenuItem! getItem(int);
+ method public java.util.ArrayList<androidx.appcompat.view.menu.MenuItemImpl!>! getNonActionItems();
+ method public androidx.appcompat.view.menu.MenuBuilder! getRootMenu();
+ method public java.util.ArrayList<androidx.appcompat.view.menu.MenuItemImpl!> getVisibleItems();
+ method public boolean hasVisibleItems();
+ method public boolean isDispatchingItemsChanged();
+ method public boolean isGroupDividerEnabled();
+ method public boolean isShortcutKey(int, android.view.KeyEvent!);
+ method public boolean isShortcutsVisible();
+ method public void onItemsChanged(boolean);
+ method public boolean performIdentifierAction(int, int);
+ method public boolean performItemAction(android.view.MenuItem!, androidx.appcompat.view.menu.MenuPresenter!, int);
+ method public boolean performItemAction(android.view.MenuItem!, int);
+ method public boolean performShortcut(int, android.view.KeyEvent!, int);
+ method public void removeGroup(int);
+ method public void removeItem(int);
+ method public void removeItemAt(int);
+ method public void removeMenuPresenter(androidx.appcompat.view.menu.MenuPresenter!);
+ method public void restoreActionViewStates(android.os.Bundle!);
+ method public void restorePresenterStates(android.os.Bundle!);
+ method public void saveActionViewStates(android.os.Bundle!);
+ method public void savePresenterStates(android.os.Bundle!);
+ method public void setCallback(androidx.appcompat.view.menu.MenuBuilder.Callback!);
+ method public void setCurrentMenuInfo(android.view.ContextMenu.ContextMenuInfo!);
+ method public androidx.appcompat.view.menu.MenuBuilder! setDefaultShowAsAction(int);
+ method public void setGroupCheckable(int, boolean, boolean);
+ method public void setGroupEnabled(int, boolean);
+ method public void setGroupVisible(int, boolean);
+ method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderIconInt(android.graphics.drawable.Drawable!);
+ method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderIconInt(int);
+ method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderTitleInt(int);
+ method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderTitleInt(CharSequence!);
+ method protected androidx.appcompat.view.menu.MenuBuilder! setHeaderViewInt(android.view.View!);
+ method public void setOptionalIconsVisible(boolean);
+ method public void setOverrideVisibleItems(boolean);
+ method public void setQwertyMode(boolean);
+ method public void setShortcutsVisible(boolean);
+ method public int size();
+ method public void startDispatchingItemsChanged();
+ method public void stopDispatchingItemsChanged();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface MenuBuilder.Callback {
+ method public boolean onMenuItemSelected(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+ method public void onMenuModeChange(androidx.appcompat.view.menu.MenuBuilder);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface MenuBuilder.ItemInvoker {
+ method public boolean invokeItem(androidx.appcompat.view.menu.MenuItemImpl!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class MenuItemImpl implements androidx.core.internal.view.SupportMenuItem {
+ method public void actionFormatChanged();
+ method public boolean collapseActionView();
+ method public boolean expandActionView();
+ method public android.view.ActionProvider! getActionProvider();
+ method public android.view.View! getActionView();
+ method public char getAlphabeticShortcut();
+ method public int getGroupId();
+ method public android.graphics.drawable.Drawable! getIcon();
+ method public android.content.Intent! getIntent();
+ method public int getItemId();
+ method public android.view.ContextMenu.ContextMenuInfo! getMenuInfo();
+ method public char getNumericShortcut();
+ method public int getOrder();
+ method public int getOrdering();
+ method public android.view.SubMenu! getSubMenu();
+ method public androidx.core.view.ActionProvider! getSupportActionProvider();
+ method public CharSequence! getTitle();
+ method public CharSequence! getTitleCondensed();
+ method public boolean hasCollapsibleActionView();
+ method public boolean hasSubMenu();
+ method public boolean invoke();
+ method public boolean isActionButton();
+ method public boolean isActionViewExpanded();
+ method public boolean isCheckable();
+ method public boolean isChecked();
+ method public boolean isEnabled();
+ method public boolean isExclusiveCheckable();
+ method public boolean isVisible();
+ method public boolean requestsActionButton();
+ method public boolean requiresActionButton();
+ method public boolean requiresOverflow();
+ method public android.view.MenuItem! setActionProvider(android.view.ActionProvider!);
+ method public androidx.core.internal.view.SupportMenuItem setActionView(android.view.View!);
+ method public androidx.core.internal.view.SupportMenuItem setActionView(int);
+ method public void setActionViewExpanded(boolean);
+ method public android.view.MenuItem! setAlphabeticShortcut(char);
+ method public android.view.MenuItem! setCallback(Runnable!);
+ method public android.view.MenuItem! setCheckable(boolean);
+ method public android.view.MenuItem! setChecked(boolean);
+ method public androidx.core.internal.view.SupportMenuItem setContentDescription(CharSequence!);
+ method public android.view.MenuItem! setEnabled(boolean);
+ method public void setExclusiveCheckable(boolean);
+ method public android.view.MenuItem! setIcon(android.graphics.drawable.Drawable!);
+ method public android.view.MenuItem! setIcon(int);
+ method public android.view.MenuItem! setIntent(android.content.Intent!);
+ method public void setIsActionButton(boolean);
+ method public android.view.MenuItem! setNumericShortcut(char);
+ method public android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem.OnActionExpandListener!);
+ method public android.view.MenuItem! setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener!);
+ method public android.view.MenuItem! setShortcut(char, char);
+ method public void setShowAsAction(int);
+ method public androidx.core.internal.view.SupportMenuItem setShowAsActionFlags(int);
+ method public void setSubMenu(androidx.appcompat.view.menu.SubMenuBuilder!);
+ method public androidx.core.internal.view.SupportMenuItem setSupportActionProvider(androidx.core.view.ActionProvider!);
+ method public android.view.MenuItem! setTitle(int);
+ method public android.view.MenuItem! setTitle(CharSequence!);
+ method public android.view.MenuItem! setTitleCondensed(CharSequence!);
+ method public androidx.core.internal.view.SupportMenuItem setTooltipText(CharSequence!);
+ method public android.view.MenuItem! setVisible(boolean);
+ method public boolean shouldShowIcon();
+ method public boolean showsTextAsAction();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuItemWrapperICS implements android.view.MenuItem {
+ ctor public MenuItemWrapperICS(android.content.Context!, androidx.core.internal.view.SupportMenuItem!);
+ method public boolean collapseActionView();
+ method public boolean expandActionView();
+ method public android.view.ActionProvider! getActionProvider();
+ method public android.view.View! getActionView();
+ method public char getAlphabeticShortcut();
+ method public int getGroupId();
+ method public android.graphics.drawable.Drawable! getIcon();
+ method public android.content.Intent! getIntent();
+ method public int getItemId();
+ method public android.view.ContextMenu.ContextMenuInfo! getMenuInfo();
+ method public char getNumericShortcut();
+ method public int getOrder();
+ method public android.view.SubMenu! getSubMenu();
+ method public CharSequence! getTitle();
+ method public CharSequence! getTitleCondensed();
+ method public boolean hasSubMenu();
+ method public boolean isActionViewExpanded();
+ method public boolean isCheckable();
+ method public boolean isChecked();
+ method public boolean isEnabled();
+ method public boolean isVisible();
+ method public android.view.MenuItem! setActionProvider(android.view.ActionProvider!);
+ method public android.view.MenuItem! setActionView(android.view.View!);
+ method public android.view.MenuItem! setActionView(int);
+ method public android.view.MenuItem! setAlphabeticShortcut(char);
+ method public android.view.MenuItem! setCheckable(boolean);
+ method public android.view.MenuItem! setChecked(boolean);
+ method public android.view.MenuItem! setEnabled(boolean);
+ method public void setExclusiveCheckable(boolean);
+ method public android.view.MenuItem! setIcon(android.graphics.drawable.Drawable!);
+ method public android.view.MenuItem! setIcon(int);
+ method public android.view.MenuItem! setIntent(android.content.Intent!);
+ method public android.view.MenuItem! setNumericShortcut(char);
+ method public android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem.OnActionExpandListener!);
+ method public android.view.MenuItem! setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener!);
+ method public android.view.MenuItem! setShortcut(char, char);
+ method public void setShowAsAction(int);
+ method public android.view.MenuItem! setShowAsActionFlags(int);
+ method public android.view.MenuItem! setTitle(int);
+ method public android.view.MenuItem! setTitle(CharSequence!);
+ method public android.view.MenuItem! setTitleCondensed(CharSequence!);
+ method public android.view.MenuItem! setVisible(boolean);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuPopupHelper {
+ ctor public MenuPopupHelper(android.content.Context, androidx.appcompat.view.menu.MenuBuilder);
+ ctor public MenuPopupHelper(android.content.Context, androidx.appcompat.view.menu.MenuBuilder, android.view.View);
+ ctor public MenuPopupHelper(android.content.Context, androidx.appcompat.view.menu.MenuBuilder, android.view.View, boolean, @AttrRes int);
+ ctor public MenuPopupHelper(android.content.Context, androidx.appcompat.view.menu.MenuBuilder, android.view.View, boolean, @AttrRes int, @StyleRes int);
+ method public void dismiss();
+ method public int getGravity();
+ method public android.widget.ListView! getListView();
+ method public boolean isShowing();
+ method protected void onDismiss();
+ method public void setAnchorView(android.view.View);
+ method public void setForceShowIcon(boolean);
+ method public void setGravity(int);
+ method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener?);
+ method public void setPresenterCallback(androidx.appcompat.view.menu.MenuPresenter.Callback?);
+ method public void show();
+ method public void show(int, int);
+ method public boolean tryShow();
+ method public boolean tryShow(int, int);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface MenuPresenter {
+ method public boolean collapseItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+ method public boolean expandItemActionView(androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+ method public boolean flagActionItems();
+ method public int getId();
+ method public androidx.appcompat.view.menu.MenuView! getMenuView(android.view.ViewGroup!);
+ method public void initForMenu(android.content.Context!, androidx.appcompat.view.menu.MenuBuilder!);
+ method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder!, boolean);
+ method public void onRestoreInstanceState(android.os.Parcelable!);
+ method public android.os.Parcelable! onSaveInstanceState();
+ method public boolean onSubMenuSelected(androidx.appcompat.view.menu.SubMenuBuilder!);
+ method public void setCallback(androidx.appcompat.view.menu.MenuPresenter.Callback!);
+ method public void updateMenuView(boolean);
+ }
+
+ public static interface MenuPresenter.Callback {
+ method public void onCloseMenu(androidx.appcompat.view.menu.MenuBuilder, boolean);
+ method public boolean onOpenSubMenu(androidx.appcompat.view.menu.MenuBuilder);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface MenuView {
+ method public int getWindowAnimations();
+ method public void initialize(androidx.appcompat.view.menu.MenuBuilder!);
+ }
+
+ public static interface MenuView.ItemView {
+ method public androidx.appcompat.view.menu.MenuItemImpl! getItemData();
+ method public void initialize(androidx.appcompat.view.menu.MenuItemImpl!, int);
+ method public boolean prefersCondensedTitle();
+ method public void setCheckable(boolean);
+ method public void setChecked(boolean);
+ method public void setEnabled(boolean);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setShortcut(boolean, char);
+ method public void setTitle(CharSequence!);
+ method public boolean showsIcon();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuWrapperICS implements android.view.Menu {
+ ctor public MenuWrapperICS(android.content.Context!, androidx.core.internal.view.SupportMenu!);
+ method public android.view.MenuItem! add(int);
+ method public android.view.MenuItem! add(int, int, int, int);
+ method public android.view.MenuItem! add(int, int, int, CharSequence!);
+ method public android.view.MenuItem! add(CharSequence!);
+ method public int addIntentOptions(int, int, int, android.content.ComponentName!, android.content.Intent![]!, android.content.Intent!, int, android.view.MenuItem![]!);
+ method public android.view.SubMenu! addSubMenu(int);
+ method public android.view.SubMenu! addSubMenu(int, int, int, int);
+ method public android.view.SubMenu! addSubMenu(int, int, int, CharSequence!);
+ method public android.view.SubMenu! addSubMenu(CharSequence!);
+ method public void clear();
+ method public void close();
+ method public android.view.MenuItem! findItem(int);
+ method public android.view.MenuItem! getItem(int);
+ method public boolean hasVisibleItems();
+ method public boolean isShortcutKey(int, android.view.KeyEvent!);
+ method public boolean performIdentifierAction(int, int);
+ method public boolean performShortcut(int, android.view.KeyEvent!, int);
+ method public void removeGroup(int);
+ method public void removeItem(int);
+ method public void setGroupCheckable(int, boolean, boolean);
+ method public void setGroupEnabled(int, boolean);
+ method public void setGroupVisible(int, boolean);
+ method public void setQwertyMode(boolean);
+ method public int size();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface ShowableListMenu {
+ method public void dismiss();
+ method public android.widget.ListView! getListView();
+ method public boolean isShowing();
+ method public void show();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class SubMenuBuilder extends androidx.appcompat.view.menu.MenuBuilder implements android.view.SubMenu {
+ ctor public SubMenuBuilder(android.content.Context!, androidx.appcompat.view.menu.MenuBuilder!, androidx.appcompat.view.menu.MenuItemImpl!);
+ method public String! getActionViewStatesKey();
+ method public android.view.MenuItem! getItem();
+ method public android.view.Menu! getParentMenu();
+ method public boolean isQwertyMode();
+ method public android.view.SubMenu! setHeaderIcon(android.graphics.drawable.Drawable!);
+ method public android.view.SubMenu! setHeaderIcon(int);
+ method public android.view.SubMenu! setHeaderTitle(int);
+ method public android.view.SubMenu! setHeaderTitle(CharSequence!);
+ method public android.view.SubMenu! setHeaderView(android.view.View!);
+ method public android.view.SubMenu! setIcon(android.graphics.drawable.Drawable!);
+ method public android.view.SubMenu! setIcon(int);
+ }
+
+}
+
+package androidx.appcompat.widget {
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionBarContainer extends android.widget.FrameLayout {
+ ctor public ActionBarContainer(android.content.Context!);
+ ctor public ActionBarContainer(android.content.Context!, android.util.AttributeSet!);
+ method public android.view.View! getTabContainer();
+ method public void onFinishInflate();
+ method public void onLayout(boolean, int, int, int, int);
+ method public void onMeasure(int, int);
+ method public void setPrimaryBackground(android.graphics.drawable.Drawable!);
+ method public void setSplitBackground(android.graphics.drawable.Drawable!);
+ method public void setStackedBackground(android.graphics.drawable.Drawable!);
+ method public void setTabContainer(androidx.appcompat.widget.ScrollingTabContainerView!);
+ method public void setTransitioning(boolean);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionBarContextView extends android.view.ViewGroup {
+ ctor public ActionBarContextView(android.content.Context);
+ ctor public ActionBarContextView(android.content.Context, android.util.AttributeSet?);
+ ctor public ActionBarContextView(android.content.Context, android.util.AttributeSet?, int);
+ method public void animateToVisibility(int);
+ method public boolean canShowOverflowMenu();
+ method public void closeMode();
+ method public void dismissPopupMenus();
+ method public int getAnimatedVisibility();
+ method public int getContentHeight();
+ method public CharSequence! getSubtitle();
+ method public CharSequence! getTitle();
+ method public boolean hideOverflowMenu();
+ method public void initForMode(androidx.appcompat.view.ActionMode!);
+ method public boolean isOverflowMenuShowPending();
+ method public boolean isOverflowMenuShowing();
+ method public boolean isOverflowReserved();
+ method public boolean isTitleOptional();
+ method public void killMode();
+ method public void onDetachedFromWindow();
+ method public void postShowOverflowMenu();
+ method public void setContentHeight(int);
+ method public void setCustomView(android.view.View!);
+ method public void setSubtitle(CharSequence!);
+ method public void setTitle(CharSequence!);
+ method public void setTitleOptional(boolean);
+ method public androidx.core.view.ViewPropertyAnimatorCompat! setupAnimatorToVisibility(int, long);
+ method public boolean showOverflowMenu();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActionBarOverlayLayout extends android.view.ViewGroup implements androidx.appcompat.widget.DecorContentParent androidx.core.view.NestedScrollingParent androidx.core.view.NestedScrollingParent2 androidx.core.view.NestedScrollingParent3 {
+ ctor public ActionBarOverlayLayout(android.content.Context);
+ ctor public ActionBarOverlayLayout(android.content.Context, android.util.AttributeSet?);
+ method public boolean canShowOverflowMenu();
+ method public void dismissPopups();
+ method protected boolean fitSystemWindows(android.graphics.Rect!);
+ method protected androidx.appcompat.widget.ActionBarOverlayLayout.LayoutParams! generateDefaultLayoutParams();
+ method public androidx.appcompat.widget.ActionBarOverlayLayout.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+ method public int getActionBarHideOffset();
+ method public CharSequence! getTitle();
+ method public boolean hasIcon();
+ method public boolean hasLogo();
+ method public boolean hideOverflowMenu();
+ method public void initFeature(int);
+ method public boolean isHideOnContentScrollEnabled();
+ method public boolean isInOverlayMode();
+ method public boolean isOverflowMenuShowPending();
+ method public boolean isOverflowMenuShowing();
+ method public void onNestedPreScroll(android.view.View!, int, int, int[]!, int);
+ method public void onNestedScroll(android.view.View!, int, int, int, int, int);
+ method public void onNestedScroll(android.view.View!, int, int, int, int, int, int[]!);
+ method public void onNestedScrollAccepted(android.view.View!, android.view.View!, int, int);
+ method public boolean onStartNestedScroll(android.view.View!, android.view.View!, int, int);
+ method public void onStopNestedScroll(android.view.View!, int);
+ method public void restoreToolbarHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+ method public void saveToolbarHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+ method public void setActionBarHideOffset(int);
+ method public void setActionBarVisibilityCallback(androidx.appcompat.widget.ActionBarOverlayLayout.ActionBarVisibilityCallback!);
+ method public void setHasNonEmbeddedTabs(boolean);
+ method public void setHideOnContentScrollEnabled(boolean);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setIcon(int);
+ method public void setLogo(int);
+ method public void setMenu(android.view.Menu!, androidx.appcompat.view.menu.MenuPresenter.Callback!);
+ method public void setMenuPrepared();
+ method public void setOverlayMode(boolean);
+ method public void setShowingForActionMode(boolean);
+ method public void setUiOptions(int);
+ method public void setWindowCallback(android.view.Window.Callback!);
+ method public void setWindowTitle(CharSequence!);
+ method public boolean showOverflowMenu();
+ }
+
+ public static interface ActionBarOverlayLayout.ActionBarVisibilityCallback {
+ method public void enableContentAnimations(boolean);
+ method public void hideForSystem();
+ method public void onContentScrollStarted();
+ method public void onContentScrollStopped();
+ method public void onWindowVisibilityChanged(int);
+ method public void showForSystem();
+ }
+
+ public static class ActionBarOverlayLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+ ctor public ActionBarOverlayLayout.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+ ctor public ActionBarOverlayLayout.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public ActionBarOverlayLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+ ctor public ActionBarOverlayLayout.LayoutParams(int, int);
+ }
+
+ public class ActionMenuView extends androidx.appcompat.widget.LinearLayoutCompat implements androidx.appcompat.view.menu.MenuBuilder.ItemInvoker androidx.appcompat.view.menu.MenuView {
+ ctor public ActionMenuView(android.content.Context);
+ ctor public ActionMenuView(android.content.Context, android.util.AttributeSet?);
+ method public void dismissPopupMenus();
+ method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateDefaultLayoutParams();
+ method public androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.widget.ActionMenuView.LayoutParams! generateOverflowButtonLayoutParams();
+ method public android.view.Menu! getMenu();
+ method public android.graphics.drawable.Drawable? getOverflowIcon();
+ method public int getPopupTheme();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int getWindowAnimations();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected boolean hasSupportDividerBeforeChildAt(int);
+ method public boolean hideOverflowMenu();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void initialize(androidx.appcompat.view.menu.MenuBuilder!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean invokeItem(androidx.appcompat.view.menu.MenuItemImpl!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isOverflowMenuShowPending();
+ method public boolean isOverflowMenuShowing();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isOverflowReserved();
+ method public void onConfigurationChanged(android.content.res.Configuration!);
+ method public void onDetachedFromWindow();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.view.menu.MenuBuilder! peekMenu();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setExpandedActionViewsExclusive(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setMenuCallbacks(androidx.appcompat.view.menu.MenuPresenter.Callback!, androidx.appcompat.view.menu.MenuBuilder.Callback!);
+ method public void setOnMenuItemClickListener(androidx.appcompat.widget.ActionMenuView.OnMenuItemClickListener!);
+ method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setOverflowReserved(boolean);
+ method public void setPopupTheme(@StyleRes int);
+ method public boolean showOverflowMenu();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface ActionMenuView.ActionMenuChildView {
+ method public boolean needsDividerAfter();
+ method public boolean needsDividerBefore();
+ }
+
+ public static class ActionMenuView.LayoutParams extends androidx.appcompat.widget.LinearLayoutCompat.LayoutParams {
+ ctor public ActionMenuView.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+ ctor public ActionMenuView.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public ActionMenuView.LayoutParams(androidx.appcompat.widget.ActionMenuView.LayoutParams!);
+ ctor public ActionMenuView.LayoutParams(int, int);
+ field public int cellsUsed;
+ field public boolean expandable;
+ field public int extraPixels;
+ field public boolean isOverflowButton;
+ field public boolean preventEdgeOffset;
+ }
+
+ public static interface ActionMenuView.OnMenuItemClickListener {
+ method public boolean onMenuItemClick(android.view.MenuItem!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ActivityChooserView extends android.view.ViewGroup {
+ ctor public ActivityChooserView(android.content.Context);
+ ctor public ActivityChooserView(android.content.Context, android.util.AttributeSet?);
+ ctor public ActivityChooserView(android.content.Context, android.util.AttributeSet?, int);
+ method public boolean dismissPopup();
+ method public boolean isShowingPopup();
+ method public void setDefaultActionButtonContentDescription(int);
+ method public void setExpandActivityOverflowButtonContentDescription(int);
+ method public void setExpandActivityOverflowButtonDrawable(android.graphics.drawable.Drawable!);
+ method public void setInitialActivityCount(int);
+ method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setProvider(androidx.core.view.ActionProvider!);
+ method public boolean showPopup();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ActivityChooserView.InnerLayout extends android.widget.LinearLayout {
+ ctor public ActivityChooserView.InnerLayout(android.content.Context!, android.util.AttributeSet!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AlertDialogLayout extends androidx.appcompat.widget.LinearLayoutCompat {
+ ctor public AlertDialogLayout(android.content.Context?);
+ ctor public AlertDialogLayout(android.content.Context?, android.util.AttributeSet?);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatAutoCompleteTextView(android.content.Context);
+ ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatButton extends android.widget.Button implements androidx.core.widget.AutoSizeableTextView androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatButton(android.content.Context);
+ ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method public void setSupportAllCaps(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatCheckBox extends android.widget.CheckBox implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatCheckBox(android.content.Context);
+ ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatCheckedTextView extends android.widget.CheckedTextView implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCheckedTextView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatCheckedTextView(android.content.Context);
+ ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCheckMarkTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCheckMarkTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCheckMarkTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCheckMarkTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context, int);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class AppCompatDrawableManager {
+ ctor public AppCompatDrawableManager();
+ method public static androidx.appcompat.widget.AppCompatDrawableManager! get();
+ method public android.graphics.drawable.Drawable! getDrawable(android.content.Context, @DrawableRes int);
+ method public static android.graphics.PorterDuffColorFilter! getPorterDuffColorFilter(int, android.graphics.PorterDuff.Mode!);
+ method public void onConfigurationChanged(android.content.Context);
+ method public static void preload();
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatEditText extends android.widget.EditText implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.OnReceiveContentViewBehavior androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatEditText(android.content.Context);
+ ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatImageButton extends android.widget.ImageButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
+ ctor public AppCompatImageButton(android.content.Context);
+ ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportImageTintMode();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AppCompatImageHelper {
+ ctor public AppCompatImageHelper(android.widget.ImageView);
+ method public void loadFromAttributes(android.util.AttributeSet!, int);
+ method public void setImageResource(int);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatImageView extends android.widget.ImageView implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
+ ctor public AppCompatImageView(android.content.Context);
+ ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportImageTintMode();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportImageTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+ ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatRadioButton extends android.widget.RadioButton implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatRadioButton(android.content.Context!);
+ ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?);
+ ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public class AppCompatRatingBar extends android.widget.RatingBar {
+ ctor public AppCompatRatingBar(android.content.Context);
+ ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+ public class AppCompatSeekBar extends android.widget.SeekBar {
+ ctor public AppCompatSeekBar(android.content.Context);
+ ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatSpinner extends android.widget.Spinner implements androidx.core.view.TintableBackgroundView {
+ ctor public AppCompatSpinner(android.content.Context);
+ ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int);
+ ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int);
+ ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int, android.content.res.Resources.Theme!);
+ ctor public AppCompatSpinner(android.content.Context, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatTextView extends android.widget.TextView implements androidx.core.widget.AutoSizeableTextView androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatTextView(android.content.Context);
+ ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParamsCompat();
+ method public boolean isEmojiCompatEnabled();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setEmojiCompatEnabled(boolean);
+ method public void setPrecomputedText(androidx.core.text.PrecomputedTextCompat);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTextAppearance(android.content.Context!, int);
+ method public void setTextFuture(java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>?);
+ method public void setTextMetricsParamsCompat(androidx.core.text.PrecomputedTextCompat.Params);
+ }
+
+ @androidx.resourceinspection.annotation.AppCompatShadowedAttributes public class AppCompatToggleButton extends android.widget.ToggleButton implements androidx.appcompat.widget.EmojiCompatConfigurationView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
+ ctor public AppCompatToggleButton(android.content.Context);
+ ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?);
+ ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?, int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void setEmojiCompatEnabled(boolean);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ButtonBarLayout extends android.widget.LinearLayout {
+ ctor public ButtonBarLayout(android.content.Context, android.util.AttributeSet?);
+ method public void setAllowStacking(boolean);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface DecorContentParent {
+ method public boolean canShowOverflowMenu();
+ method public void dismissPopups();
+ method public CharSequence! getTitle();
+ method public boolean hasIcon();
+ method public boolean hasLogo();
+ method public boolean hideOverflowMenu();
+ method public void initFeature(int);
+ method public boolean isOverflowMenuShowPending();
+ method public boolean isOverflowMenuShowing();
+ method public void restoreToolbarHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+ method public void saveToolbarHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setIcon(int);
+ method public void setLogo(int);
+ method public void setMenu(android.view.Menu!, androidx.appcompat.view.menu.MenuPresenter.Callback!);
+ method public void setMenuPrepared();
+ method public void setUiOptions(int);
+ method public void setWindowCallback(android.view.Window.Callback!);
+ method public void setWindowTitle(CharSequence!);
+ method public boolean showOverflowMenu();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface DecorToolbar {
+ method public void animateToVisibility(int);
+ method public boolean canShowOverflowMenu();
+ method public void collapseActionView();
+ method public void dismissPopupMenus();
+ method public android.content.Context! getContext();
+ method public android.view.View! getCustomView();
+ method public int getDisplayOptions();
+ method public int getDropdownItemCount();
+ method public int getDropdownSelectedPosition();
+ method public int getHeight();
+ method public android.view.Menu! getMenu();
+ method public int getNavigationMode();
+ method public CharSequence! getSubtitle();
+ method public CharSequence! getTitle();
+ method public android.view.ViewGroup! getViewGroup();
+ method public int getVisibility();
+ method public boolean hasEmbeddedTabs();
+ method public boolean hasExpandedActionView();
+ method public boolean hasIcon();
+ method public boolean hasLogo();
+ method public boolean hideOverflowMenu();
+ method public void initIndeterminateProgress();
+ method public void initProgress();
+ method public boolean isOverflowMenuShowPending();
+ method public boolean isOverflowMenuShowing();
+ method public boolean isTitleTruncated();
+ method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+ method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
+ method public void setCollapsible(boolean);
+ method public void setCustomView(android.view.View!);
+ method public void setDefaultNavigationContentDescription(int);
+ method public void setDefaultNavigationIcon(android.graphics.drawable.Drawable!);
+ method public void setDisplayOptions(int);
+ method public void setDropdownParams(android.widget.SpinnerAdapter!, android.widget.AdapterView.OnItemSelectedListener!);
+ method public void setDropdownSelectedPosition(int);
+ method public void setEmbeddedTabView(androidx.appcompat.widget.ScrollingTabContainerView!);
+ method public void setHomeButtonEnabled(boolean);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setIcon(int);
+ method public void setLogo(android.graphics.drawable.Drawable!);
+ method public void setLogo(int);
+ method public void setMenu(android.view.Menu!, androidx.appcompat.view.menu.MenuPresenter.Callback!);
+ method public void setMenuCallbacks(androidx.appcompat.view.menu.MenuPresenter.Callback!, androidx.appcompat.view.menu.MenuBuilder.Callback!);
+ method public void setMenuPrepared();
+ method public void setNavigationContentDescription(int);
+ method public void setNavigationContentDescription(CharSequence!);
+ method public void setNavigationIcon(android.graphics.drawable.Drawable!);
+ method public void setNavigationIcon(int);
+ method public void setNavigationMode(int);
+ method public void setSubtitle(CharSequence!);
+ method public void setTitle(CharSequence!);
+ method public void setVisibility(int);
+ method public void setWindowCallback(android.view.Window.Callback!);
+ method public void setWindowTitle(CharSequence!);
+ method public androidx.core.view.ViewPropertyAnimatorCompat! setupAnimatorToVisibility(int, long);
+ method public boolean showOverflowMenu();
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DialogTitle extends androidx.appcompat.widget.AppCompatTextView {
+ ctor public DialogTitle(android.content.Context);
+ ctor public DialogTitle(android.content.Context, android.util.AttributeSet?);
+ ctor public DialogTitle(android.content.Context, android.util.AttributeSet?, int);
+ }
+
+ public interface EmojiCompatConfigurationView {
+ method public boolean isEmojiCompatEnabled();
+ method public void setEmojiCompatEnabled(boolean);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FitWindowsFrameLayout extends android.widget.FrameLayout implements androidx.appcompat.widget.FitWindowsViewGroup {
+ ctor public FitWindowsFrameLayout(android.content.Context);
+ ctor public FitWindowsFrameLayout(android.content.Context, android.util.AttributeSet?);
+ method protected boolean fitSystemWindows(android.graphics.Rect!);
+ method public void setOnFitSystemWindowsListener(androidx.appcompat.widget.FitWindowsViewGroup.OnFitSystemWindowsListener!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FitWindowsLinearLayout extends android.widget.LinearLayout implements androidx.appcompat.widget.FitWindowsViewGroup {
+ ctor public FitWindowsLinearLayout(android.content.Context);
+ ctor public FitWindowsLinearLayout(android.content.Context, android.util.AttributeSet?);
+ method protected boolean fitSystemWindows(android.graphics.Rect!);
+ method public void setOnFitSystemWindowsListener(androidx.appcompat.widget.FitWindowsViewGroup.OnFitSystemWindowsListener!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface FitWindowsViewGroup {
+ method public void setOnFitSystemWindowsListener(androidx.appcompat.widget.FitWindowsViewGroup.OnFitSystemWindowsListener!);
+ }
+
+ public static interface FitWindowsViewGroup.OnFitSystemWindowsListener {
+ method public void onFitSystemWindows(android.graphics.Rect!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class ForwardingListener implements android.view.View.OnAttachStateChangeListener android.view.View.OnTouchListener {
+ ctor public ForwardingListener(android.view.View!);
+ method public abstract androidx.appcompat.view.menu.ShowableListMenu! getPopup();
+ method protected boolean onForwardingStarted();
+ method protected boolean onForwardingStopped();
+ method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+ method public void onViewAttachedToWindow(android.view.View!);
+ method public void onViewDetachedFromWindow(android.view.View!);
+ }
+
+ public class LinearLayoutCompat extends android.view.ViewGroup {
+ ctor public LinearLayoutCompat(android.content.Context);
+ ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?);
+ ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?, int);
+ method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateDefaultLayoutParams();
+ method public androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+ method @androidx.resourceinspection.annotation.Attribute("android:baselineAlignedChildIndex") public int getBaselineAlignedChildIndex();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:divider") public android.graphics.drawable.Drawable! getDividerDrawable();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:dividerPadding") public int getDividerPadding();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int getDividerWidth();
+ method @GravityInt @androidx.resourceinspection.annotation.Attribute("android:gravity") public int getGravity();
+ method @androidx.appcompat.widget.LinearLayoutCompat.OrientationMode @androidx.resourceinspection.annotation.Attribute(value="android:orientation", intMapping={@androidx.resourceinspection.annotation.Attribute.IntMap(name="horizontal", value=0), @androidx.resourceinspection.annotation.Attribute.IntMap(name="vertical", value=1)}) public int getOrientation();
+ method @androidx.appcompat.widget.LinearLayoutCompat.DividerMode @androidx.resourceinspection.annotation.Attribute(value="androidx.appcompat:showDividers", intMapping={@androidx.resourceinspection.annotation.Attribute.IntMap(name="none", value=0), @androidx.resourceinspection.annotation.Attribute.IntMap(name="beginning", value=1, mask=1), @androidx.resourceinspection.annotation.Attribute.IntMap(name="middle", value=2, mask=2), @androidx.resourceinspection.annotation.Attribute.IntMap(name="end", value=4, mask=4)}) public int getShowDividers();
+ method @androidx.resourceinspection.annotation.Attribute("android:weightSum") public float getWeightSum();
+ method @androidx.resourceinspection.annotation.Attribute("android:baselineAligned") public boolean isBaselineAligned();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:measureWithLargestChild") public boolean isMeasureWithLargestChildEnabled();
+ method public void setBaselineAligned(boolean);
+ method public void setBaselineAlignedChildIndex(int);
+ method public void setDividerDrawable(android.graphics.drawable.Drawable!);
+ method public void setDividerPadding(int);
+ method public void setGravity(@GravityInt int);
+ method public void setHorizontalGravity(int);
+ method public void setMeasureWithLargestChildEnabled(boolean);
+ method public void setOrientation(@androidx.appcompat.widget.LinearLayoutCompat.OrientationMode int);
+ method public void setShowDividers(@androidx.appcompat.widget.LinearLayoutCompat.DividerMode int);
+ method public void setVerticalGravity(int);
+ method public void setWeightSum(float);
+ field public static final int HORIZONTAL = 0; // 0x0
+ field public static final int SHOW_DIVIDER_BEGINNING = 1; // 0x1
+ field public static final int SHOW_DIVIDER_END = 4; // 0x4
+ field public static final int SHOW_DIVIDER_MIDDLE = 2; // 0x2
+ field public static final int SHOW_DIVIDER_NONE = 0; // 0x0
+ field public static final int VERTICAL = 1; // 0x1
+ }
+
+ @IntDef(flag=true, value={androidx.appcompat.widget.LinearLayoutCompat.SHOW_DIVIDER_NONE, androidx.appcompat.widget.LinearLayoutCompat.SHOW_DIVIDER_BEGINNING, androidx.appcompat.widget.LinearLayoutCompat.SHOW_DIVIDER_MIDDLE, androidx.appcompat.widget.LinearLayoutCompat.SHOW_DIVIDER_END}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface LinearLayoutCompat.DividerMode {
+ }
+
+ public static class LinearLayoutCompat.LayoutParams extends android.widget.LinearLayout.LayoutParams {
+ ctor public LinearLayoutCompat.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+ ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public LinearLayoutCompat.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+ ctor public LinearLayoutCompat.LayoutParams(int, int);
+ ctor public LinearLayoutCompat.LayoutParams(int, int, float);
+ }
+
+ @IntDef({androidx.appcompat.widget.LinearLayoutCompat.HORIZONTAL, androidx.appcompat.widget.LinearLayoutCompat.VERTICAL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface LinearLayoutCompat.OrientationMode {
+ }
+
+ public class ListPopupWindow implements androidx.appcompat.view.menu.ShowableListMenu {
+ ctor public ListPopupWindow(android.content.Context);
+ ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?);
+ ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int);
+ ctor public ListPopupWindow(android.content.Context, android.util.AttributeSet?, @AttrRes int, @StyleRes int);
+ method public void clearListSelection();
+ method public android.view.View.OnTouchListener! createDragToOpenListener(android.view.View!);
+ method public void dismiss();
+ method public android.view.View? getAnchorView();
+ method @StyleRes public int getAnimationStyle();
+ method public android.graphics.drawable.Drawable? getBackground();
+ method public android.graphics.Rect? getEpicenterBounds();
+ method public int getHeight();
+ method public int getHorizontalOffset();
+ method public int getInputMethodMode();
+ method public android.widget.ListView? getListView();
+ method public int getPromptPosition();
+ method public Object? getSelectedItem();
+ method public long getSelectedItemId();
+ method public int getSelectedItemPosition();
+ method public android.view.View? getSelectedView();
+ method public int getSoftInputMode();
+ method public int getVerticalOffset();
+ method public int getWidth();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isDropDownAlwaysVisible();
+ method public boolean isInputMethodNotNeeded();
+ method public boolean isModal();
+ method public boolean isShowing();
+ method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyPreIme(int, android.view.KeyEvent);
+ method public boolean onKeyUp(int, android.view.KeyEvent);
+ method public boolean performItemClick(int);
+ method public void postShow();
+ method public void setAdapter(android.widget.ListAdapter?);
+ method public void setAnchorView(android.view.View?);
+ method public void setAnimationStyle(@StyleRes int);
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable?);
+ method public void setContentWidth(int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setDropDownAlwaysVisible(boolean);
+ method public void setDropDownGravity(int);
+ method public void setEpicenterBounds(android.graphics.Rect?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setForceIgnoreOutsideTouch(boolean);
+ method public void setHeight(int);
+ method public void setHorizontalOffset(int);
+ method public void setInputMethodMode(int);
+ method public void setListSelector(android.graphics.drawable.Drawable!);
+ method public void setModal(boolean);
+ method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener?);
+ method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener?);
+ method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setOverlapAnchor(boolean);
+ method public void setPromptPosition(int);
+ method public void setPromptView(android.view.View?);
+ method public void setSelection(int);
+ method public void setSoftInputMode(int);
+ method public void setVerticalOffset(int);
+ method public void setWidth(int);
+ method public void setWindowLayoutType(int);
+ method public void show();
+ field public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; // 0x0
+ field public static final int INPUT_METHOD_NEEDED = 1; // 0x1
+ field public static final int INPUT_METHOD_NOT_NEEDED = 2; // 0x2
+ field public static final int MATCH_PARENT = -1; // 0xffffffff
+ field public static final int POSITION_PROMPT_ABOVE = 0; // 0x0
+ field public static final int POSITION_PROMPT_BELOW = 1; // 0x1
+ field public static final int WRAP_CONTENT = -2; // 0xfffffffe
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface MenuItemHoverListener {
+ method public void onItemHoverEnter(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+ method public void onItemHoverExit(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MenuPopupWindow extends androidx.appcompat.widget.ListPopupWindow implements androidx.appcompat.widget.MenuItemHoverListener {
+ ctor public MenuPopupWindow(android.content.Context, android.util.AttributeSet?, int, int);
+ method public void onItemHoverEnter(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+ method public void onItemHoverExit(androidx.appcompat.view.menu.MenuBuilder, android.view.MenuItem);
+ method public void setEnterTransition(Object!);
+ method public void setExitTransition(Object!);
+ method public void setHoverListener(androidx.appcompat.widget.MenuItemHoverListener!);
+ method public void setTouchModal(boolean);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class MenuPopupWindow.MenuDropDownListView extends android.widget.ListView {
+ ctor public MenuPopupWindow.MenuDropDownListView(android.content.Context!, boolean);
+ method public void clearSelection();
+ method public int lookForSelectablePosition(int, boolean);
+ method public int measureHeightOfChildrenCompat(int, int, int, int, int);
+ method public boolean onForwardedEvent(android.view.MotionEvent!, int);
+ method public void setHoverListener(androidx.appcompat.widget.MenuItemHoverListener!);
+ field public static final int INVALID_POSITION = -1; // 0xffffffff
+ field public static final int NO_POSITION = -1; // 0xffffffff
+ }
+
+ public class PopupMenu {
+ ctor public PopupMenu(android.content.Context, android.view.View);
+ ctor public PopupMenu(android.content.Context, android.view.View, int);
+ ctor public PopupMenu(android.content.Context, android.view.View, int, @AttrRes int, @StyleRes int);
+ method public void dismiss();
+ method public android.view.View.OnTouchListener getDragToOpenListener();
+ method public int getGravity();
+ method public android.view.Menu getMenu();
+ method public android.view.MenuInflater getMenuInflater();
+ method public void inflate(@MenuRes int);
+ method public void setForceShowIcon(boolean);
+ method public void setGravity(int);
+ method public void setOnDismissListener(androidx.appcompat.widget.PopupMenu.OnDismissListener?);
+ method public void setOnMenuItemClickListener(androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener?);
+ method public void show();
+ }
+
+ public static interface PopupMenu.OnDismissListener {
+ method public void onDismiss(androidx.appcompat.widget.PopupMenu!);
+ }
+
+ public static interface PopupMenu.OnMenuItemClickListener {
+ method public boolean onMenuItemClick(android.view.MenuItem!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ScrollingTabContainerView extends android.widget.HorizontalScrollView implements android.widget.AdapterView.OnItemSelectedListener {
+ ctor public ScrollingTabContainerView(android.content.Context);
+ method public void addTab(androidx.appcompat.app.ActionBar.Tab!, boolean);
+ method public void addTab(androidx.appcompat.app.ActionBar.Tab!, int, boolean);
+ method public void animateToTab(int);
+ method public void animateToVisibility(int);
+ method public void onAttachedToWindow();
+ method public void onDetachedFromWindow();
+ method public void onItemSelected(android.widget.AdapterView<?>!, android.view.View!, int, long);
+ method public void onMeasure(int, int);
+ method public void onNothingSelected(android.widget.AdapterView<?>!);
+ method public void removeAllTabs();
+ method public void removeTabAt(int);
+ method public void setAllowCollapse(boolean);
+ method public void setContentHeight(int);
+ method public void setTabSelected(int);
+ method public void updateTab(int);
+ field protected final androidx.appcompat.widget.ScrollingTabContainerView.VisibilityAnimListener! mVisAnimListener;
+ field protected android.view.ViewPropertyAnimator! mVisibilityAnim;
+ }
+
+ protected class ScrollingTabContainerView.VisibilityAnimListener extends android.animation.AnimatorListenerAdapter {
+ ctor protected ScrollingTabContainerView.VisibilityAnimListener();
+ method public androidx.appcompat.widget.ScrollingTabContainerView.VisibilityAnimListener! withFinalVisibility(android.view.ViewPropertyAnimator!, int);
+ }
+
+ public class SearchView extends androidx.appcompat.widget.LinearLayoutCompat implements androidx.appcompat.view.CollapsibleActionView {
+ ctor public SearchView(android.content.Context);
+ ctor public SearchView(android.content.Context, android.util.AttributeSet?);
+ ctor public SearchView(android.content.Context, android.util.AttributeSet?, int);
+ method @androidx.resourceinspection.annotation.Attribute("android:imeOptions") public int getImeOptions();
+ method public int getInputType();
+ method @androidx.resourceinspection.annotation.Attribute("android:maxWidth") public int getMaxWidth();
+ method public CharSequence! getQuery();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:queryHint") public CharSequence? getQueryHint();
+ method public androidx.cursoradapter.widget.CursorAdapter! getSuggestionsAdapter();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:iconifiedByDefault") public boolean isIconfiedByDefault();
+ method public boolean isIconified();
+ method public boolean isQueryRefinementEnabled();
+ method public boolean isSubmitButtonEnabled();
+ method public void onActionViewCollapsed();
+ method public void onActionViewExpanded();
+ method protected void onQueryRefine(CharSequence?);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAppSearchData(android.os.Bundle!);
+ method public void setIconified(boolean);
+ method public void setIconifiedByDefault(boolean);
+ method public void setImeOptions(int);
+ method public void setInputType(int);
+ method public void setMaxWidth(int);
+ method public void setOnCloseListener(androidx.appcompat.widget.SearchView.OnCloseListener!);
+ method public void setOnQueryTextFocusChangeListener(android.view.View.OnFocusChangeListener!);
+ method public void setOnQueryTextListener(androidx.appcompat.widget.SearchView.OnQueryTextListener!);
+ method public void setOnSearchClickListener(android.view.View.OnClickListener!);
+ method public void setOnSuggestionListener(androidx.appcompat.widget.SearchView.OnSuggestionListener!);
+ method public void setQuery(CharSequence!, boolean);
+ method public void setQueryHint(CharSequence?);
+ method public void setQueryRefinementEnabled(boolean);
+ method public void setSearchableInfo(android.app.SearchableInfo!);
+ method public void setSubmitButtonEnabled(boolean);
+ method public void setSuggestionsAdapter(androidx.cursoradapter.widget.CursorAdapter!);
+ }
+
+ public static interface SearchView.OnCloseListener {
+ method public boolean onClose();
+ }
+
+ public static interface SearchView.OnQueryTextListener {
+ method public boolean onQueryTextChange(String!);
+ method public boolean onQueryTextSubmit(String!);
+ }
+
+ public static interface SearchView.OnSuggestionListener {
+ method public boolean onSuggestionClick(int);
+ method public boolean onSuggestionSelect(int);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class SearchView.SearchAutoComplete extends androidx.appcompat.widget.AppCompatAutoCompleteTextView {
+ ctor public SearchView.SearchAutoComplete(android.content.Context!);
+ ctor public SearchView.SearchAutoComplete(android.content.Context!, android.util.AttributeSet!);
+ ctor public SearchView.SearchAutoComplete(android.content.Context!, android.util.AttributeSet!, int);
+ }
+
+ public class ShareActionProvider extends androidx.core.view.ActionProvider {
+ ctor public ShareActionProvider(android.content.Context!);
+ method public android.view.View! onCreateActionView();
+ method public void setOnShareTargetSelectedListener(androidx.appcompat.widget.ShareActionProvider.OnShareTargetSelectedListener!);
+ method public void setShareHistoryFileName(String!);
+ method public void setShareIntent(android.content.Intent!);
+ field public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+ }
+
+ public static interface ShareActionProvider.OnShareTargetSelectedListener {
+ method public boolean onShareTargetSelected(androidx.appcompat.widget.ShareActionProvider!, android.content.Intent!);
+ }
+
+ public class SwitchCompat extends android.widget.CompoundButton implements androidx.appcompat.widget.EmojiCompatConfigurationView {
+ ctor public SwitchCompat(android.content.Context);
+ ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?);
+ ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?, int);
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:showText") public boolean getShowText();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:splitTrack") public boolean getSplitTrack();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:switchMinWidth") public int getSwitchMinWidth();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:switchPadding") public int getSwitchPadding();
+ method @androidx.resourceinspection.annotation.Attribute("android:textOff") public CharSequence! getTextOff();
+ method @androidx.resourceinspection.annotation.Attribute("android:textOn") public CharSequence! getTextOn();
+ method @androidx.resourceinspection.annotation.Attribute("android:thumb") public android.graphics.drawable.Drawable! getThumbDrawable();
+ method @FloatRange(from=0.0, to=1.0) protected final float getThumbPosition();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:thumbTextPadding") public int getThumbTextPadding();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:thumbTint") public android.content.res.ColorStateList? getThumbTintList();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:thumbTintMode") public android.graphics.PorterDuff.Mode? getThumbTintMode();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:track") public android.graphics.drawable.Drawable! getTrackDrawable();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:trackTint") public android.content.res.ColorStateList? getTrackTintList();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:trackTintMode") public android.graphics.PorterDuff.Mode? getTrackTintMode();
+ method public boolean isEmojiCompatEnabled();
+ method public void onMeasure(int, int);
+ method public void setEmojiCompatEnabled(boolean);
+ method protected final void setEnforceSwitchWidth(boolean);
+ method public void setShowText(boolean);
+ method public void setSplitTrack(boolean);
+ method public void setSwitchMinWidth(int);
+ method public void setSwitchPadding(int);
+ method public void setSwitchTextAppearance(android.content.Context!, int);
+ method public void setSwitchTypeface(android.graphics.Typeface!);
+ method public void setSwitchTypeface(android.graphics.Typeface!, int);
+ method public void setTextOff(CharSequence!);
+ method public void setTextOn(CharSequence!);
+ method public void setThumbDrawable(android.graphics.drawable.Drawable!);
+ method public void setThumbResource(int);
+ method public void setThumbTextPadding(int);
+ method public void setThumbTintList(android.content.res.ColorStateList?);
+ method public void setThumbTintMode(android.graphics.PorterDuff.Mode?);
+ method public void setTrackDrawable(android.graphics.drawable.Drawable!);
+ method public void setTrackResource(int);
+ method public void setTrackTintList(android.content.res.ColorStateList?);
+ method public void setTrackTintMode(android.graphics.PorterDuff.Mode?);
+ }
+
+ public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
+ method public android.content.res.Resources.Theme? getDropDownViewTheme();
+ method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+ }
+
+ public static final class ThemedSpinnerAdapter.Helper {
+ ctor public ThemedSpinnerAdapter.Helper(android.content.Context);
+ method public android.view.LayoutInflater getDropDownViewInflater();
+ method public android.content.res.Resources.Theme? getDropDownViewTheme();
+ method public void setDropDownViewTheme(android.content.res.Resources.Theme?);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TintTypedArray {
+ method public boolean getBoolean(int, boolean);
+ method @RequiresApi(21) public int getChangingConfigurations();
+ method public int getColor(int, int);
+ method public android.content.res.ColorStateList! getColorStateList(int);
+ method public float getDimension(int, float);
+ method public int getDimensionPixelOffset(int, int);
+ method public int getDimensionPixelSize(int, int);
+ method public android.graphics.drawable.Drawable! getDrawable(int);
+ method public android.graphics.drawable.Drawable! getDrawableIfKnown(int);
+ method public float getFloat(int, float);
+ method public android.graphics.Typeface? getFont(@StyleableRes int, int, androidx.core.content.res.ResourcesCompat.FontCallback?);
+ method public float getFraction(int, int, int, float);
+ method public int getIndex(int);
+ method public int getIndexCount();
+ method public int getInt(int, int);
+ method public int getInteger(int, int);
+ method public int getLayoutDimension(int, int);
+ method public int getLayoutDimension(int, String!);
+ method public String! getNonResourceString(int);
+ method public String! getPositionDescription();
+ method public int getResourceId(int, int);
+ method public android.content.res.Resources! getResources();
+ method public String! getString(int);
+ method public CharSequence! getText(int);
+ method public CharSequence![]! getTextArray(int);
+ method public int getType(int);
+ method public boolean getValue(int, android.util.TypedValue!);
+ method public android.content.res.TypedArray! getWrappedTypeArray();
+ method public boolean hasValue(int);
+ method public int length();
+ method public static androidx.appcompat.widget.TintTypedArray! obtainStyledAttributes(android.content.Context!, android.util.AttributeSet!, int[]!);
+ method public static androidx.appcompat.widget.TintTypedArray! obtainStyledAttributes(android.content.Context!, android.util.AttributeSet!, int[]!, int, int);
+ method public static androidx.appcompat.widget.TintTypedArray! obtainStyledAttributes(android.content.Context!, int, int[]!);
+ method public android.util.TypedValue! peekValue(int);
+ method public void recycle();
+ }
+
+ public class Toolbar extends android.view.ViewGroup implements androidx.core.view.MenuHost {
+ ctor public Toolbar(android.content.Context);
+ ctor public Toolbar(android.content.Context, android.util.AttributeSet?);
+ ctor public Toolbar(android.content.Context, android.util.AttributeSet?, int);
+ method @MainThread public void addMenuProvider(androidx.core.view.MenuProvider);
+ method @MainThread public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner);
+ method @MainThread public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean canShowOverflowMenu();
+ method public void collapseActionView();
+ method public void dismissPopupMenus();
+ method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateDefaultLayoutParams();
+ method public androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
+ method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:collapseContentDescription") public CharSequence? getCollapseContentDescription();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:collapseIcon") public android.graphics.drawable.Drawable? getCollapseIcon();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetEnd") public int getContentInsetEnd();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetEndWithActions") public int getContentInsetEndWithActions();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetLeft") public int getContentInsetLeft();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetRight") public int getContentInsetRight();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetStart") public int getContentInsetStart();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:contentInsetStartWithNavigation") public int getContentInsetStartWithNavigation();
+ method public int getCurrentContentInsetEnd();
+ method public int getCurrentContentInsetLeft();
+ method public int getCurrentContentInsetRight();
+ method public int getCurrentContentInsetStart();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:logo") public android.graphics.drawable.Drawable! getLogo();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:logoDescription") public CharSequence! getLogoDescription();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:menu") public android.view.Menu! getMenu();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:navigationContentDescription") public CharSequence? getNavigationContentDescription();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:navigationIcon") public android.graphics.drawable.Drawable? getNavigationIcon();
+ method public android.graphics.drawable.Drawable? getOverflowIcon();
+ method @StyleRes @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:popupTheme") public int getPopupTheme();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:subtitle") public CharSequence! getSubtitle();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:title") public CharSequence! getTitle();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:titleMarginBottom") public int getTitleMarginBottom();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:titleMarginEnd") public int getTitleMarginEnd();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:titleMarginStart") public int getTitleMarginStart();
+ method @androidx.resourceinspection.annotation.Attribute("androidx.appcompat:titleMarginTop") public int getTitleMarginTop();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.appcompat.widget.DecorToolbar! getWrapper();
+ method public boolean hasExpandedActionView();
+ method public boolean hideOverflowMenu();
+ method public void inflateMenu(@MenuRes int);
+ method @MainThread public void invalidateMenu();
+ method public boolean isBackInvokedCallbackEnabled();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isOverflowMenuShowPending();
+ method public boolean isOverflowMenuShowing();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isTitleTruncated();
+ method @MainThread public void removeMenuProvider(androidx.core.view.MenuProvider);
+ method public void setBackInvokedCallbackEnabled(boolean);
+ method public void setCollapseContentDescription(@StringRes int);
+ method public void setCollapseContentDescription(CharSequence?);
+ method public void setCollapseIcon(android.graphics.drawable.Drawable?);
+ method public void setCollapseIcon(@DrawableRes int);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setCollapsible(boolean);
+ method public void setContentInsetEndWithActions(int);
+ method public void setContentInsetStartWithNavigation(int);
+ method public void setContentInsetsAbsolute(int, int);
+ method public void setContentInsetsRelative(int, int);
+ method public void setLogo(android.graphics.drawable.Drawable!);
+ method public void setLogo(@DrawableRes int);
+ method public void setLogoDescription(@StringRes int);
+ method public void setLogoDescription(CharSequence!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setMenuCallbacks(androidx.appcompat.view.menu.MenuPresenter.Callback!, androidx.appcompat.view.menu.MenuBuilder.Callback!);
+ method public void setNavigationContentDescription(@StringRes int);
+ method public void setNavigationContentDescription(CharSequence?);
+ method public void setNavigationIcon(android.graphics.drawable.Drawable?);
+ method public void setNavigationIcon(@DrawableRes int);
+ method public void setNavigationOnClickListener(android.view.View.OnClickListener!);
+ method public void setOnMenuItemClickListener(androidx.appcompat.widget.Toolbar.OnMenuItemClickListener!);
+ method public void setOverflowIcon(android.graphics.drawable.Drawable?);
+ method public void setPopupTheme(@StyleRes int);
+ method public void setSubtitle(@StringRes int);
+ method public void setSubtitle(CharSequence!);
+ method public void setSubtitleTextAppearance(android.content.Context!, @StyleRes int);
+ method public void setSubtitleTextColor(android.content.res.ColorStateList);
+ method public void setSubtitleTextColor(@ColorInt int);
+ method public void setTitle(@StringRes int);
+ method public void setTitle(CharSequence!);
+ method public void setTitleMargin(int, int, int, int);
+ method public void setTitleMarginBottom(int);
+ method public void setTitleMarginEnd(int);
+ method public void setTitleMarginStart(int);
+ method public void setTitleMarginTop(int);
+ method public void setTitleTextAppearance(android.content.Context!, @StyleRes int);
+ method public void setTitleTextColor(android.content.res.ColorStateList);
+ method public void setTitleTextColor(@ColorInt int);
+ method public boolean showOverflowMenu();
+ }
+
+ public static class Toolbar.LayoutParams extends androidx.appcompat.app.ActionBar.LayoutParams {
+ ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet!);
+ ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+ ctor public Toolbar.LayoutParams(androidx.appcompat.app.ActionBar.LayoutParams!);
+ ctor public Toolbar.LayoutParams(androidx.appcompat.widget.Toolbar.LayoutParams!);
+ ctor public Toolbar.LayoutParams(int);
+ ctor public Toolbar.LayoutParams(int, int);
+ ctor public Toolbar.LayoutParams(int, int, int);
+ }
+
+ public static interface Toolbar.OnMenuItemClickListener {
+ method public boolean onMenuItemClick(android.view.MenuItem!);
+ }
+
+ public static class Toolbar.SavedState extends androidx.customview.view.AbsSavedState {
+ ctor public Toolbar.SavedState(android.os.Parcel!);
+ ctor public Toolbar.SavedState(android.os.Parcel!, ClassLoader!);
+ ctor public Toolbar.SavedState(android.os.Parcelable!);
+ field public static final android.os.Parcelable.Creator<androidx.appcompat.widget.Toolbar.SavedState!>! CREATOR;
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ToolbarWidgetWrapper implements androidx.appcompat.widget.DecorToolbar {
+ ctor public ToolbarWidgetWrapper(androidx.appcompat.widget.Toolbar!, boolean);
+ ctor public ToolbarWidgetWrapper(androidx.appcompat.widget.Toolbar!, boolean, int, int);
+ method public void animateToVisibility(int);
+ method public boolean canShowOverflowMenu();
+ method public void collapseActionView();
+ method public void dismissPopupMenus();
+ method public android.content.Context! getContext();
+ method public android.view.View! getCustomView();
+ method public int getDisplayOptions();
+ method public int getDropdownItemCount();
+ method public int getDropdownSelectedPosition();
+ method public int getHeight();
+ method public android.view.Menu! getMenu();
+ method public int getNavigationMode();
+ method public CharSequence! getSubtitle();
+ method public CharSequence! getTitle();
+ method public android.view.ViewGroup! getViewGroup();
+ method public int getVisibility();
+ method public boolean hasEmbeddedTabs();
+ method public boolean hasExpandedActionView();
+ method public boolean hasIcon();
+ method public boolean hasLogo();
+ method public boolean hideOverflowMenu();
+ method public void initIndeterminateProgress();
+ method public void initProgress();
+ method public boolean isOverflowMenuShowPending();
+ method public boolean isOverflowMenuShowing();
+ method public boolean isTitleTruncated();
+ method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+ method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable!>!);
+ method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
+ method public void setCollapsible(boolean);
+ method public void setCustomView(android.view.View!);
+ method public void setDefaultNavigationContentDescription(int);
+ method public void setDefaultNavigationIcon(android.graphics.drawable.Drawable!);
+ method public void setDisplayOptions(int);
+ method public void setDropdownParams(android.widget.SpinnerAdapter!, android.widget.AdapterView.OnItemSelectedListener!);
+ method public void setDropdownSelectedPosition(int);
+ method public void setEmbeddedTabView(androidx.appcompat.widget.ScrollingTabContainerView!);
+ method public void setHomeButtonEnabled(boolean);
+ method public void setIcon(android.graphics.drawable.Drawable!);
+ method public void setIcon(int);
+ method public void setLogo(android.graphics.drawable.Drawable!);
+ method public void setLogo(int);
+ method public void setMenu(android.view.Menu!, androidx.appcompat.view.menu.MenuPresenter.Callback!);
+ method public void setMenuCallbacks(androidx.appcompat.view.menu.MenuPresenter.Callback!, androidx.appcompat.view.menu.MenuBuilder.Callback!);
+ method public void setMenuPrepared();
+ method public void setNavigationContentDescription(int);
+ method public void setNavigationContentDescription(CharSequence!);
+ method public void setNavigationIcon(android.graphics.drawable.Drawable!);
+ method public void setNavigationIcon(int);
+ method public void setNavigationMode(int);
+ method public void setSubtitle(CharSequence!);
+ method public void setTitle(CharSequence!);
+ method public void setVisibility(int);
+ method public void setWindowCallback(android.view.Window.Callback!);
+ method public void setWindowTitle(CharSequence!);
+ method public androidx.core.view.ViewPropertyAnimatorCompat! setupAnimatorToVisibility(int, long);
+ method public boolean showOverflowMenu();
+ }
+
+ public class TooltipCompat {
+ method public static void setTooltipText(android.view.View, CharSequence?);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ViewStubCompat extends android.view.View {
+ ctor public ViewStubCompat(android.content.Context, android.util.AttributeSet?);
+ ctor public ViewStubCompat(android.content.Context, android.util.AttributeSet?, int);
+ method public int getInflatedId();
+ method public android.view.LayoutInflater! getLayoutInflater();
+ method public int getLayoutResource();
+ method public android.view.View! inflate();
+ method public void setInflatedId(int);
+ method public void setLayoutInflater(android.view.LayoutInflater!);
+ method public void setLayoutResource(int);
+ method public void setOnInflateListener(androidx.appcompat.widget.ViewStubCompat.OnInflateListener!);
+ }
+
+ public static interface ViewStubCompat.OnInflateListener {
+ method public void onInflate(androidx.appcompat.widget.ViewStubCompat!, android.view.View!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ViewUtils {
+ method public static void computeFitSystemWindows(android.view.View, android.graphics.Rect, android.graphics.Rect);
+ method public static boolean isLayoutRtl(android.view.View!);
+ method public static void makeOptionalFitsSystemWindows(android.view.View!);
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface WithHint {
+ method public CharSequence? getHint();
+ }
+
+}
+
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 1387c96..5cc8a4c 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -16,10 +16,10 @@
dependencies {
api("androidx.annotation:annotation:1.3.0")
- api(projectOrArtifact(":core:core"))
+ api("androidx.core:core:1.13.0")
// Required to make activity 1.5.0-rc01 dependencies resolve.
- implementation("androidx.core:core-ktx:1.8.0")
+ implementation("androidx.core:core-ktx:1.13.0")
implementation(libs.kotlinStdlib)
implementation("androidx.emoji2:emoji2:1.3.0")
diff --git a/appcompat/appcompat/lint-baseline.xml b/appcompat/appcompat/lint-baseline.xml
index ec2d907..e5dbab9 100644
--- a/appcompat/appcompat/lint-baseline.xml
+++ b/appcompat/appcompat/lint-baseline.xml
@@ -1,18 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="NewApi"
- message="Class requires API level 21 (current min is 19): `android.graphics.drawable.AnimatedStateListDrawable`"
- errorLine1=" || button instanceof AnimatedStateListDrawable);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/appcompat/widget/AppCompatCheckBoxTest.java"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 25 (current min is 19): `new android.view.inputmethod.InputContentInfo`"
+ message="Call requires API level 25 (current min is 21): `new android.view.inputmethod.InputContentInfo`"
errorLine1=" final InputContentInfo contentInfo = new InputContentInfo("
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -21,7 +12,7 @@
<issue
id="NewApi"
- message="Call requires API level 25 (current min is 19): `android.view.inputmethod.InputConnection#commitContent`"
+ message="Call requires API level 25 (current min is 21): `android.view.inputmethod.InputConnection#commitContent`"
errorLine1=" return ic.commitContent(contentInfo, flags, null);"
errorLine2=" ~~~~~~~~~~~~~">
<location
@@ -30,7 +21,7 @@
<issue
id="NewApi"
- message="Class requires API level 26 (current min is 19): `android.view.textclassifier.TextClassifier`"
+ message="Class requires API level 26 (current min is 21): `android.view.textclassifier.TextClassifier`"
errorLine1=" private static class NoOpTextClassifier implements TextClassifier {}"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -38,24 +29,6 @@
</issue>
<issue
- id="NewApi"
- message="Class requires API level 21 (current min is 19): `android.graphics.drawable.AnimatedStateListDrawable`"
- errorLine1=" || button instanceof AnimatedStateListDrawable);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/appcompat/widget/AppCompatRadioButtonTest.java"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 20 (current min is 19): `android.view.WindowInsets#getSystemWindowInsetTop`"
- errorLine1=" mSystemWindowInsetTop = insets.getSystemWindowInsetTop();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/appcompat/custom/CustomDrawerLayout.java"/>
- </issue>
-
- <issue
id="BanSynchronizedMethods"
message="Use of synchronized methods is not recommended"
errorLine1=" public static synchronized void preload() {"
@@ -336,26 +309,367 @@
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v16`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `values`.">
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarContainer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarContainer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarContainer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarContainer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21 && !mLastInnerInsets.equals(mInnerInsets)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT < 21 && !mLastInnerInsetsRect.equals(mInnerInsetsRect)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" private static final boolean IS_PRE_LOLLIPOP = Build.VERSION.SDK_INT < 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/AppCompatImageHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" private static final boolean COMPAT_OVERLAP_ANCHOR = Build.VERSION.SDK_INT < 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/AppCompatPopupWindow.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/DropDownListView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/DropDownListView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/DropDownListView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/app/ResourcesFlusher.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/app/ResourcesFlusher.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/ShareActionProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `color`.">
+ <location
+ file="src/main/res/color-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `drawable`.">
+ <location
+ file="src/main/res/drawable-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values-ldltr`.">
+ <location
+ file="src/main/res/values-ldltr-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v16`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
<location
file="src/main/res/values-v16"/>
</issue>
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v17`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `values`.">
+ message="This folder configuration (`v17`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
<location
file="src/main/res/values-v17"/>
</issue>
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v18`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `values`.">
+ message="This folder configuration (`v18`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
<location
file="src/main/res/values-v18"/>
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="src/main/res/values-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values-watch`.">
+ <location
+ file="src/main/res/values-watch-v21"/>
+ </issue>
+
+ <issue
id="KotlinPropertyAccess"
message="The getter return type (`View`) and setter parameter type (`ScrollingTabContainerView`) getter and setter methods for property `tabContainer` should have exactly the same type to allow be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
errorLine1=" public View getTabContainer() {"
diff --git a/appsearch/appsearch-builtin-types/lint-baseline.xml b/appsearch/appsearch-builtin-types/lint-baseline.xml
new file mode 100644
index 0000000..5f9c409
--- /dev/null
+++ b/appsearch/appsearch-builtin-types/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/appsearch/app/ShortcutAdapter.java"/>
+ </issue>
+
+</issues>
diff --git a/appsearch/appsearch-ktx/build.gradle b/appsearch/appsearch-ktx/build.gradle
index afd8740..e771a6d 100644
--- a/appsearch/appsearch-ktx/build.gradle
+++ b/appsearch/appsearch-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id('AndroidXPlugin')
@@ -45,7 +45,7 @@
androidx {
name = "AppSearch - Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = '2021'
description = 'AndroidX AppSearch - Kotlin Extensions'
metalavaK2UastEnabled = true
diff --git a/appsearch/appsearch-play-services-storage/build.gradle b/appsearch/appsearch-play-services-storage/build.gradle
index ec30664..aa27864 100644
--- a/appsearch/appsearch-play-services-storage/build.gradle
+++ b/appsearch/appsearch-play-services-storage/build.gradle
@@ -58,6 +58,5 @@
android {
namespace "androidx.appsearch.playservicesstorage"
defaultConfig {
- minSdkVersion 19
}
}
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerExtension.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerExtension.kt
index 858ae55..2e9e676 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerExtension.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerExtension.kt
@@ -16,18 +16,21 @@
package androidx.baselineprofile.gradle.consumer
+import androidx.baselineprofile.gradle.utils.WarningsExtension
import javax.inject.Inject
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory
+import org.gradle.api.plugins.ExtensionAware
+import org.gradle.api.tasks.Nested
/**
* Allows specifying settings for the Baseline Profile Consumer Plugin.
*/
abstract class BaselineProfileConsumerExtension @Inject constructor(
objectFactory: ObjectFactory
-) : BaselineProfileVariantConfiguration {
+) : BaselineProfileVariantConfiguration, ExtensionAware {
companion object {
private const val EXTENSION_NAME = "baselineProfile"
@@ -45,6 +48,9 @@
}
}
+ @Nested
+ val warnings = WarningsExtension.register(this.extensions)
+
val variants: NamedDomainObjectContainer<BaselineProfileVariantConfigurationImpl> =
objectFactory.domainObjectContainer(BaselineProfileVariantConfigurationImpl::class.java)
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPlugin.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPlugin.kt
index 04dc5489..24946de 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPlugin.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPlugin.kt
@@ -17,11 +17,11 @@
package androidx.baselineprofile.gradle.consumer
import androidx.baselineprofile.gradle.configuration.ConfigurationManager
-import androidx.baselineprofile.gradle.consumer.task.MainGenerateBaselineProfileTask
+import androidx.baselineprofile.gradle.consumer.task.GenerateBaselineProfileTask
+import androidx.baselineprofile.gradle.consumer.task.MainGenerateBaselineProfileTaskForAgp80Only
import androidx.baselineprofile.gradle.consumer.task.MergeBaselineProfileTask
import androidx.baselineprofile.gradle.consumer.task.PrintConfigurationForVariantTask
import androidx.baselineprofile.gradle.consumer.task.PrintMapPropertiesForVariantTask
-import androidx.baselineprofile.gradle.consumer.task.maybeCreateGenerateTask
import androidx.baselineprofile.gradle.utils.AgpFeature
import androidx.baselineprofile.gradle.utils.AgpPlugin
import androidx.baselineprofile.gradle.utils.AgpPluginId
@@ -39,6 +39,7 @@
import androidx.baselineprofile.gradle.utils.namedOrNull
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.LibraryExtension
+import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.ApplicationVariant
import com.android.build.api.variant.ApplicationVariantBuilder
import com.android.build.api.variant.Variant
@@ -72,9 +73,12 @@
// List of the non debuggable build types
private val nonDebuggableBuildTypes = mutableListOf<String>()
+ // The baseline profile consumer extension to access non-variant specific configuration options
+ private val baselineProfileExtension = BaselineProfileConsumerExtension.register(project)
+
// Offers quick access to configuration extension, hiding the property override and merge logic
private val perVariantBaselineProfileExtensionManager =
- PerVariantConsumerExtensionManager(BaselineProfileConsumerExtension.register(project))
+ PerVariantConsumerExtensionManager(baselineProfileExtension)
// Manages creation of configurations
private val configurationManager = ConfigurationManager(project)
@@ -99,13 +103,6 @@
return camelCase(*parts.toTypedArray())
}
- private val Variant.benchmarkBuildType: String
- get() {
- val parts = listOfNotNull(BUILD_TYPE_BENCHMARK_PREFIX, buildType)
- .filter { it.isNotBlank() }
- return camelCase(*parts.toTypedArray())
- }
-
override fun onAgpPluginNotFound(pluginIds: Set<AgpPluginId>) {
throw IllegalStateException(
"""
@@ -155,6 +152,10 @@
)
}
+ override fun onFinalizeDsl(extension: AndroidComponentsExtension<*, *, *>) {
+ setWarnings(baselineProfileExtension.warnings)
+ }
+
override fun onApplicationBeforeVariants(variantBuilder: ApplicationVariantBuilder) {
// Note that the lifecycle is for each variant `beforeVariant`, `onVariant`. This means
@@ -176,7 +177,10 @@
afterVariants {
if (!variantBuilder.enable && isBaselineProfilePluginCreatedBuildType) {
removeOnVariantCallback(variantBuilder.name)
- logger.info(
+ logger.warn(
+ property = { disabledVariants },
+ propertyName = "disabledVariants",
+ message =
"Variant `${variantBuilder.name}` is disabled. If this " +
"is not intentional, please check your gradle configuration " +
"for beforeVariants blocks. For more information on variant " +
@@ -318,6 +322,7 @@
outputDir = mergedTaskOutputDir,
filterRules = variantConfiguration.filterRules,
library = isLibraryModule(),
+ warnings = baselineProfileExtension.warnings,
// Note that the merge task is the last task only if saveInSrc is disabled. When
// saveInSrc is enabled an additional task is created to copy the profile in the sources
@@ -357,7 +362,9 @@
library = isLibraryModule(),
sourceDir = mergeTaskProvider.flatMap { it.baselineProfileDir },
outputDir = project.provider { srcOutputDir },
- isLastTask = true
+ hasDependencies = baselineProfileConfiguration.allDependencies.isNotEmpty(),
+ isLastTask = true,
+ warnings = baselineProfileExtension.warnings
)
// Applies the source path for this variant. Note that this doesn't apply when the
@@ -498,7 +505,7 @@
// Here we create the final generate task that triggers the whole generation for this
// variant and all the parent tasks. For this one the child task is either copy or merge,
// depending on the configuration.
- maybeCreateGenerateTask<Task>(
+ GenerateBaselineProfileTask.maybeCreate(
project = project,
variantName = mergeAwareTaskName,
lastTaskProvider = lastTaskProvider
@@ -511,7 +518,7 @@
!variant.buildType.isNullOrBlank() &&
variant.buildType != variant.name
) {
- maybeCreateGenerateTask<Task>(
+ GenerateBaselineProfileTask.maybeCreate(
project = project,
variantName = variant.buildType!!,
lastTaskProvider = lastTaskProvider
@@ -534,7 +541,7 @@
.filter { it != variant.name && it.isNotBlank() }
.toSet()
.forEach {
- maybeCreateGenerateTask<Task>(
+ GenerateBaselineProfileTask.maybeCreate(
project = project,
variantName = it,
lastTaskProvider = lastTaskProvider
@@ -543,7 +550,7 @@
}
// Generate the main global tasks `generateBaselineProfile
- maybeCreateGenerateTask<Task>(
+ GenerateBaselineProfileTask.maybeCreate(
project = project,
variantName = "",
lastTaskProvider = lastTaskProvider
@@ -557,10 +564,11 @@
// task, such as `generateFreeBaselineProfile` because that would run generation for
// all the build types with flavor free, that is not as well supported.
if (variant.buildType == RELEASE) {
- maybeCreateGenerateTask<MainGenerateBaselineProfileTask>(
+ MainGenerateBaselineProfileTaskForAgp80Only.maybeCreate(
project = project,
variantName = "",
- lastTaskProvider = lastTaskProvider
+ lastTaskProvider = lastTaskProvider,
+ warnings = baselineProfileExtension.warnings
)
}
}
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/GenerateBaselineProfileTask.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/GenerateBaselineProfileTask.kt
index 189938f..86f3578 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/GenerateBaselineProfileTask.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/GenerateBaselineProfileTask.kt
@@ -16,13 +16,16 @@
package androidx.baselineprofile.gradle.consumer.task
+import androidx.baselineprofile.gradle.utils.BaselineProfilePluginLogger
import androidx.baselineprofile.gradle.utils.TASK_NAME_SUFFIX
+import androidx.baselineprofile.gradle.utils.Warnings
import androidx.baselineprofile.gradle.utils.maybeRegister
import com.android.build.gradle.internal.tasks.BuildAnalyzer
import com.android.buildanalyzer.common.TaskCategory
import org.gradle.api.DefaultTask
import org.gradle.api.Project
-import org.gradle.api.Task
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.TaskProvider
import org.gradle.work.DisableCachingByDefault
@@ -30,46 +33,85 @@
private const val GENERATE_TASK_NAME = "generate"
/**
- * Creates the `generate<variant>BaselineProfile` task. Note that this task does nothing on its
- * own and it's only to start the generation process.
+ * This task does nothing and it's only to start the generation process.
*/
-internal inline fun <reified T : Task> maybeCreateGenerateTask(
- project: Project,
- variantName: String,
- lastTaskProvider: TaskProvider<*>? = null
-) = project.tasks.maybeRegister<T>(GENERATE_TASK_NAME, variantName, TASK_NAME_SUFFIX) {
- it.group = "Baseline Profile"
- it.description = "Generates a baseline profile for the specified variants or dimensions."
- if (lastTaskProvider != null) it.dependsOn(lastTaskProvider)
-}
-
@DisableCachingByDefault(because = "Not worth caching.")
@BuildAnalyzer(primaryTaskCategory = TaskCategory.OPTIMIZATION)
-abstract class MainGenerateBaselineProfileTask : DefaultTask() {
+internal abstract class GenerateBaselineProfileTask : DefaultTask() {
+
+ companion object {
+ fun maybeCreate(
+ project: Project,
+ variantName: String,
+ lastTaskProvider: TaskProvider<*>? = null,
+ ) = project.tasks.maybeRegister<GenerateBaselineProfileTask>(
+ GENERATE_TASK_NAME,
+ variantName,
+ TASK_NAME_SUFFIX
+ ) {
+ if (lastTaskProvider != null) it.dependsOn(lastTaskProvider)
+ }
+ }
init {
group = "Baseline Profile"
- description = "Generates a baseline profile"
+ description = "Generates a baseline profile for the specified variants or dimensions."
}
+}
+
+/**
+ * This task does nothing and it's only to start the generation process. This task differs from
+ * [GenerateBaselineProfileTask] and it's used ONLY to print the warning about
+ * `generateBaselineProfile` generating only for `release` with AGP 8.0.
+ */
+@DisableCachingByDefault(because = "Not worth caching.")
+@BuildAnalyzer(primaryTaskCategory = TaskCategory.OPTIMIZATION)
+internal abstract class MainGenerateBaselineProfileTaskForAgp80Only : DefaultTask() {
+
+ companion object {
+ fun maybeCreate(
+ project: Project,
+ variantName: String,
+ lastTaskProvider: TaskProvider<*>? = null,
+ warnings: Warnings
+ ) = project.tasks.maybeRegister<MainGenerateBaselineProfileTaskForAgp80Only>(
+ GENERATE_TASK_NAME,
+ variantName,
+ TASK_NAME_SUFFIX
+ ) {
+ if (lastTaskProvider != null) it.dependsOn(lastTaskProvider)
+ it.printWarningMultipleBuildTypesWithAgp80.set(warnings.multipleBuildTypesWithAgp80)
+ }
+ }
+
+ init {
+ group = "Baseline Profile"
+ description = "Generates a baseline profile for the `release` variant."
+ }
+
+ @get:Input
+ abstract val printWarningMultipleBuildTypesWithAgp80: Property<Boolean>
+
+ private val logger by lazy { BaselineProfilePluginLogger(this.getLogger()) }
@TaskAction
fun exec() {
this.logger.warn(
- """
- The task `generateBaselineProfile` cannot currently support
- generation for all the variants when there are multiple build
- types without improvements planned for a future version of the
- Android Gradle Plugin.
- Until then, `generateBaselineProfile` will only generate
- baseline profiles for the variants of the release build type,
- behaving like `generateReleaseBaselineProfile`.
- If you intend to generate profiles for multiple build types
- you'll need to run separate gradle commands for each build type.
- For example: `generateReleaseBaselineProfile` and
- `generateAnotherReleaseBaselineProfile`.
+ property = { printWarningMultipleBuildTypesWithAgp80.get() },
+ propertyName = "multipleBuildTypesWithAgp80",
+ message = """
+ The task `generateBaselineProfile` does not support generating baseline profiles for
+ multiple build types with AGP 8.0.
- Details on https://issuetracker.google.com/issue?id=270433400.
- """.trimIndent()
+ Only baseline profile for variants of build type `release` will be generated.
+ With AGP 8.0, this command behaves like `generateReleaseBaselineProfile`.
+
+ If you intend to generate profiles for multiple build types using AGP 8.0 you'll
+ need to run separate gradle commands for each build type.
+ Example: `generateReleaseBaselineProfile` and `generateAnotherReleaseBaselineProfile`.
+
+ Details on https://issuetracker.google.com/issue?id=270433400.
+ """.trimIndent()
)
}
}
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/MergeBaselineProfileTask.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/MergeBaselineProfileTask.kt
index 5dfd6b5..c886ed9 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/MergeBaselineProfileTask.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/consumer/task/MergeBaselineProfileTask.kt
@@ -17,7 +17,9 @@
package androidx.baselineprofile.gradle.consumer.task
import androidx.baselineprofile.gradle.consumer.RuleType
+import androidx.baselineprofile.gradle.utils.BaselineProfilePluginLogger
import androidx.baselineprofile.gradle.utils.TASK_NAME_SUFFIX
+import androidx.baselineprofile.gradle.utils.Warnings
import androidx.baselineprofile.gradle.utils.maybeRegister
import com.android.build.gradle.internal.tasks.BuildAnalyzer
import com.android.buildanalyzer.common.TaskCategory
@@ -74,12 +76,13 @@
project: Project,
variantName: String,
mergeAwareTaskName: String,
- hasDependencies: Boolean = false,
+ hasDependencies: Boolean,
library: Boolean,
sourceProfilesFileCollection: FileCollection,
outputDir: Provider<Directory>,
filterRules: List<Pair<RuleType, String>> = listOf(),
- isLastTask: Boolean
+ isLastTask: Boolean,
+ warnings: Warnings
): TaskProvider<MergeBaselineProfileTask> {
return project
.tasks
@@ -113,6 +116,15 @@
// Determines whether this is the last task to be executed. This flag is used
// exclusively for logging purposes.
task.lastTask.set(isLastTask)
+
+ // Determines whether this task should print warnings. Note that warnings used
+ // by Android Studio cannot be suppressed.
+ task.printWarningNoBaselineProfileRulesGenerated
+ .set(warnings.noBaselineProfileRulesGenerated)
+ task.printWarningNoStartupProfileRulesGenerated
+ .set(warnings.noStartupProfileRulesGenerated)
+ task.printWarningVariantHasNoBaselineProfileDependency
+ .set(warnings.variantHasNoBaselineProfileDependency)
}
}
@@ -123,7 +135,9 @@
library: Boolean,
sourceDir: Provider<Directory>,
outputDir: Provider<Directory>,
- isLastTask: Boolean
+ isLastTask: Boolean,
+ hasDependencies: Boolean,
+ warnings: Warnings
): TaskProvider<MergeBaselineProfileTask> {
return project
.tasks
@@ -132,14 +146,20 @@
mergeAwareTaskName,
"baselineProfileIntoSrc"
) { task ->
+ // For explanation about each of these properties, see above function named
+ // `maybeRegisterForMerge`.
task.baselineProfileFileCollection.from.add(sourceDir)
task.baselineProfileDir.set(outputDir)
task.library.set(library)
task.variantName.set(variantName)
-
- // Determines whether this is the last task to be executed. This flag is used
- // exclusively for logging purposes.
task.lastTask.set(isLastTask)
+ task.hasDependencies.set(hasDependencies)
+ task.printWarningNoBaselineProfileRulesGenerated
+ .set(warnings.noBaselineProfileRulesGenerated)
+ task.printWarningNoStartupProfileRulesGenerated
+ .set(warnings.noStartupProfileRulesGenerated)
+ task.printWarningVariantHasNoBaselineProfileDependency
+ .set(warnings.variantHasNoBaselineProfileDependency)
}
}
}
@@ -167,17 +187,52 @@
@get:OutputDirectory
abstract val baselineProfileDir: DirectoryProperty
+ @get:Input
+ abstract val printWarningNoBaselineProfileRulesGenerated: Property<Boolean>
+
+ @get:Input
+ abstract val printWarningNoStartupProfileRulesGenerated: Property<Boolean>
+
+ @get:Input
+ abstract val printWarningVariantHasNoBaselineProfileDependency: Property<Boolean>
+
+ private val logger by lazy { BaselineProfilePluginLogger(this.getLogger()) }
+
+ private val variantHasDependencies by lazy {
+ hasDependencies.isPresent && hasDependencies.get()
+ }
+
@TaskAction
fun exec() {
- if (hasDependencies.isPresent && !hasDependencies.get()) {
- throw GradleException(
- """
+ // This warning should be printed only if no dependency has been set for the processed
+ // variant.
+ if (lastTask.get() && !variantHasDependencies) {
+ logger.warn(
+ property = { printWarningVariantHasNoBaselineProfileDependency.get() },
+ propertyName = "variantHasNoBaselineProfileDependency",
+ message = """
The baseline profile consumer plugin is applied to this module but no dependency
- has been set. Please review the configuration of build.gradle for this module
- making sure that a `baselineProfile` dependency exists and points to a valid
- `com.android.test` module that has the `androidx.baselineprofile` or
- `androidx.baselineprofile.producer` plugin applied.
+ has been set for variant `${variantName.get()}`, so no baseline profile will be
+ generated for it.
+
+ A dependency for all the variants can be added in the dependency block using
+ `baselineProfile` configuration:
+
+ dependencies {
+ ...
+ baselineProfile(project(":baselineprofile"))
+ }
+
+ Or for a specific variant in the baseline profile block:
+
+ baselineProfile {
+ variants {
+ freeRelease {
+ from(project(":baselineprofile"))
+ }
+ }
+ }
""".trimIndent()
)
}
@@ -195,15 +250,19 @@
// Read the profile rules from the file collection that contains the profile artifacts from
// all the configurations for this variant and merge them in a single list.
- val profileRules = baselineProfileFileCollection.files
+ val baselineProfileRules = baselineProfileFileCollection.files
.readLines {
FILENAME_MATCHER_BASELINE_PROFILE in it.name ||
FILENAME_MATCHER_STARTUP_PROFILE in it.name
}
- if (variantName.isPresent && profileRules.isEmpty()) {
+ // This warning should be printed only if the variant has dependencies but there are no
+ // baseline profile rules.
+ if (lastTask.get() && variantHasDependencies && baselineProfileRules.isEmpty()) {
logger.warn(
- """
+ property = { printWarningNoBaselineProfileRulesGenerated.get() },
+ propertyName = "noBaselineProfileRulesGenerated",
+ message = """
No baseline profile rules were generated for the variant `${variantName.get()}`.
This is most likely because there are no instrumentation test for it. If this
is not intentional check that tests for this variant exist in the `baselineProfile`
@@ -218,7 +277,7 @@
// - group by class and method (ignoring flag) and for each group keep only the first value
// - apply the filters
// - sort with comparator
- val filteredProfileRules = profileRules
+ val filteredBaselineProfileRules = baselineProfileRules
.sorted()
.asSequence()
.mapNotNull { ProfileRule.parse(it) }
@@ -241,10 +300,12 @@
return@filter !rules.any { r -> r.isInclude() }
}
.sortedWith(ProfileRule.comparator)
- .map { it.underlying }
// Check if the filters filtered out all the rules.
- if (profileRules.isNotEmpty() && filteredProfileRules.isEmpty() && rules.isNotEmpty()) {
+ if (baselineProfileRules.isNotEmpty() &&
+ filteredBaselineProfileRules.isEmpty() &&
+ rules.isNotEmpty()
+ ) {
throw GradleException(
"""
The baseline profile consumer plugin is configured with filters that exclude all
@@ -254,24 +315,11 @@
)
}
- baselineProfileDir
- .file(BASELINE_PROFILE_FILENAME)
- .get()
- .asFile
- .apply {
- delete()
- if (filteredProfileRules.isNotEmpty()) {
- writeText(filteredProfileRules.joinToString(System.lineSeparator()))
- if (lastTask.get()) {
- logger.warn(
- """
- A baseline profile was generated for the variant `${variantName.get()}`:
- ${Path(absolutePath).toUri()}
- """.trimIndent()
- )
- }
- }
- }
+ writeProfile(
+ filename = BASELINE_PROFILE_FILENAME,
+ rules = filteredBaselineProfileRules,
+ profileType = "baseline"
+ )
// If this is a library we can stop here and don't manage the startup profiles.
if (library.get()) {
@@ -282,9 +330,13 @@
val startupRules = baselineProfileFileCollection.files
.readLines { FILENAME_MATCHER_STARTUP_PROFILE in it.name }
- if (variantName.isPresent && startupRules.isEmpty()) {
+ // This warning should be printed only if the variant has dependencies but there are no
+ // startup profile rules.
+ if (lastTask.get() && variantHasDependencies && startupRules.isEmpty()) {
logger.warn(
- """
+ property = { printWarningNoStartupProfileRulesGenerated.get() },
+ propertyName = "noBaselineProfileRulesGenerated",
+ message = """
No startup profile rules were generated for the variant `${variantName.get()}`.
This is most likely because there are no instrumentation test with baseline profile
rule, which specify `includeInStartupProfile = true`. If this is not intentional
@@ -294,32 +346,72 @@
}
// Use same sorting without filter for startup profiles.
- val sortedProfileRules = startupRules
+ val sortedStartupProfileRules = startupRules
.asSequence()
.sorted()
.mapNotNull { ProfileRule.parse(it) }
.groupBy { it.classDescriptor + it.methodDescriptor }
.map { it.value[0] }
.sortedWith(ProfileRule.comparator)
- .map { it.underlying }
- .toList()
+ writeProfile(
+ filename = STARTUP_PROFILE_FILENAME,
+ profileType = "startup",
+ rules = sortedStartupProfileRules,
+ )
+ }
+
+ private fun writeProfile(filename: String, rules: List<ProfileRule>, profileType: String) {
baselineProfileDir
- .file(STARTUP_PROFILE_FILENAME)
+ .file(filename)
.get()
.asFile
.apply {
+
+ // If an old profile file already exists calculate stats.
+ val stats = if (exists()) ProfileStats.from(
+ existingRules = readLines().mapNotNull { ProfileRule.parse(it) },
+ newRules = rules
+ ) else null
+
delete()
- if (sortedProfileRules.isNotEmpty()) {
- writeText(sortedProfileRules.joinToString(System.lineSeparator()))
- if (lastTask.get()) {
- logger.warn(
- """
- A startup profile was generated for the variant `${variantName.get()}`:
- ${Path(absolutePath).toUri()}
+ if (rules.isEmpty()) return
+ writeText(rules.joinToString(System.lineSeparator()) { it.underlying })
+
+ // If this is the last task display a success message (depending on the flag
+ // `saveInSrc` this task may be configured as a merge or copy task).
+ if (!lastTask.get()) {
+ return
+ }
+
+ // This log should not be suppressed because it's used by Android Studio to
+ // open the generated HRF file.
+ logger.warn(
+ property = { true },
+ propertyName = null,
+ message = """
+
+ A $profileType profile was generated for the variant `${variantName.get()}`:
+ ${Path(absolutePath).toUri()}
""".trimIndent()
- )
- }
+ )
+
+ // Print stats if was previously calculated
+ stats?.apply {
+ logger.warn(
+ property = { true },
+ propertyName = null,
+ message = """
+
+ Comparison with previous $profileType profile:
+ $existing Old rules
+ $new New rules
+ $added Added rules (${"%.2f".format(addedRatio * 100)}%)
+ $removed Removed rules (${"%.2f".format(removedRatio * 100)}%)
+ $unmodified Unmodified rules (${"%.2f".format(unmodifiedRatio * 100)}%)
+
+ """.trimIndent()
+ )
}
}
}
@@ -363,3 +455,54 @@
.filter(filterBlock)
.flatMap { it.readLines() }
}
+
+internal data class ProfileStats(
+ val existing: Int,
+ val new: Int,
+ val added: Int,
+ val removed: Int,
+ val unmodified: Int,
+ val addedRatio: Float,
+ val removedRatio: Float,
+ val unmodifiedRatio: Float,
+) {
+ companion object {
+ fun from(existingRules: List<ProfileRule>, newRules: List<ProfileRule>): ProfileStats {
+ val existingRulesSet = existingRules
+ .map { "${it.classDescriptor}:${it.methodDescriptor}" }
+ .toHashSet()
+ val newRulesSet = newRules
+ .map { "${it.classDescriptor}:${it.methodDescriptor}" }
+ .toHashSet()
+ val allUniqueRules = existingRulesSet.union(newRulesSet).size
+
+ var unmodified = 0
+ var added = 0
+ var removed = 0
+
+ for (x in existingRulesSet) {
+ if (x in newRulesSet) {
+ unmodified++
+ } else {
+ removed++
+ }
+ }
+ for (x in newRulesSet) {
+ if (x !in existingRulesSet) {
+ added++
+ }
+ }
+
+ return ProfileStats(
+ existing = existingRulesSet.size,
+ new = newRulesSet.size,
+ unmodified = unmodified,
+ added = added,
+ removed = removed,
+ addedRatio = added.toFloat() / allUniqueRules,
+ removedRatio = removed.toFloat() / allUniqueRules,
+ unmodifiedRatio = unmodified.toFloat() / allUniqueRules
+ )
+ }
+ }
+}
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/AgpPlugin.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/AgpPlugin.kt
index fd7ee6a..2c6bbf8 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/AgpPlugin.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/AgpPlugin.kt
@@ -36,7 +36,6 @@
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.Task
-import org.gradle.api.logging.Logger
import org.gradle.api.tasks.TaskProvider
/**
@@ -50,8 +49,7 @@
private val maxAgpVersionExclusive: AndroidPluginVersion,
) {
- protected val logger: Logger
- get() = project.logger
+ protected val logger = BaselineProfilePluginLogger(project.logger)
// Defines a list of block to be executed after all the onVariants callback
private val afterVariantsBlocks = mutableListOf<() -> (Unit)>()
@@ -93,8 +91,6 @@
private fun configureWithAndroidPlugin() {
- checkAgpVersion()
-
onBeforeFinalizeDsl()
testAndroidComponentExtension()?.let { testComponent ->
@@ -126,6 +122,11 @@
androidComponentsExtension()?.let { commonComponent ->
commonComponent.finalizeDsl { onFinalizeDsl(commonComponent) }
+
+ // Note that check agp version can be performed only here, because one of the plugins
+ // may set suppress warning option.
+ checkAgpVersion()
+
commonComponent.beforeVariants { onBeforeVariants(it) }
commonComponent.onVariants {
onVariantBlockScheduler.onVariant(it)
@@ -204,6 +205,10 @@
protected fun isGradleSyncRunning() = project.isGradleSyncRunning()
+ protected fun setWarnings(warnings: Warnings) {
+ logger.setWarnings(warnings)
+ }
+
protected fun afterVariants(block: () -> (Unit)) = afterVariantsBlocks.add(block)
@JvmName("onVariant")
@@ -247,7 +252,9 @@
}
if (agpVersion >= maxAgpVersionExclusive) {
logger.warn(
- """
+ property = { maxAgpVersion },
+ propertyName = "maxAgpVersion",
+ message = """
This version of the Baseline Profile Gradle Plugin was tested with versions below Android
Gradle Plugin version $maxAgpVersionExclusive and it may not work as intended.
Current version is $agpVersion.
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfilePluginLogger.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfilePluginLogger.kt
new file mode 100644
index 0000000..8658412
--- /dev/null
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfilePluginLogger.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024 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.baselineprofile.gradle.utils
+
+import org.gradle.api.logging.Logger
+
+internal class BaselineProfilePluginLogger(private val logger: Logger) {
+
+ private var warnings = Warnings()
+
+ fun setWarnings(warnings: Warnings) {
+ this.warnings = warnings
+ }
+
+ fun debug(message: String) = logger.debug(message)
+
+ fun info(message: String) = logger.info(message)
+
+ fun warn(
+ property: Warnings.() -> (Boolean),
+ propertyName: String?,
+ message: String
+ ) {
+ if (property(warnings)) {
+ logger.warn(message)
+ if (propertyName != null) {
+ logger.warn(
+ """
+
+ This warning can be disabled setting the following property:
+ baselineProfile {
+ warnings {
+ $propertyName = false
+ }
+ }
+ """.trimIndent()
+ )
+ }
+ }
+ }
+
+ fun error(message: String) = logger.error(message)
+}
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/WarningsExtension.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/WarningsExtension.kt
new file mode 100644
index 0000000..d9cd3d2
--- /dev/null
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/WarningsExtension.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2024 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.baselineprofile.gradle.utils
+
+import org.gradle.api.plugins.ExtensionContainer
+
+/**
+ * Extensions to enable/disable each warnings separately.
+ */
+abstract class WarningsExtension : Warnings() {
+
+ companion object {
+ private const val EXTENSION_NAME = "warnings"
+
+ internal fun register(extensions: ExtensionContainer): Warnings {
+ return extensions.findByType(Warnings::class.java)
+ ?: extensions.create(EXTENSION_NAME, Warnings::class.java)
+ }
+ }
+}
+
+open class Warnings {
+
+ /**
+ * Controls all the warnings printed by the baseline profile gradle plugin.
+ */
+ fun setAll(value: Boolean) {
+ this.maxAgpVersion = value
+ this.disabledVariants = value
+ this.multipleBuildTypesWithAgp80 = value
+ this.noBaselineProfileRulesGenerated = value
+ this.noStartupProfileRulesGenerated = value
+ }
+
+ /**
+ * Controls the warning for when the Android Gradle Plugin version is higher than the max
+ * tested one.
+ */
+ var maxAgpVersion = true
+
+ /**
+ * Controls the warning for when a benchmark or baseline profile variant has been disabled.
+ */
+ var disabledVariants = true
+
+ /**
+ * Controls the warning printed when invoking `generateBaselineProfile` with AGP 8.0, that
+ * does not support running instrumentation tests for multiple build types at once.
+ */
+ var multipleBuildTypesWithAgp80 = true
+
+ /**
+ * Controls the warning printed when no baseline profile are generated after running
+ * the generate baseline profile command.
+ */
+ var noBaselineProfileRulesGenerated = true
+
+ /**
+ * Controls the warning printed when no startup profile are generated after running
+ * the generate baseline profile command.
+ */
+ var noStartupProfileRulesGenerated = true
+
+ /**
+ * Controls the warning printed when a variant has no baseline profile dependency set,
+ * either globally or a specific one.
+ */
+ var variantHasNoBaselineProfileDependency = true
+}
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt
index 92750a7..aefa4be 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt
@@ -386,18 +386,12 @@
flavors = false,
dependencyOnProducerProject = false
)
-
- gradleRunner
- .withArguments("generateReleaseBaselineProfile", "--stacktrace")
- .buildAndFail()
- .output
- .replace("\n", " ")
- .also {
- assertThat(it).contains(
- "The baseline profile consumer plugin is applied to " +
- "this module but no dependency has been set"
- )
- }
+ gradleRunner.build("generateReleaseBaselineProfile", "--stacktrace") {
+ assertThat(it.replace("\n", " ")).contains(
+ "The baseline profile consumer plugin is applied to this module but no " +
+ "dependency has been set for variant `release`"
+ )
+ }
}
@Test
@@ -1345,6 +1339,119 @@
contains("Variant `benchmarkRelease` is disabled.")
}
}
+
+ @Test
+ fun testProfileStats() {
+ projectSetup.consumer.setup(
+ androidPlugin = ANDROID_APPLICATION_PLUGIN
+ )
+
+ // Test no previous execution
+ projectSetup.producer.setupWithoutFlavors(
+ releaseProfileLines = listOf(
+ Fixtures.CLASS_1_METHOD_1,
+ Fixtures.CLASS_1,
+ ),
+ releaseStartupProfileLines = listOf(
+ Fixtures.CLASS_1_METHOD_1,
+ Fixtures.CLASS_1,
+ )
+ )
+ gradleRunner.build("generateBaselineProfile") {
+ val notFound = it.lines().requireInOrder(
+ "Comparison with previous baseline profile:",
+ "Comparison with previous startup profile:",
+ )
+ assertThat(notFound.size).isEqualTo(2)
+ }
+
+ // Test unchanged
+ gradleRunner.build("generateBaselineProfile", "--rerun-tasks") {
+ println(it)
+ val notFound = it.lines().requireInOrder(
+ "Comparison with previous baseline profile:",
+ " 2 Old rules",
+ " 2 New rules",
+ " 0 Added rules (0.00%)",
+ " 0 Removed rules (0.00%)",
+ " 2 Unmodified rules (100.00%)",
+
+ "Comparison with previous startup profile:",
+ " 2 Old rules",
+ " 2 New rules",
+ " 0 Added rules (0.00%)",
+ " 0 Removed rules (0.00%)",
+ " 2 Unmodified rules (100.00%)",
+ )
+ assertThat(notFound).isEmpty()
+ }
+
+ // Test added
+ projectSetup.producer.setupWithoutFlavors(
+ releaseProfileLines = listOf(
+ Fixtures.CLASS_1_METHOD_1,
+ Fixtures.CLASS_1,
+ Fixtures.CLASS_2_METHOD_2,
+ Fixtures.CLASS_2,
+ ),
+ releaseStartupProfileLines = listOf(
+ Fixtures.CLASS_1_METHOD_1,
+ Fixtures.CLASS_1,
+ Fixtures.CLASS_2_METHOD_2,
+ Fixtures.CLASS_2,
+ )
+ )
+ gradleRunner.build("generateBaselineProfile", "--rerun-tasks") {
+ println(it)
+ val notFound = it.lines().requireInOrder(
+ "Comparison with previous baseline profile:",
+ " 2 Old rules",
+ " 4 New rules",
+ " 2 Added rules (50.00%)",
+ " 0 Removed rules (0.00%)",
+ " 2 Unmodified rules (50.00%)",
+
+ "Comparison with previous startup profile:",
+ " 2 Old rules",
+ " 4 New rules",
+ " 2 Added rules (50.00%)",
+ " 0 Removed rules (0.00%)",
+ " 2 Unmodified rules (50.00%)",
+ )
+ assertThat(notFound).isEmpty()
+ }
+
+ // Test removed
+ projectSetup.producer.setupWithoutFlavors(
+ releaseProfileLines = listOf(
+ Fixtures.CLASS_2_METHOD_2,
+ Fixtures.CLASS_2,
+ ),
+ releaseStartupProfileLines = listOf(
+ Fixtures.CLASS_2_METHOD_2,
+ Fixtures.CLASS_2,
+ )
+ )
+ gradleRunner.build("generateBaselineProfile", "--rerun-tasks") {
+ println(it)
+ val notFound = it.lines().requireInOrder(
+ "Comparison with previous baseline profile:",
+ " 4 Old rules",
+ " 2 New rules",
+ " 0 Added rules (0.00%)",
+ " 2 Removed rules (50.00%)",
+ " 2 Unmodified rules (50.00%)",
+
+ "Comparison with previous startup profile:",
+ " 4 Old rules",
+ " 2 New rules",
+ " 0 Added rules (0.00%)",
+ " 2 Removed rules (50.00%)",
+ " 2 Unmodified rules (50.00%)",
+ )
+ assertThat(notFound).isEmpty()
+ }
+ }
}
@RunWith(JUnit4::class)
@@ -1472,42 +1579,50 @@
}
@Test
- fun testGenerateTaskWithFlavorsAndMergeAll() {
+ fun testSuppressWarningMainGenerateTask() {
+ val requiredLines = listOf(
+ "The task `generateBaselineProfile` does not support generating baseline profiles for",
+ "multiple build types with AGP 8.0.",
+ "This warning can be disabled setting the following property:",
+ "baselineProfile {",
+ " warnings {",
+ " multipleBuildTypesWithAgp80 = false",
+ " }",
+ "}"
+ )
+ projectSetup.producer.setupWithoutFlavors(
+ releaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1),
+ )
+
+ // Setup with default warnings
+ projectSetup.consumer.setup(
+ androidPlugin = ANDROID_APPLICATION_PLUGIN
+ )
+ projectSetup
+ .consumer
+ .gradleRunner
+ .build("generateBaselineProfile") {
+ println(it)
+ val notFound = it.lines().requireInOrder(*requiredLines.toTypedArray())
+ assertThat(notFound).isEmpty()
+ }
+
+ // Setup turning off warning
projectSetup.consumer.setup(
androidPlugin = ANDROID_APPLICATION_PLUGIN,
- flavors = true,
- dependencyOnProducerProject = true,
baselineProfileBlock = """
- mergeIntoMain = true
+ warnings {
+ multipleBuildTypesWithAgp80 = false
+ }
""".trimIndent()
)
- projectSetup.producer.setupWithFreeAndPaidFlavors(
- freeReleaseProfileLines = listOf(Fixtures.CLASS_1_METHOD_1, Fixtures.CLASS_1),
- paidReleaseProfileLines = listOf(Fixtures.CLASS_2_METHOD_1, Fixtures.CLASS_2)
- )
-
- // Asserts that all per-variant, per-flavor and per-build type tasks are being generated.
- projectSetup.consumer.gradleRunner.buildAndAssertThatOutput("tasks") {
- contains("generateBaselineProfile - ")
- contains("generateReleaseBaselineProfile - ")
- doesNotContain("generateFreeReleaseBaselineProfile - ")
- doesNotContain("generatePaidReleaseBaselineProfile - ")
- }
-
- projectSetup.consumer.gradleRunner
- .withArguments("generateBaselineProfile", "--stacktrace")
- .build()
-
- val lines = File(
- projectSetup.consumer.rootDir,
- "src/main/$EXPECTED_PROFILE_FOLDER/baseline-prof.txt"
- ).readLines()
- assertThat(lines).containsExactly(
- Fixtures.CLASS_1,
- Fixtures.CLASS_1_METHOD_1,
- Fixtures.CLASS_2,
- Fixtures.CLASS_2_METHOD_1,
- )
+ projectSetup
+ .consumer
+ .gradleRunner
+ .build("generateBaselineProfile") {
+ val notFound = it.lines().requireInOrder(*requiredLines.toTypedArray())
+ assertThat(notFound).isEqualTo(requiredLines)
+ }
}
}
diff --git a/benchmark/benchmark-common/lint-baseline.xml b/benchmark/benchmark-common/lint-baseline.xml
index 01496cf7..0568301 100644
--- a/benchmark/benchmark-common/lint-baseline.xml
+++ b/benchmark/benchmark-common/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="UnsafeOptInUsageError"
@@ -15,4 +15,337 @@
file="src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Api21.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // needed for shell access"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Profiler.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/PropOverride.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/Shell.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/SideEffects.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/benchmark/SideEffects.kt"/>
+ </issue>
+
</issues>
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateConfigTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateConfigTest.kt
index 0c71b29..65b2efc 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateConfigTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateConfigTest.kt
@@ -16,7 +16,6 @@
package androidx.benchmark
-import android.os.Build
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SmallTest
@@ -39,14 +38,11 @@
val state = BenchmarkState(config)
var count = 0
while (state.keepRunning()) {
- if (Build.VERSION.SDK_INT < 21) {
- // This spin loop works around an issue where on Mako API 17, nanoTime is only
- // precise to 30us. A more ideal fix might introduce an automatic divisor to
- // WarmupManager when the duration values it sees are 0, but this is simple.
- val start = System.nanoTime()
- @Suppress("ControlFlowWithEmptyBody")
- while (System.nanoTime() == start) {}
- }
+ // This spin loop works around an issue where nanoTime is only precise to 30us on some
+ // devices. This was reproduced on api 17 and emulators api 33. (b/331226761)
+ val start = System.nanoTime()
+ @Suppress("ControlFlowWithEmptyBody")
+ while (System.nanoTime() == start) {}
count++
}
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/MetricResultTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/MetricResultTest.kt
index f68f14d..ac0aee1 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/MetricResultTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/MetricResultTest.kt
@@ -18,15 +18,25 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import kotlin.test.assertFailsWith
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
-public class MetricResultTest {
+class MetricResultTest {
@Test
- public fun repeat() {
+ fun constructorThrowsIfEmpty() {
+ val exception = assertFailsWith<IllegalArgumentException> {
+ MetricResult("test", emptyList())
+ }
+
+ assertEquals("At least one result is necessary, 0 found for test.", exception.message!!)
+ }
+
+ @Test
+ fun repeat() {
val metricResult = MetricResult("test", listOf(10.0, 10.0, 10.0, 10.0))
assertEquals(10.0, metricResult.min, 0.0)
assertEquals(10.0, metricResult.max, 0.0)
@@ -39,7 +49,7 @@
}
@Test
- public fun one() {
+ fun one() {
val metricResult = MetricResult("test", listOf(10.0))
assertEquals(10.0, metricResult.min, 0.0)
assertEquals(10.0, metricResult.max, 0.0)
@@ -52,7 +62,7 @@
}
@Test
- public fun simple() {
+ fun simple() {
val metricResult = MetricResult("test", (0..100).map { it.toDouble() })
assertEquals(50.0, metricResult.median, 0.0)
assertEquals(100.0, metricResult.max, 0.0)
@@ -65,7 +75,7 @@
}
@Test
- public fun lerp() {
+ fun lerp() {
assertEquals(MetricResult.lerp(0.0, 1000.0, 0.5), 500.0, 0.0)
assertEquals(MetricResult.lerp(0.0, 1000.0, 0.75), 750.0, 0.0)
assertEquals(MetricResult.lerp(0.0, 1000.0, 0.25), 250.0, 0.0)
@@ -73,7 +83,7 @@
}
@Test
- public fun getPercentile() {
+ fun getPercentile() {
(0..100).forEach {
assertEquals(
it.toDouble(),
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricResult.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricResult.kt
index 3af2d32..357557a 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricResult.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricResult.kt
@@ -47,7 +47,7 @@
init {
val values = data.sorted()
val size = values.size
- require(size >= 1) { "At least one result is necessary." }
+ require(size >= 1) { "At least one result is necessary, $size found for $name." }
val mean: Double = data.average()
min = values.first()
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt
index 42ed2e9..639e350 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Profiler.kt
@@ -77,6 +77,16 @@
source = null
)
+ fun ofMethodTrace(
+ label: String,
+ absolutePath: String
+ ) = ResultFile(
+ label = label,
+ outputRelativePath = Outputs.relativePathFor(absolutePath),
+ type = ProfilerOutput.Type.MethodTrace,
+ source = null
+ )
+
fun of(
label: String,
type: ProfilerOutput.Type,
diff --git a/benchmark/benchmark-macro/api/current.txt b/benchmark/benchmark-macro/api/current.txt
index 7116325..3b69bad3 100644
--- a/benchmark/benchmark-macro/api/current.txt
+++ b/benchmark/benchmark-macro/api/current.txt
@@ -153,6 +153,8 @@
method public static androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
method public static androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
method public static androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+ method public static boolean deviceBatteryHasMinimumCharge();
+ method public static boolean deviceSupportsHighPrecisionTracking();
field public static final androidx.benchmark.macro.PowerMetric.Companion Companion;
}
@@ -160,6 +162,8 @@
method public androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
method public androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
method public androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+ method public boolean deviceBatteryHasMinimumCharge();
+ method public boolean deviceSupportsHighPrecisionTracking();
}
public abstract static sealed class PowerMetric.Type {
diff --git a/benchmark/benchmark-macro/api/restricted_current.txt b/benchmark/benchmark-macro/api/restricted_current.txt
index 820522c..0c32513 100644
--- a/benchmark/benchmark-macro/api/restricted_current.txt
+++ b/benchmark/benchmark-macro/api/restricted_current.txt
@@ -166,6 +166,8 @@
method public static androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
method public static androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
method public static androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+ method public static boolean deviceBatteryHasMinimumCharge();
+ method public static boolean deviceSupportsHighPrecisionTracking();
field public static final androidx.benchmark.macro.PowerMetric.Companion Companion;
}
@@ -173,6 +175,8 @@
method public androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
method public androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
method public androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+ method public boolean deviceBatteryHasMinimumCharge();
+ method public boolean deviceSupportsHighPrecisionTracking();
}
public abstract static sealed class PowerMetric.Type {
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 2237b10..7d08162 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
@@ -21,7 +21,6 @@
import android.content.pm.PackageManager
import android.os.Build
import androidx.benchmark.DeviceInfo
-import androidx.benchmark.Outputs
import androidx.benchmark.Shell
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
@@ -225,34 +224,45 @@
}
@Test
- fun startActivityAndWait_methodTracing() {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val device = UiDevice.getInstance(instrumentation)
- val files = Outputs.outputDirectory.walk().filter {
- it.isFile
- }.toSet()
+ fun measureBlock_methodTracing() {
val scope = MacrobenchmarkScope(
Packages.TEST, // self-instrumenting macrobench, so don't kill the process!
launchWithClearTask = true,
)
- // Turn on method tracing
- scope.launchWithMethodTracing = true
- // Force Method Tracing
- scope.methodTracingForTests = true
+ scope.fileLabel = "TEST-UNIQUE-NAME"
+ scope.startMethodTracing()
// Launch first activity, and validate it is displayed
scope.startActivityAndWait(ConfigurableActivity.createIntent("InitialText"))
- assertTrue(device.hasObject(By.text("InitialText")))
- scope.stopMethodTracing("TEST-UNIQUE-NAME")
- val outputs = Outputs.outputDirectory.walk().filter {
- it.isFile
- }.toSet()
- val testOutputs = outputs - files
+ assertTrue(scope.device.hasObject(By.text("InitialText")))
+ val testOutputs = scope.stopMethodTracing()
val trace = testOutputs.singleOrNull { file ->
- file.name.endsWith(".trace") && file.name.contains("-methodTracing-")
+ file.outputRelativePath.endsWith(".trace") &&
+ file.outputRelativePath.contains("-methodTracing-")
}
// One method trace should have been created
assertNotNull(trace)
- assertTrue(trace.name.startsWith("TEST-UNIQUE-NAME-methodTracing-"))
+ assertTrue(trace.outputRelativePath.startsWith("TEST-UNIQUE-NAME-methodTracing-"))
+ }
+
+ @Test
+ fun multipleMethodTraces_onProcessStartStop() {
+ val scope = MacrobenchmarkScope(
+ Packages.TARGET,
+ launchWithClearTask = true
+ )
+ scope.fileLabel = "TEST-UNIQUE-NAME"
+ scope.startMethodTracing()
+ scope.startActivityAndWait()
+ scope.killProcess()
+ scope.startActivityAndWait()
+ scope.killProcess()
+ val testOutputs = scope.stopMethodTracing()
+ // We should have 2 method traces
+ val traces = testOutputs.filter { file ->
+ file.outputRelativePath.endsWith(".trace") &&
+ file.outputRelativePath.contains("-methodTracing-")
+ }
+ assertEquals(traces.size, 2)
}
private fun validateLaunchAndFrameStats(pressHome: Boolean) {
@@ -318,6 +328,7 @@
Packages.TARGET,
launchWithClearTask = false
)
+ scope.fileLabel = "TEST-UNIQUE-NAME"
// reset to empty to begin with
scope.killProcess()
scope.dropShaderCacheBlock()
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
index 50b1662..01c4831 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
@@ -17,8 +17,10 @@
package androidx.benchmark.macro
import android.annotation.SuppressLint
+import android.content.Intent
import androidx.annotation.RequiresApi
import androidx.benchmark.DeviceInfo
+import androidx.benchmark.json.BenchmarkData
import androidx.benchmark.perfetto.PerfettoConfig
import androidx.benchmark.perfetto.PerfettoHelper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -85,6 +87,38 @@
assertTrue(exception.message!!.contains("Require iterations > 0"))
}
+ @Test
+ fun macrobenchmarkWithStartupMode_noMethodTrace() {
+ val result = macrobenchmarkWithStartupMode(
+ uniqueName = "uniqueName", // ignored, uniqueness not important
+ className = "className",
+ testName = "testName",
+ packageName = Packages.TARGET,
+ metrics = listOf(StartupTimingMetric()),
+ compilationMode = CompilationMode.Ignore(),
+ iterations = 1,
+ startupMode = StartupMode.COLD,
+ perfettoConfig = null,
+ setupBlock = {},
+ measureBlock = {
+ startActivityAndWait(
+ Intent(
+ "androidx.benchmark.integration.macrobenchmark.target" +
+ ".TRIVIAL_STARTUP_ACTIVITY"
+ )
+ )
+ }
+ )
+ assertEquals(
+ 1,
+ result.profilerOutputs!!.size
+ )
+ assertEquals(
+ result.profilerOutputs!!.single().type,
+ BenchmarkData.TestResult.ProfilerOutput.Type.PerfettoTrace
+ )
+ }
+
enum class Block { Setup, Measure }
@RequiresApi(29)
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/Packages.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/Packages.kt
index 65ada92..e15e059 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/Packages.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/Packages.kt
@@ -30,4 +30,11 @@
* Preferably use this app/package if not killing/compiling target.
*/
const val TEST = "androidx.benchmark.macro.test"
+
+ /**
+ * Package not present on device.
+ *
+ * Used to validate behavior when package can't be found.
+ */
+ const val MISSING = "not.real.fake.package"
}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerMetricTest.kt
index b47435e..37d20d1 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerMetricTest.kt
@@ -16,15 +16,15 @@
package androidx.benchmark.macro
-import android.os.Build
-import androidx.annotation.RequiresApi
import androidx.benchmark.macro.Metric.Measurement
import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
import androidx.benchmark.perfetto.PerfettoTraceProcessor
+import androidx.test.filters.SdkSuppress
import kotlin.test.assertEquals
import org.junit.Assume.assumeTrue
import org.junit.Test
+@SdkSuppress(minSdkVersion = 29)
@OptIn(ExperimentalMetricApi::class)
class PowerMetricTest {
private val captureInfo = Metric.CaptureInfo(
@@ -34,7 +34,6 @@
StartupMode.COLD
)
- @RequiresApi(Build.VERSION_CODES.Q)
@Test
fun successfulFixedTraceEnergyBreakdown() {
assumeTrue(isAbiSupported())
@@ -72,7 +71,6 @@
)
}
- @RequiresApi(Build.VERSION_CODES.Q)
@Test
fun successfulFixedTracePowerTotal() {
assumeTrue(isAbiSupported())
@@ -101,7 +99,6 @@
)
}
- @RequiresApi(Build.VERSION_CODES.Q)
@Test
fun successfulFixedTracePowerMix() {
assumeTrue(isAbiSupported())
@@ -134,7 +131,6 @@
)
}
- @RequiresApi(Build.VERSION_CODES.Q)
@Test
fun emptyFixedTrace() {
assumeTrue(isAbiSupported())
@@ -150,7 +146,7 @@
assertEquals(emptyList(), actualMetrics)
}
- @RequiresApi(Build.VERSION_CODES.Q)
+ @SdkSuppress(minSdkVersion = 29)
@Test
fun successfulFixedTraceBatteryDischarge() {
assumeTrue(isAbiSupported())
@@ -171,4 +167,20 @@
threshold = 0.1
)
}
+
+ @Test
+ fun deviceSupportsPowerEnergy() {
+ assertEquals(
+ PowerRail.hasMetrics(throwOnMissingMetrics = false),
+ PowerMetric.deviceSupportsHighPrecisionTracking()
+ )
+ }
+
+ @Test
+ fun deviceBatteryHasMinimumCharge() {
+ assertEquals(
+ BatteryCharge.hasMinimumCharge(throwOnMissingMetrics = false),
+ PowerMetric.deviceBatteryHasMinimumCharge()
+ )
+ }
}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerRailTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerRailTest.kt
index 0d729e3..6cce497 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerRailTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerRailTest.kt
@@ -18,6 +18,7 @@
import android.os.Build
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
@@ -30,12 +31,14 @@
@SmallTest
class PowerRailTest {
+ @SdkSuppress(minSdkVersion = 32)
@Test
fun hasMetrics_Pixel6() {
- assumeTrue(Build.VERSION.SDK_INT > 31 && Build.MODEL.lowercase() == "oriole")
+ assumeTrue(Build.MODEL.lowercase() == "oriole")
assertTrue(PowerRail.hasMetrics(throwOnMissingMetrics = true))
assertTrue(PowerRail.hasMetrics(throwOnMissingMetrics = false))
+ assertTrue(PowerMetric.deviceSupportsHighPrecisionTracking())
}
@Test
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/ProfileInstallBroadcastTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/ProfileInstallBroadcastTest.kt
index 055ccfd..b299dde 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/ProfileInstallBroadcastTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/ProfileInstallBroadcastTest.kt
@@ -17,43 +17,82 @@
package androidx.benchmark.macro
import android.os.Build
-import androidx.benchmark.junit4.PerfettoTraceRule
-import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
+import kotlin.test.assertContains
import kotlin.test.assertNull
-import org.junit.Rule
+import org.junit.Assert.assertNotNull
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class ProfileInstallBroadcastTest {
- @OptIn(ExperimentalPerfettoCaptureApi::class)
- @get:Rule
- val perfettoTraceRule = PerfettoTraceRule()
-
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
@Test
fun installProfile() {
assertNull(ProfileInstallBroadcast.installProfile(Packages.TARGET))
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
+ @Test
+ fun installProfile_missing() {
+ val errorString = ProfileInstallBroadcast.installProfile(Packages.MISSING)
+ assertNotNull(errorString)
+ assertContains(
+ errorString!!,
+ "The baseline profile install broadcast was not received"
+ )
+ }
+
@Test
fun skipFileOperation() {
assertNull(ProfileInstallBroadcast.skipFileOperation(Packages.TARGET, "WRITE_SKIP_FILE"))
assertNull(ProfileInstallBroadcast.skipFileOperation(Packages.TARGET, "DELETE_SKIP_FILE"))
}
+ @Test
+ fun skipFileOperation_missing() {
+ ProfileInstallBroadcast.skipFileOperation(Packages.MISSING, "WRITE_SKIP_FILE").apply {
+ assertNotNull(this)
+ assertContains(this!!, "The baseline profile skip file broadcast was not received")
+ }
+ ProfileInstallBroadcast.skipFileOperation(Packages.MISSING, "DELETE_SKIP_FILE").apply {
+ assertNotNull(this)
+ assertContains(this!!, "The baseline profile skip file broadcast was not received")
+ }
+ }
+
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
@Test
fun saveProfile() {
assertNull(ProfileInstallBroadcast.saveProfile(Packages.TARGET))
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
+ @Test
+ fun saveProfile_missing() {
+ val errorString = ProfileInstallBroadcast.saveProfile(Packages.MISSING)
+ assertNotNull(errorString)
+ assertContains(errorString!!, "The save profile broadcast event was not received")
+ }
+
@Test
fun dropShaderCache() {
assertNull(ProfileInstallBroadcast.dropShaderCache(Packages.TARGET))
}
+
+ @Test
+ fun dropShaderCache_missing() {
+ val errorString = ProfileInstallBroadcast.dropShaderCache(Packages.MISSING)
+ assertNotNull(errorString)
+ assertContains(errorString!!, "The DROP_SHADER_CACHE broadcast was not received")
+
+ // validate extra instructions
+ assertContains(
+ errorString,
+ "verify: 1) androidx.profileinstaller.ProfileInstallReceiver appears unobfuscated"
+ )
+ }
}
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 92eecb5..a16bfd5 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
@@ -221,7 +221,7 @@
val startTime = System.nanoTime()
// Ensure method tracing is explicitly enabled and that we are not running in dry run mode.
- val launchWithMethodTracing = Arguments.macrobenchMethodTracingEnabled()
+ val requestMethodTracing = Arguments.macrobenchMethodTracingEnabled()
val applicationInfo = getInstalledPackageInfo(packageName)
val scope = MacrobenchmarkScope(
packageName,
@@ -229,7 +229,6 @@
)
// Capture if the app being benchmarked is a system app.
scope.isSystemApp = applicationInfo.isSystemApp()
- scope.launchWithMethodTracing = launchWithMethodTracing
// Ensure the device is awake
scope.device.wakeUp()
@@ -276,9 +275,9 @@
}
val iterString = iteration.toString().padStart(3, '0')
- val fileLabel = "${uniqueName}_iter$iterString"
+ scope.fileLabel = "${uniqueName}_iter$iterString"
val tracePath = perfettoCollector.record(
- fileLabel = fileLabel,
+ fileLabel = scope.fileLabel,
config = perfettoConfig ?: PerfettoConfig.Benchmark(
/**
* Prior to API 24, every package name was joined into a single setprop
@@ -306,6 +305,9 @@
it.start()
}
}
+ if (requestMethodTracing) {
+ scope.startMethodTracing()
+ }
trace("measureBlock") {
measureBlock(scope)
}
@@ -314,14 +316,8 @@
metrics.forEach {
it.stop()
}
- if (launchWithMethodTracing && scope.isMethodTracing) {
- val (label, tracePath) = scope.stopMethodTracing(fileLabel)
- val resultFile = Profiler.ResultFile.ofPerfettoTrace(
- label = label,
- absolutePath = tracePath
- )
- methodTracingResultFiles += resultFile
- scope.isMethodTracing = false
+ if (requestMethodTracing) {
+ methodTracingResultFiles += scope.stopMethodTracing()
}
}
}
@@ -371,13 +367,8 @@
in the measure block?
""".trimIndent()
}
- InstrumentationResults.instrumentationReport {
- if (launchWithMethodTracing && methodTracingResultFiles.size < iterations) {
- warningMessage += "\nNOTE: Method traces cannot be captured during iterations" +
- " that start while the target process is already running (including HOT/WARM" +
- " launches)."
- }
+ InstrumentationResults.instrumentationReport {
reportSummaryToIde(
warningMessage = warningMessage,
testName = uniqueName,
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt
index a9ba1a8..18e960c 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MacrobenchmarkScope.kt
@@ -22,11 +22,10 @@
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
-import androidx.annotation.VisibleForTesting
import androidx.benchmark.Arguments
import androidx.benchmark.DeviceInfo
import androidx.benchmark.Outputs
-import androidx.benchmark.Outputs.dateToFileName
+import androidx.benchmark.Profiler
import androidx.benchmark.Shell
import androidx.benchmark.macro.MacrobenchmarkScope.Companion.Api24ContextHelper.createDeviceProtectedStorageContextCompat
import androidx.benchmark.macro.perfetto.forceTrace
@@ -53,8 +52,14 @@
private val launchWithClearTask: Boolean
) {
- private val instrumentation = InstrumentationRegistry.getInstrumentation()
- private val context = instrumentation.context
+ internal val instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ internal val context = instrumentation.context
+
+ /**
+ * The per-iteration file label used as a prefix when storing Macrobenchmark results.
+ */
+ internal lateinit var fileLabel: String
/**
* Controls if the process will be launched with method tracing turned on.
@@ -62,19 +67,12 @@
* Default to false, because we only want to turn on method tracing when explicitly enabled
* via `Arguments.methodTracingOptions`.
*/
- internal var launchWithMethodTracing: Boolean = false
+ private var isMethodTracingActive: Boolean = false
/**
- * Only use this for testing. This forces `--start-profiler` without the check for process
- * live ness.
+ * This is `true` iff method tracing is currently active for this benchmarking session.
*/
- @VisibleForTesting
- internal var methodTracingForTests: Boolean = false
-
- /**
- * This is `true` iff method tracing is currently active.
- */
- internal var isMethodTracing: Boolean = false
+ private var isMethodTracingSessionActive: Boolean = false
/**
* When `true`, the app will be forced to flush its ART profiles
@@ -101,6 +99,13 @@
internal set
/**
+ * The list of method traces accumulated during a benchmarking session. The [Pair] has the
+ * label and the absolute path to the trace. These should be reported at the end of a
+ * Macro benchmarking session, if method tracing was on.
+ */
+ private val methodTraces: MutableList<Pair<String, String>> = mutableListOf()
+
+ /**
* Get the [UiDevice] instance, to use in reading target app UI state, or interacting with the
* UI via touches, scrolls, or other inputs.
*
@@ -152,34 +157,47 @@
startActivityImpl(intent.toUri(Intent.URI_INTENT_SCHEME))
}
- @SuppressLint("BanThreadSleep") // Cannot always detect activity launches.
private fun startActivityImpl(uri: String) {
+ if (
+ isMethodTracingActive &&
+ !isMethodTracingSessionActive &&
+ !Shell.isPackageAlive(packageName)
+ ) {
+ isMethodTracingSessionActive = true
+ // Use the canonical trace path for the given package name.
+ val tracePath = methodTraceRecordPath(packageName)
+ val profileArgs = "--start-profiler \"$tracePath\" --streaming"
+ amStartAndWait(uri, profileArgs)
+ } else {
+ amStartAndWait(uri)
+ }
+ }
+
+ @SuppressLint("BanThreadSleep") // Cannot always detect activity launches.
+ private fun amStartAndWait(uri: String, profilingArgs: String? = null) {
val ignoredUniqueNames = if (!launchWithClearTask) {
emptyList()
} else {
// ignore existing names, as we expect a new window
getFrameStats().map { it.uniqueName }
}
+
val preLaunchTimestampNs = System.nanoTime()
- // Only use --start-profiler is the package is not alive. Otherwise re-use the existing
- // profiling session.
- val profileArgs =
- if (launchWithMethodTracing && (methodTracingForTests || !Shell.isPackageAlive(
- packageName
- ))
- ) {
- isMethodTracing = true
- val tracePath = methodTraceRecordPath(packageName)
- "--start-profiler \"$tracePath\" --streaming"
- } else {
- ""
- }
- val cmd = "am start $profileArgs -W \"$uri\""
+
+ val additionalArgs = profilingArgs ?: ""
+ val cmd = "am start $additionalArgs -W \"$uri\""
Log.d(TAG, "Starting activity with command: $cmd")
// executeShellScript used to access stderr, and avoid need to escape special chars like `;`
val result = Shell.executeScriptCaptureStdoutStderr(cmd)
+ if (result.stderr.contains("java.lang.SecurityException")) {
+ throw SecurityException(result.stderr)
+ }
+ if (result.stderr.isNotEmpty()) {
+ throw IllegalStateException(result.stderr)
+ }
+
val outputLines = result.stdout
.split("\n")
.map { it.trim() }
@@ -191,13 +209,6 @@
}
}
- if (result.stderr.contains("java.lang.SecurityException")) {
- throw SecurityException(result.stderr)
- }
- if (result.stderr.isNotEmpty()) {
- throw IllegalStateException(result.stderr)
- }
-
Log.d(TAG, "Result: ${result.stdout}")
if (outputLines.any { it.startsWith("Warning: Activity not started") }) {
@@ -286,6 +297,8 @@
* Force-stop the process being measured.
*/
public fun killProcess() {
+ // Method traces are only flushed is a method tracing session is active.
+ flushMethodTraces()
if (flushArtProfiles && Build.VERSION.SDK_INT >= 24) {
// Flushing ART profiles will also kill the process at the end.
killProcessAndFlushArtProfiles()
@@ -341,50 +354,6 @@
}
/**
- * Stops method tracing for the given [packageName] and copies the output to the
- * `additionalTestOutputDir`.
- *
- * @return a [Pair] representing the label, and the absolute path of the method trace.
- */
- internal fun stopMethodTracing(uniqueLabel: String): Pair<String, String> {
- val tracePath = methodTraceRecordPath(packageName)
-
- // We have to poll here as `am profile stop` is async, but it's hard to calibrate these
- // numbers, as different devices take drastically different amounts of time.
- // E.g. pixel 8 takes 100ms for it's full flush, while mokey takes 1700ms to start, then a
- // few hundred ms to complete.
- //
- // Ideally, we'd use the native approach that Studio profilers use (on P+):
- // https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:transport/native/utils/activity_manager.cc;l=111;drc=a4c97db784418341c9f1be60b98ba22301b5ced8
- Shell.waitForFileFlush(
- tracePath,
- maxInitialFlushWaitIterations = 50, // up to 2.5 sec of waiting on flush to start
- maxStableFlushWaitIterations = 50, // up to 2.5 sec of waiting on flush to complete
- stableIterations = 8, // 400ms of stability after flush starts
- pollDurationMs = 50L
- ) {
- Shell.executeScriptSilent("am profile stop $packageName")
- }
- // unique label so source is clear, dateToFileName so each run of test is unique on host
- val outputFileName = "$uniqueLabel-methodTracing-${dateToFileName()}.trace"
- val stagingFile = File.createTempFile("methodTrace", null, Outputs.dirUsableByAppAndShell)
- // Staging location before we write it again using Outputs.writeFile(...)
- // NOTE: staging copy may be unnecessary if we just use a single `cp`
- Shell.executeScriptSilent("cp '$tracePath' '$stagingFile'")
-
- // Report file
- val outputPath = Outputs.writeFile(outputFileName) {
- Log.d(TAG, "Writing method traces to ${it.absolutePath}")
- stagingFile.copyTo(it, overwrite = true)
-
- // Cleanup
- stagingFile.delete()
- Shell.executeScriptSilent("rm \"$tracePath\"")
- }
- return "MethodTrace iteration ${this.iteration ?: 0}" to outputPath
- }
-
- /**
* Drop caches via setprop added in API 31
*
* Feature for dropping caches without root added in 31: https://r.android.com/1584525
@@ -514,6 +483,99 @@
}
}
+ /**
+ * Starts method tracing for the given [packageName].
+ */
+ internal fun startMethodTracing() {
+ require(!isMethodTracingActive && !isMethodTracingSessionActive) {
+ "Method tracing should not already be active."
+ }
+ isMethodTracingActive = true
+ // If the process is running, start a profiling session by connecting to the process.
+ // Otherwise, given isMethodTracingActive = true, startActivityAndWait(...) will ensure
+ // that a subsequent process launch happens with tracing turned on.
+ if (Shell.isPackageAlive(packageName)) {
+ isMethodTracingSessionActive = true
+ // Use the canonical trace path for the given package name.
+ val tracePath = methodTraceRecordPath(packageName)
+ // Clock Type is only available starting Android V
+ // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/app/ProfilerInfo.java;l=115;drc=c58be09d9273485c54d6a16defc42d9f26182b73
+ val clockType = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ "--clock-type wall"
+ } else {
+ ""
+ }
+ val arguments = "--streaming $clockType $packageName \"$tracePath\""
+ Shell.executeScriptSilent("am profile start $arguments")
+ }
+ }
+
+ /**
+ * Stops a method tracing session for the provided [packageName]. This returns a list of traces
+ * accumulated in an active Method tracing session.
+ */
+ internal fun stopMethodTracing(): List<Profiler.ResultFile> {
+ require(isMethodTracingActive) {
+ "startMethodTracing() must be called prior to a call to stopMethodTracing()."
+ }
+ // Only flushes method traces when a trace session is active.
+ flushMethodTraces()
+ isMethodTracingActive = false
+ val results = methodTraces.map {
+ Profiler.ResultFile.ofMethodTrace(it.first, it.second)
+ }
+ methodTraces.clear()
+ return results
+ }
+
+ /**
+ * Stops the current method tracing session and copies the output to the
+ * `additionalTestOutputDir` if a session was active.
+ */
+ private fun flushMethodTraces() {
+ if (isMethodTracingSessionActive) {
+ isMethodTracingSessionActive = false
+ val tracePath = methodTraceRecordPath(packageName)
+
+ // We have to poll here as `am profile stop` is async, but it's hard to calibrate these
+ // numbers, as different devices take drastically different amounts of time.
+ // E.g. pixel 8 takes 100ms for it's full flush, while mokey takes 1700ms to start,
+ // then a few hundred ms to complete.
+ //
+ // Ideally, we'd use the native approach that Studio profilers use (on P+):
+ // https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:transport/native/utils/activity_manager.cc;l=111;drc=a4c97db784418341c9f1be60b98ba22301b5ced8
+ Shell.waitForFileFlush(
+ tracePath,
+ maxInitialFlushWaitIterations = 50, // up to 2.5 sec of waiting on flush to start
+ maxStableFlushWaitIterations = 50, // up to 2.5 sec of waiting on flush to complete
+ stableIterations = 8, // 400ms of stability after flush starts
+ pollDurationMs = 50L
+ ) {
+ Shell.executeScriptSilent("am profile stop $packageName")
+ }
+ // unique label so source is clear, dateToFileName so each run of test is unique on host
+ val outputFileName = "$fileLabel-methodTracing-${Outputs.dateToFileName()}.trace"
+ val stagingFile =
+ File.createTempFile("methodTrace", null, Outputs.dirUsableByAppAndShell)
+ // Staging location before we write it again using Outputs.writeFile(...)
+ // NOTE: staging copy may be unnecessary if we just use a single `cp`
+ Shell.executeScriptSilent("cp '$tracePath' '$stagingFile'")
+
+ // Report file
+ val outputPath = Outputs.writeFile(outputFileName) {
+ Log.d(TAG, "Writing method traces to ${it.absolutePath}")
+ stagingFile.copyTo(it, overwrite = true)
+
+ // Cleanup
+ stagingFile.delete()
+ Shell.executeScriptSilent("rm \"$tracePath\"")
+ }
+ val traceLabel = "MethodTrace iteration ${iteration ?: 0}"
+ // Keep track of the label and the corresponding output paths.
+ methodTraces += traceLabel to outputPath
+ }
+ }
+
internal companion object {
fun getShaderCachePath(packageName: String): String {
val context = InstrumentationRegistry.getInstrumentation().context
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
index 6437ad16..3076087 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -549,6 +549,12 @@
* does not appear in the trace.
*/
object Average : Mode("Average")
+
+ /**
+ * Internal class to prevent external exhaustive when statements, which would break as we
+ * add more to this sealed class.
+ */
+ internal object WhenPrevention : Mode("N/A")
}
override fun configure(packageName: String) {
@@ -630,6 +636,7 @@
)
)
}
+ Mode.WhenPrevention -> throw IllegalStateException("WhenPrevention should be unused")
}
}
}
@@ -647,7 +654,9 @@
*
* For [Type.Energy] or [Type.Power], the sum of all categories will be displayed as a `Total`
* metric. The sum of all unrequested categories will be displayed as an `Unselected` metric. The
- * subsystems that have not been categorized will be displayed as an `Uncategorized` metric.
+ * subsystems that have not been categorized will be displayed as an `Uncategorized` metric. You can
+ * check if the local device supports this high precision tracking with
+ * [deviceSupportsHighPrecisionTracking].
*
* For [Type.Battery], the charge for the start of the run and the end of the run will be displayed.
* An additional `Diff` metric will be displayed to indicate the charge drain over the course of
@@ -712,6 +721,49 @@
): Type.Power {
return Type.Power(categories)
}
+
+ /**
+ * Returns true if the current device can be used for high precision [Power] and [Energy]
+ * metrics.
+ *
+ * This can be used to change behavior or fall back to lower precision tracking:
+ *
+ * ```
+ * metrics = listOf(
+ * if (PowerMetric.deviceSupportsHighPrecisionTracking()) {
+ * PowerMetric(Type.Energy()) // high precision tracking
+ * } else {
+ * PowerMetric(Type.Battery()) // fall back to less precise tracking
+ * }
+ * )
+ * ```
+ *
+ * Or to skip a test when detailed tracking isn't available:
+ * ```
+ * @Test fun myDetailedPowerBenchmark {
+ * assumeTrue(PowerMetric.deviceSupportsHighPrecisionTracking())
+ * macrobenchmarkRule.measureRepeated (
+ * metrics = listOf(PowerMetric(Type.Energy(...)))
+ * ) {
+ * ...
+ * }
+ * }
+ * ```
+ */
+ @JvmStatic
+ fun deviceSupportsHighPrecisionTracking(): Boolean =
+ hasMetrics(throwOnMissingMetrics = false)
+
+ /**
+ * Returns true if [Type.Battery] measurements can be performed, based on current device
+ * charge.
+ *
+ * This can be used to change behavior or throw a clear error before metric configuration,
+ * or to skip the test, e.g. with `assumeTrue(PowerMetric.deviceBatteryHasMinimumCharge())`
+ */
+ @JvmStatic
+ fun deviceBatteryHasMinimumCharge(): Boolean =
+ hasMinimumCharge(throwOnMissingMetrics = false)
}
/**
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/PowerRail.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/PowerRail.kt
index 1013b84..ecaf2ad 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/PowerRail.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/PowerRail.kt
@@ -65,7 +65,7 @@
adb shell $DUMPSYS_POWERSTATS
- To check at runtime for this, use PowerRail.hasMetrics()
+ To check at runtime for this, use PowerMetric.deviceSupportsPowerEnergy()
""".trimIndent()
)
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/ProfileInstallBroadcast.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/ProfileInstallBroadcast.kt
index bd622f0..e51872e 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/ProfileInstallBroadcast.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/ProfileInstallBroadcast.kt
@@ -187,10 +187,8 @@
// Use an explicit broadcast given the app was force-stopped.
val action = "androidx.profileinstaller.action.BENCHMARK_OPERATION"
val operationKey = "EXTRA_BENCHMARK_OPERATION"
- val result = Shell.amBroadcast(
- "-a $action -e $operationKey $operation $packageName/$receiverName"
- )
- return when (result) {
+ val broadcastArguments = "-a $action -e $operationKey $operation $packageName/$receiverName"
+ return when (val result = Shell.amBroadcast(broadcastArguments)) {
null, 0, 16 /* BENCHMARK_OPERATION_UNKNOWN */ -> {
// 0 is returned by the platform by default, and also if no broadcast receiver
// receives the broadcast.
@@ -201,7 +199,12 @@
"This most likely means that the `androidx.profileinstaller` library " +
"used by the target apk is old. Please use `1.3.0-alpha02` or newer. " +
"For more information refer to the release notes at " +
- "https://developer.android.com/jetpack/androidx/releases/profileinstaller."
+ "https://developer.android.com/jetpack/androidx/releases/profileinstaller. " +
+ "If you are already using androidx.profileinstaller library and still seeing " +
+ "error, verify: 1) androidx.profileinstaller.ProfileInstallReceiver appears " +
+ "unobfuscated in your APK's AndroidManifest and dex, and 2) the following " +
+ "command executes successfully (should print 14): " +
+ "adb shell am broadcast $broadcastArguments"
}
15 -> { // RESULT_BENCHMARK_OPERATION_FAILURE
"The $operation broadcast failed."
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
index 6960117..e89c5d6 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
@@ -26,6 +26,7 @@
import androidx.benchmark.ShellScript
import androidx.benchmark.inMemoryTrace
import androidx.benchmark.perfetto.PerfettoTraceProcessor
+import java.io.FileNotFoundException
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
@@ -189,6 +190,12 @@
val statusResult = status()
return@inMemoryTrace statusResult.api_version != null && statusResult.api_version > 0
} catch (e: ConnectException) {
+ // Note that this is fired when the server port is not bound yet.
+ // This can happen before the perfetto trace processor server is fully started.
+ false
+ } catch (e: FileNotFoundException) {
+ // Note that this is fired when the endpoint queried does not exist.
+ // This can happen before the perfetto trace processor server is fully started.
false
}
}
diff --git a/benchmark/integration-tests/baselineprofile-consumer/build.gradle b/benchmark/integration-tests/baselineprofile-consumer/build.gradle
index c98970f..5d6d8d8 100644
--- a/benchmark/integration-tests/baselineprofile-consumer/build.gradle
+++ b/benchmark/integration-tests/baselineprofile-consumer/build.gradle
@@ -28,17 +28,27 @@
id("com.android.application")
id("kotlin-android")
id("androidx.baselineprofile")
- id("kotlin-kapt")
+ id("com.google.devtools.ksp")
id("com.google.dagger.hilt.android")
}
android {
+ sourceSets {
+ // This is required because of release variant specific code used by hilt.
+ releaseLibrariesOnly {
+ java.srcDirs += "src/release/"
+ }
+ }
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt")
}
+ releaseLibrariesOnly {
+ initWith(release)
+ matchingFallbacks += "release"
+ }
}
namespace "androidx.benchmark.integration.baselineprofile.consumer"
}
@@ -47,9 +57,8 @@
implementation(libs.kotlinStdlib)
implementation(libs.constraintLayout)
implementation(libs.hiltAndroid)
- kapt(libs.hiltCompiler)
+ ksp(libs.hiltCompiler)
implementation(project(":profileinstaller:profileinstaller"))
- baselineProfile(project(":benchmark:integration-tests:baselineprofile-producer"))
}
baselineProfile {
@@ -58,6 +67,15 @@
// trigger baseline profile generation and integration tests on device.
saveInSrc = true
automaticGenerationDuringBuild = false
+
+ variants {
+ release {
+ from(project(":benchmark:integration-tests:baselineprofile-producer"))
+ }
+ releaseLibrariesOnly {
+ // No dependency set
+ }
+ }
}
apply(from: "../baselineprofile-test-utils/utils.gradle")
diff --git a/benchmark/integration-tests/baselineprofile-producer/build.gradle b/benchmark/integration-tests/baselineprofile-producer/build.gradle
index 0f1243c..3a320b1 100644
--- a/benchmark/integration-tests/baselineprofile-producer/build.gradle
+++ b/benchmark/integration-tests/baselineprofile-producer/build.gradle
@@ -32,7 +32,7 @@
android {
defaultConfig {
- minSdkVersion 23
+ minSdkVersion 24
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
testOptions.managedDevices.devices {
@@ -42,6 +42,10 @@
systemImageSource = "aosp"
}
}
+ buildTypes {
+ release { }
+ releaseLibrariesOnly { }
+ }
targetProjectPath = ":benchmark:integration-tests:baselineprofile-consumer"
namespace "androidx.benchmark.integration.baselineprofile.producer"
}
diff --git a/benchmark/integration-tests/baselineprofile-producer/src/main/java/androidx/benchmark/integration/baselineprofile/producer/StartupBenchmarks.kt b/benchmark/integration-tests/baselineprofile-producer/src/main/java/androidx/benchmark/integration/baselineprofile/producer/StartupBenchmarks.kt
new file mode 100644
index 0000000..a1e44eb
--- /dev/null
+++ b/benchmark/integration-tests/baselineprofile-producer/src/main/java/androidx/benchmark/integration/baselineprofile/producer/StartupBenchmarks.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark.integration.baselineprofile.producer
+
+import android.content.Intent
+import androidx.benchmark.macro.BaselineProfileMode
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.StartupTimingMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class StartupBenchmarks {
+
+ @get:Rule
+ val rule = MacrobenchmarkRule()
+
+ @Test
+ fun startupCompilationBaselineProfiles() =
+ benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
+
+ private fun benchmark(compilationMode: CompilationMode) = rule.measureRepeated(
+ packageName = PACKAGE_NAME,
+ metrics = listOf(StartupTimingMetric()),
+ compilationMode = compilationMode,
+ startupMode = StartupMode.COLD,
+ iterations = 10,
+ setupBlock = { pressHome() },
+ measureBlock = { startActivityAndWait(Intent(ACTION)) }
+ )
+
+ companion object {
+ private const val PACKAGE_NAME =
+ "androidx.benchmark.integration.baselineprofile.consumer"
+ private const val ACTION =
+ "androidx.benchmark.integration.baselineprofile.consumer.EMPTY_ACTIVITY"
+ }
+}
diff --git a/binarycompatibilityvalidator/OWNERS b/binarycompatibilityvalidator/OWNERS
new file mode 100644
index 0000000..a43f7c1
--- /dev/null
+++ b/binarycompatibilityvalidator/OWNERS
@@ -0,0 +1,3 @@
[email protected]
[email protected]
[email protected]
\ No newline at end of file
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/build.gradle b/binarycompatibilityvalidator/binarycompatibilityvalidator/build.gradle
new file mode 100644
index 0000000..aa18e365
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+
+import androidx.build.LibraryType
+
+
+plugins {
+ id("AndroidXPlugin")
+ id("kotlin")
+}
+
+dependencies {
+ api(libs.kotlinStdlib)
+ implementation(libs.kotlinCompiler)
+ testImplementation(libs.truth)
+ testImplementation(libs.junit)
+}
+
+androidx {
+ name = "AndroidX Binary Compatibility Validator"
+ type = LibraryType.INTERNAL_HOST_TEST_LIBRARY
+ inceptionYear = "2024"
+ description = "Enforce binary compatibility for klibs"
+}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/AbiExtensions.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/AbiExtensions.kt
new file mode 100644
index 0000000..871247d
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/AbiExtensions.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalLibraryAbiReader::class)
+
+package androidx.binarycompatibilityvalidator
+
+import org.jetbrains.kotlin.library.abi.AbiClassifierReference
+import org.jetbrains.kotlin.library.abi.AbiQualifiedName
+import org.jetbrains.kotlin.library.abi.AbiType
+import org.jetbrains.kotlin.library.abi.AbiTypeArgument
+import org.jetbrains.kotlin.library.abi.AbiTypeNullability
+import org.jetbrains.kotlin.library.abi.AbiVariance
+import org.jetbrains.kotlin.library.abi.ExperimentalLibraryAbiReader
+
+// Convenience extensions for accessing properties that may exist without have to cast repeatedly
+// For sources with documentation see https://github.com/JetBrains/kotlin/blob/master/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbi.kt
+
+/** A classifier reference is either a simple class or a type reference **/
+internal val AbiType.classifierReference: AbiClassifierReference?
+ get() = (this as? AbiType.Simple)?.classifierReference
+/** The class name from a regular type e.g. 'Array' **/
+internal val AbiType.className: AbiQualifiedName?
+ get() = classifierReference?.className
+/** A tag from a type type parameter reference e.g. 'T' **/
+internal val AbiType.tag: String?
+ get() = classifierReference?.tag
+/** The string representation of a type, whether it is a simple type or a type reference **/
+internal val AbiType.classNameOrTag: String?
+ get() = className?.toString() ?: tag
+internal val AbiType.nullability: AbiTypeNullability?
+ get() = (this as? AbiType.Simple)?.nullability
+internal val AbiType.arguments: List<AbiTypeArgument>?
+ get() = (this as? AbiType.Simple)?.arguments
+internal val AbiTypeArgument.type: AbiType?
+ get() = (this as? AbiTypeArgument.TypeProjection)?.type
+internal val AbiTypeArgument.variance: AbiVariance?
+ get() = (this as? AbiTypeArgument.TypeProjection)?.variance
+private val AbiClassifierReference.className: AbiQualifiedName?
+ get() = (this as? AbiClassifierReference.ClassReference)?.className
+private val AbiClassifierReference.tag: String?
+ get() = (this as? AbiClassifierReference.TypeParameterReference)?.tag
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/BinaryCompatibilityChecker.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/BinaryCompatibilityChecker.kt
new file mode 100644
index 0000000..9852727
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/BinaryCompatibilityChecker.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalLibraryAbiReader::class)
+
+package androidx.binarycompatibilityvalidator
+
+import org.jetbrains.kotlin.library.abi.ExperimentalLibraryAbiReader
+import org.jetbrains.kotlin.library.abi.LibraryAbi
+
+@Suppress("UNUSED_PARAMETER")
+@OptIn(ExperimentalLibraryAbiReader::class)
+class BinaryCompatibilityChecker(
+ private val newLibraryAbi: LibraryAbi,
+ private val oldLibraryAbi: LibraryAbi
+) {
+ fun checkBinariesAreCompatible() {
+ TODO()
+ }
+
+ companion object {
+ fun checkAllBinariesAreCompatible(
+ newLibraryAbis: Map<String, LibraryAbi>,
+ oldLibraryAbis: Map<String, LibraryAbi>
+ ) {
+ TODO()
+ }
+ }
+}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/Cursor.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/Cursor.kt
new file mode 100644
index 0000000..c7468f9
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/Cursor.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 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.binarycompatibilityvalidator
+
+class Cursor private constructor(
+ private val lines: List<String>,
+ rowIndex: Int = 0,
+ private var columnIndex: Int = 0
+) {
+ constructor(text: String) : this(text.split("\n"))
+ var rowIndex: Int = rowIndex
+ private set
+ val currentLine: String
+ get() = lines[rowIndex].slice(columnIndex until lines[rowIndex].length)
+ fun hasNextRow() = rowIndex < (lines.size - 1)
+
+ /** Check if we have passed the last line in [lines] and there is nothing left to parse **/
+ fun isFinished() = rowIndex >= lines.size
+ fun nextLine() {
+ rowIndex++
+ columnIndex = 0
+ if (!isFinished()) {
+ skipInlineWhitespace()
+ }
+ }
+
+ fun parseSymbol(
+ pattern: String,
+ peek: Boolean = false,
+ skipInlineWhitespace: Boolean = true
+ ): String? {
+ val match = Regex(pattern).find(currentLine)
+ return match?.value?.also {
+ if (!peek) {
+ val offset = it.length + currentLine.indexOf(it)
+ setColumn(columnIndex + offset)
+ if (skipInlineWhitespace) {
+ skipInlineWhitespace()
+ }
+ }
+ }
+ }
+
+ fun parseValidIdentifier(peek: Boolean = false): String? =
+ parseSymbol("^[a-zA-Z_][a-zA-Z0-9_]+", peek)
+
+ fun parseWord(peek: Boolean = false): String? = parseSymbol("[a-zA-Z]+", peek)
+
+ fun copy() = Cursor(lines, rowIndex, columnIndex)
+
+ private fun hasNextColumn(): Boolean {
+ return columnIndex < lines[rowIndex].length - 1
+ }
+
+ private fun setColumn(index: Int) {
+ columnIndex = index
+ }
+
+ internal fun skipInlineWhitespace() {
+ while (currentLine.firstOrNull()?.isWhitespace() == true) {
+ setColumn(columnIndex + 1)
+ }
+ }
+}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt
new file mode 100644
index 0000000..9b2dd4a
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KLibDumpParser.kt
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2024 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.
+ */
+
+// Need to access Impl classes from 'org.jetbrains.kotlin.library.abi.impl.'
+// ideally the parser would also live alongside that project to access to impl classes
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+@file:OptIn(ExperimentalLibraryAbiReader::class)
+
+package androidx.binarycompatibilityvalidator
+
+import org.jetbrains.kotlin.library.abi.AbiClass
+import org.jetbrains.kotlin.library.abi.AbiCompoundName
+import org.jetbrains.kotlin.library.abi.AbiDeclaration
+import org.jetbrains.kotlin.library.abi.AbiFunction
+import org.jetbrains.kotlin.library.abi.AbiModality
+import org.jetbrains.kotlin.library.abi.AbiProperty
+import org.jetbrains.kotlin.library.abi.AbiQualifiedName
+import org.jetbrains.kotlin.library.abi.ExperimentalLibraryAbiReader
+import org.jetbrains.kotlin.library.abi.LibraryAbi
+import org.jetbrains.kotlin.library.abi.LibraryManifest
+import org.jetbrains.kotlin.library.abi.impl.AbiClassImpl
+import org.jetbrains.kotlin.library.abi.impl.AbiConstructorImpl
+import org.jetbrains.kotlin.library.abi.impl.AbiFunctionImpl
+import org.jetbrains.kotlin.library.abi.impl.AbiPropertyImpl
+import org.jetbrains.kotlin.library.abi.impl.AbiSignaturesImpl
+import org.jetbrains.kotlin.library.abi.impl.AbiTopLevelDeclarationsImpl
+import org.jetbrains.kotlin.library.abi.impl.AbiValueParameterImpl
+
+@OptIn(ExperimentalLibraryAbiReader::class)
+class KlibDumpParser(klibDump: String) {
+
+ /** Cursor to keep track of current location within the dump **/
+ private val cursor = Cursor(klibDump)
+ /** The set of targets that the declarations being parsed belong to **/
+ private val currentTargets = mutableSetOf<String>()
+ /**
+ * Map of all targets to the declarations that belong to them.
+ * Only update the [currentTargets] when parsing declarations.
+ **/
+ private val abiDeclarationsByTarget = mutableMapOf<String, MutableList<AbiDeclaration>>()
+
+ /** Parse the klib dump tracked by [cursor] into a map of targets to [LibraryAbi]s **/
+ fun parse(): Map<String, LibraryAbi> {
+ while (cursor.hasNextRow()) {
+ parseDeclaration(parentQualifiedName = null)?.let { abiDeclaration ->
+ // Find all the targets the current declaration belongs to
+ currentTargets.map { target ->
+ abiDeclarationsByTarget[target]
+ ?: throw IllegalStateException("Expected target $target to exist in map")
+ }.forEach {
+ // and add the parsed declaration to the list for those targets
+ it.add(abiDeclaration)
+ }
+ }
+ }
+ return abiDeclarationsByTarget.map { (target, abiDeclarations) ->
+ target to LibraryAbi(
+ uniqueName = "",
+ signatureVersions = emptySet(),
+ topLevelDeclarations = AbiTopLevelDeclarationsImpl(abiDeclarations),
+ manifest = LibraryManifest(
+ platform = target,
+ // To be completed in follow up CLs. This information is currently not
+ // considered when checking for compatibility
+ nativeTargets = listOf(),
+ compilerVersion = "",
+ abiVersion = "",
+ libraryVersion = "",
+ irProviderName = ""
+ )
+ )
+ }.toMap()
+ }
+
+ private fun parseDeclaration(parentQualifiedName: AbiQualifiedName?): AbiDeclaration? {
+ // if the line begins with a comment, we may need to parse the current target list
+ if (cursor.parseSymbol("^\\/\\/") != null) {
+ if (cursor.parseSymbol("Targets: ") != null) {
+ // There are never targets within targets, so when we encounter a new directive we
+ // always reset our current targets
+ val targets = cursor.parseTargets()
+ targets.forEach {
+ abiDeclarationsByTarget.putIfAbsent(it, mutableListOf())
+ }
+ currentTargets.clear()
+ currentTargets.addAll(targets)
+ }
+ cursor.nextLine()
+ } else if (cursor.hasClassKind()) {
+ return parseClass(parentQualifiedName)
+ } else if (cursor.hasFunctionKind()) {
+ return parseFunction(parentQualifiedName)
+ } else if (cursor.hasPropertyKind()) {
+ return parseProperty(parentQualifiedName)
+ } else if (cursor.currentLine.isBlank()) {
+ cursor.nextLine()
+ } else {
+ throw ParseException(cursor, "Unknown declaration")
+ }
+ return null
+ }
+
+ internal fun parseClass(parentQualifiedName: AbiQualifiedName? = null): AbiClass {
+ val modality = cursor.parseAbiModality()
+ ?: throw ParseException(cursor, "Failed to parse class modality")
+ val modifiers = cursor.parseClassModifiers()
+ val isInner = modifiers.contains("inner")
+ val isValue = modifiers.contains("value")
+ val isFunction = modifiers.contains("fun")
+ val kind = cursor.parseClassKind()
+ ?: throw ParseException(cursor, "Failed to parse class kind")
+ val typeParams = cursor.parseTypeParams() ?: emptyList()
+ // if we are a nested class the name won't be qualified and we will need to use the
+ // [parentQualifiedName] to complete it
+ val abiQualifiedName = parseAbiQualifiedName(parentQualifiedName)
+ val superTypes = cursor.parseSuperTypes()
+
+ val childDeclarations = if (cursor.parseSymbol("^\\{") != null) {
+ cursor.nextLine()
+ parseChildDeclarations(abiQualifiedName)
+ } else {
+ emptyList()
+ }
+ return AbiClassImpl(
+ qualifiedName = abiQualifiedName,
+ signatures = fakeSignatures,
+ annotations = emptySet(), // annotations aren't part of klib dumps
+ modality = modality,
+ kind = kind,
+ isInner = isInner,
+ isValue = isValue,
+ isFunction = isFunction,
+ superTypes = superTypes.toList(),
+ declarations = childDeclarations,
+ typeParameters = typeParams
+ )
+ }
+
+ internal fun parseFunction(
+ parentQualifiedName: AbiQualifiedName? = null,
+ isGetterOrSetter: Boolean = false
+ ): AbiFunction {
+ val modality = cursor.parseAbiModality()
+ val isConstructor = cursor.parseFunctionKind(peek = true) == "constructor"
+ return when {
+ isConstructor -> parseConstructor(parentQualifiedName)
+ else -> parseNonConstructorFunction(
+ parentQualifiedName,
+ isGetterOrSetter,
+ modality
+ ?: throw ParseException(cursor, "Non constructor function must have modality"),
+ )
+ }
+ }
+
+ internal fun parseProperty(parentQualifiedName: AbiQualifiedName? = null): AbiProperty {
+ val modality = cursor.parseAbiModality()
+ ?: throw ParseException(cursor, "Unable to parse modality for property")
+ val kind = cursor.parsePropertyKind()
+ ?: throw ParseException(cursor, "Unable to parse kind for property")
+ val qualifiedName = parseAbiQualifiedName(parentQualifiedName)
+
+ cursor.nextLine()
+ var getter: AbiFunction? = null
+ var setter: AbiFunction? = null
+ while (cursor.hasGetterOrSetter()) {
+ if (cursor.hasGetter()) {
+ getter = parseFunction(qualifiedName, isGetterOrSetter = true)
+ } else {
+ setter = parseFunction(qualifiedName, isGetterOrSetter = true)
+ }
+ if (cursor.isFinished()) {
+ break
+ }
+ }
+ return AbiPropertyImpl(
+ qualifiedName = qualifiedName,
+ signatures = fakeSignatures,
+ annotations = emptySet(), // annotations aren't part of klib dumps
+ modality = modality,
+ kind = kind,
+ getter = getter,
+ setter = setter,
+ )
+ }
+
+ /** Parse all declarations which belong to a parent such as a class **/
+ private fun parseChildDeclarations(
+ parentQualifiedName: AbiQualifiedName?
+ ): List<AbiDeclaration> {
+ val childDeclarations = mutableListOf<AbiDeclaration>()
+ // end of parent container is marked by a closing bracket, collect all declarations
+ // until we see one.
+ while (cursor.parseSymbol("^(\\s+)?\\}", peek = true) == null) {
+ parseDeclaration(parentQualifiedName)?.let { childDeclarations.add(it) }
+ }
+ cursor.nextLine()
+ return childDeclarations
+ }
+
+ private fun parseNonConstructorFunction(
+ parentQualifiedName: AbiQualifiedName? = null,
+ isGetterOrSetter: Boolean = false,
+ modality: AbiModality
+ ): AbiFunction {
+ val modifiers = cursor.parseFunctionModifiers()
+ val isInline = modifiers.contains("inline")
+ val isSuspend = modifiers.contains("suspend")
+ cursor.parseFunctionKind()
+ val typeParams = cursor.parseTypeParams() ?: emptyList()
+ val functionReceiver = cursor.parseFunctionReceiver()
+ val abiQualifiedName = if (isGetterOrSetter) {
+ parseAbiQualifiedNameForGetterOrSetter(parentQualifiedName)
+ } else {
+ parseAbiQualifiedName(parentQualifiedName)
+ }
+ val valueParameters = cursor.parseValueParameters()
+ ?: throw ParseException(cursor, "Couldn't parse value params")
+ val allValueParameters = if (null != functionReceiver) {
+ val functionReceiverAsValueParam = AbiValueParameterImpl(
+ type = functionReceiver,
+ isVararg = false,
+ hasDefaultArg = false,
+ isNoinline = false,
+ isCrossinline = false
+ )
+ listOf(functionReceiverAsValueParam) + valueParameters
+ } else {
+ valueParameters
+ }
+ val returnType = cursor.parseReturnType()
+ cursor.nextLine()
+ return AbiFunctionImpl(
+ qualifiedName = abiQualifiedName,
+ signatures = fakeSignatures,
+ annotations = emptySet(), // annotations aren't part of klib dumps
+ modality = modality,
+ isInline = isInline,
+ isSuspend = isSuspend,
+ typeParameters = typeParams,
+ hasExtensionReceiverParameter = null != functionReceiver,
+ contextReceiverParametersCount = 0, // TODO
+ valueParameters = allValueParameters,
+ returnType = returnType
+ )
+ }
+
+ private fun parseConstructor(parentQualifiedName: AbiQualifiedName?): AbiFunction {
+ val abiQualifiedName = parentQualifiedName?.let {
+ AbiQualifiedName(
+ parentQualifiedName.packageName,
+ AbiCompoundName(parentQualifiedName.relativeName.value + ".<init>")
+ )
+ } ?: throw ParseException(cursor, "Cannot parse constructor outside of class context")
+ cursor.parseSymbol("constructor")
+ cursor.parseSymbol("<init>")
+ val valueParameters = cursor.parseValueParameters()
+ ?: throw ParseException(cursor, "Couldn't parse value parameters for constructor")
+ cursor.nextLine()
+ return AbiConstructorImpl(
+ qualifiedName = abiQualifiedName,
+ signatures = fakeSignatures,
+ annotations = emptySet(), // annotations aren't part of klib dumps
+ isInline = false, // TODO
+ contextReceiverParametersCount = 0, // TODO
+ valueParameters = valueParameters,
+ )
+ }
+
+ private fun parseAbiQualifiedName(
+ parentQualifiedName: AbiQualifiedName?
+ ): AbiQualifiedName {
+ val hasQualifiedName = cursor.parseAbiQualifiedName(peek = true) != null
+ return if (hasQualifiedName) {
+ cursor.parseAbiQualifiedName()!!
+ } else {
+ if (parentQualifiedName == null) {
+ throw ParseException(cursor, "Failed to parse qName")
+ }
+ val identifier = cursor.parseValidIdentifier()
+ val relativeName = parentQualifiedName.relativeName.value + "." + identifier
+ return AbiQualifiedName(
+ parentQualifiedName.packageName,
+ AbiCompoundName(
+ relativeName
+ )
+ )
+ }
+ }
+
+ private fun parseAbiQualifiedNameForGetterOrSetter(
+ parentQualifiedName: AbiQualifiedName?
+ ): AbiQualifiedName {
+ if (parentQualifiedName == null) {
+ throw ParseException(cursor, "Failed to parse qName")
+ }
+ val identifier = cursor.parseGetterOrSetterName()
+ ?: throw ParseException(cursor, "Failed to parse qName")
+ val relativeName = parentQualifiedName.relativeName.value + "." + identifier
+ return AbiQualifiedName(
+ parentQualifiedName.packageName,
+ AbiCompoundName(
+ relativeName
+ )
+ )
+ }
+
+ companion object {
+ // placeholder signatures, currently not considered during parsing / compatibility checking
+ private val fakeSignatures = AbiSignaturesImpl(
+ signatureV1 = null,
+ signatureV2 = null
+ )
+ }
+}
+
+/** Exception which uses the cursor to include the location of the failure **/
+class ParseException(cursor: Cursor, message: String) : RuntimeException(
+ "$message ${cursor.rowIndex + 1}: ${cursor.currentLine}"
+)
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensions.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensions.kt
new file mode 100644
index 0000000..dfc2502
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensions.kt
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2024 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.
+ */
+
+// Impl classes from kotlin.library.abi.impl are necessary to instantiate parsed declarations
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+@file:OptIn(ExperimentalLibraryAbiReader::class)
+
+package androidx.binarycompatibilityvalidator
+
+import org.jetbrains.kotlin.library.abi.AbiClassKind
+import org.jetbrains.kotlin.library.abi.AbiCompoundName
+import org.jetbrains.kotlin.library.abi.AbiModality
+import org.jetbrains.kotlin.library.abi.AbiPropertyKind
+import org.jetbrains.kotlin.library.abi.AbiQualifiedName
+import org.jetbrains.kotlin.library.abi.AbiType
+import org.jetbrains.kotlin.library.abi.AbiTypeArgument
+import org.jetbrains.kotlin.library.abi.AbiTypeNullability
+import org.jetbrains.kotlin.library.abi.AbiTypeParameter
+import org.jetbrains.kotlin.library.abi.AbiValueParameter
+import org.jetbrains.kotlin.library.abi.AbiVariance
+import org.jetbrains.kotlin.library.abi.ExperimentalLibraryAbiReader
+import org.jetbrains.kotlin.library.abi.impl.AbiTypeParameterImpl
+import org.jetbrains.kotlin.library.abi.impl.AbiValueParameterImpl
+import org.jetbrains.kotlin.library.abi.impl.ClassReferenceImpl
+import org.jetbrains.kotlin.library.abi.impl.SimpleTypeImpl
+import org.jetbrains.kotlin.library.abi.impl.StarProjectionImpl
+import org.jetbrains.kotlin.library.abi.impl.TypeParameterReferenceImpl
+import org.jetbrains.kotlin.library.abi.impl.TypeProjectionImpl
+
+// This file contains Cursor methods specific to parsing klib dump files
+
+internal fun Cursor.parseAbiModality(): AbiModality? {
+ val parsed = parseAbiModalityString(peek = true)?.let {
+ AbiModality.valueOf(it)
+ }
+ if (parsed != null) {
+ parseAbiModalityString()
+ }
+ return parsed
+}
+
+internal fun Cursor.parseClassKind(peek: Boolean = false): AbiClassKind? {
+ val parsed = parseClassKindString(peek = true)?.let {
+ AbiClassKind.valueOf(it)
+ }
+ if (parsed != null && !peek) {
+ parseClassKindString()
+ }
+ return parsed
+}
+
+internal fun Cursor.parsePropertyKind(peek: Boolean = false): AbiPropertyKind? {
+ val parsed = parsePropertyKindString(peek = true)?.let {
+ AbiPropertyKind.valueOf(it)
+ }
+ if (parsed != null && !peek) {
+ parsePropertyKindString()
+ }
+ return parsed
+}
+
+internal fun Cursor.hasClassKind(): Boolean {
+ val subCursor = copy()
+ subCursor.skipInlineWhitespace()
+ subCursor.parseAbiModality()
+ subCursor.parseClassModifiers()
+ return subCursor.parseClassKind() != null
+}
+
+internal fun Cursor.hasFunctionKind(): Boolean {
+ val subCursor = copy()
+ subCursor.skipInlineWhitespace()
+ subCursor.parseAbiModality()
+ subCursor.parseFunctionModifiers()
+ return subCursor.parseFunctionKind() != null
+}
+
+internal fun Cursor.hasPropertyKind(): Boolean {
+ val subCursor = copy()
+ subCursor.skipInlineWhitespace()
+ subCursor.parseAbiModality()
+ return subCursor.parsePropertyKind() != null
+}
+
+internal fun Cursor.hasGetter() = hasPropertyAccessor(GetterOrSetter.GETTER)
+internal fun Cursor.hasSetter() = hasPropertyAccessor(GetterOrSetter.SETTER)
+
+internal fun Cursor.hasGetterOrSetter() = hasGetter() || hasSetter()
+
+internal fun Cursor.parseGetterName(peek: Boolean = false): String? {
+ val cursor = subCursor(peek)
+ cursor.parseSymbol("^<get\\-") ?: return null
+ val name = cursor.parseValidIdentifier() ?: return null
+ cursor.parseSymbol("^>") ?: return null
+ return "<get-$name>"
+}
+
+internal fun Cursor.parseSetterName(peek: Boolean = false): String? {
+ val cursor = subCursor(peek)
+ cursor.parseSymbol("^<set\\-") ?: return null
+ val name = cursor.parseValidIdentifier() ?: return null
+ cursor.parseSymbol("^>") ?: return null
+ return "<set-$name>"
+}
+
+internal fun Cursor.parseGetterOrSetterName(peek: Boolean = false) =
+ parseGetterName(peek) ?: parseSetterName(peek)
+
+internal fun Cursor.parseClassModifier(peek: Boolean = false): String? =
+ parseSymbol("^(inner|value|fun|open)", peek)
+
+internal fun Cursor.parseClassModifiers(): Set<String> {
+ val modifiers = mutableSetOf<String>()
+ while (parseClassModifier(peek = true) != null) {
+ modifiers.add(parseClassModifier()!!)
+ }
+ return modifiers
+}
+
+internal fun Cursor.parseFunctionKind(peek: Boolean = false) =
+ parseSymbol("^(constructor|fun)", peek)
+
+internal fun Cursor.parseFunctionModifier(peek: Boolean = false): String? =
+ parseSymbol("^(inline|suspend)", peek)
+
+internal fun Cursor.parseFunctionModifiers(): Set<String> {
+ val modifiers = mutableSetOf<String>()
+ while (parseFunctionModifier(peek = true) != null) {
+ modifiers.add(parseFunctionModifier()!!)
+ }
+ return modifiers
+}
+
+internal fun Cursor.parseAbiQualifiedName(peek: Boolean = false): AbiQualifiedName? {
+ val symbol = parseSymbol("^[a-zA-Z0-9\\.]+\\/[a-zA-Z0-9]+(\\.[a-zA-Z0-9]+)?", peek)
+ ?: return null
+ val (packageName, relativeName) = symbol.split("/")
+ return AbiQualifiedName(
+ AbiCompoundName(packageName),
+ AbiCompoundName(relativeName)
+ )
+}
+
+internal fun Cursor.parseAbiType(peek: Boolean = false): AbiType? {
+ val cursor = subCursor(peek)
+ // A type will either be a qualified name (kotlin/Array) or a type reference (#A)
+ // try to parse a qualified name and a type reference if it doesn't exist
+ val abiQualifiedName = cursor.parseAbiQualifiedName()
+ ?: return cursor.parseTypeReference()
+ val typeArgs = cursor.parseTypeArgs() ?: emptyList()
+ val nullability = cursor.parseNullability(assumeNotNull = true)
+ return SimpleTypeImpl(
+ ClassReferenceImpl(abiQualifiedName),
+ arguments = typeArgs,
+ nullability = nullability
+ )
+}
+
+internal fun Cursor.parseTypeArgs(): List<AbiTypeArgument>? {
+ val typeArgsString = parseTypeParamsString() ?: return null
+ val subCursor = Cursor(typeArgsString)
+ subCursor.parseSymbol("<") ?: return null
+ val typeArgs = mutableListOf<AbiTypeArgument>()
+ while (subCursor.parseTypeArg(peek = true) != null) {
+ typeArgs.add(subCursor.parseTypeArg()!!)
+ subCursor.parseSymbol(",")
+ }
+ return typeArgs
+}
+
+internal fun Cursor.parseTypeArg(peek: Boolean = false): AbiTypeArgument? {
+ val cursor = subCursor(peek)
+ val variance = cursor.parseAbiVariance()
+ cursor.parseSymbol("\\*")?.let {
+ return StarProjectionImpl
+ }
+ val type = cursor.parseAbiType(peek) ?: return null
+ return TypeProjectionImpl(
+ type = type,
+ variance = variance
+ )
+}
+
+internal fun Cursor.parseAbiVariance(): AbiVariance {
+ val variance = parseSymbol("^(out|in)") ?: return AbiVariance.INVARIANT
+ return AbiVariance.valueOf(variance.uppercase())
+}
+
+internal fun Cursor.parseTypeReference(): AbiType? {
+ val typeParamReference = parseTag() ?: return null
+ val typeArgs = parseTypeArgs() ?: emptyList()
+ val nullability = parseNullability()
+ return SimpleTypeImpl(
+ TypeParameterReferenceImpl(typeParamReference),
+ arguments = typeArgs,
+ nullability = nullability
+ )
+}
+
+internal fun Cursor.parseTag() = parseSymbol("^#[a-zA-Z0-9]+")?.removePrefix("#")
+
+internal fun Cursor.parseNullability(assumeNotNull: Boolean = false): AbiTypeNullability {
+ val nullable = parseSymbol("^\\?") != null
+ val definitelyNotNull = parseSymbol("^\\!\\!") != null
+ return when {
+ nullable -> AbiTypeNullability.MARKED_NULLABLE
+ definitelyNotNull -> AbiTypeNullability.DEFINITELY_NOT_NULL
+ else -> if (assumeNotNull) {
+ AbiTypeNullability.DEFINITELY_NOT_NULL
+ } else {
+ AbiTypeNullability.NOT_SPECIFIED
+ }
+ }
+}
+
+internal fun Cursor.parseSuperTypes(): MutableSet<AbiType> {
+ parseSymbol(":")
+ val superTypes = mutableSetOf<AbiType>()
+ while (parseAbiQualifiedName(peek = true) != null) {
+ superTypes.add(parseAbiType()!!)
+ parseSymbol(",")
+ }
+ return superTypes
+}
+
+fun Cursor.parseTypeParams(peek: Boolean = false): List<AbiTypeParameter>? {
+ val typeParamsString = parseTypeParamsString(peek) ?: return null
+ val subCursor = Cursor(typeParamsString)
+ subCursor.parseSymbol("^<")
+ val typeParams = mutableListOf<AbiTypeParameter>()
+ while (subCursor.parseTypeParam(peek = true) != null) {
+ typeParams.add(subCursor.parseTypeParam()!!)
+ subCursor.parseSymbol("^,")
+ }
+ return typeParams
+}
+
+fun Cursor.parseTypeParam(peek: Boolean = false): AbiTypeParameter? {
+ val cursor = subCursor(peek)
+ val tag = cursor.parseTag() ?: return null
+ cursor.parseSymbol("^:")
+ val variance = cursor.parseAbiVariance()
+ val isReified = cursor.parseSymbol("reified") != null
+ val upperBounds = mutableListOf<AbiType>()
+ if (null != cursor.parseAbiType(peek = true)) {
+ upperBounds.add(cursor.parseAbiType()!!)
+ }
+
+ return AbiTypeParameterImpl(
+ tag = tag,
+ variance = variance,
+ isReified = isReified,
+ upperBounds = upperBounds
+ )
+}
+
+internal fun Cursor.parseValueParameters(): List<AbiValueParameter>? {
+ val valueParamString = parseValueParametersString() ?: return null
+ val subCursor = Cursor(valueParamString)
+ val valueParams = mutableListOf<AbiValueParameter>()
+ subCursor.parseSymbol("\\(")
+ while (null != subCursor.parseValueParameter(peek = true)) {
+ valueParams.add(subCursor.parseValueParameter()!!)
+ subCursor.parseSymbol("^,")
+ }
+ return valueParams
+}
+
+internal fun Cursor.parseValueParameter(peek: Boolean = false): AbiValueParameter? {
+ val cursor = subCursor(peek)
+ val modifiers = cursor.parseValueParameterModifiers()
+ val isNoInline = modifiers.contains("noinline")
+ val isCrossinline = modifiers.contains("crossinline")
+ val type = cursor.parseAbiType() ?: return null
+ val isVararg = cursor.parseVarargSymbol() != null
+ val hasDefaultArg = cursor.parseDefaultArg() != null
+ return AbiValueParameterImpl(
+ type = type,
+ isVararg = isVararg,
+ hasDefaultArg = hasDefaultArg,
+ isNoinline = isNoInline,
+ isCrossinline = isCrossinline
+ )
+}
+
+internal fun Cursor.parseValueParameterModifiers(): Set<String> {
+ val modifiers = mutableSetOf<String>()
+ while (parseValueParameterModifier(peek = true) != null) {
+ modifiers.add(parseValueParameterModifier()!!)
+ }
+ return modifiers
+}
+
+internal fun Cursor.parseValueParameterModifier(peek: Boolean = false): String? =
+ parseSymbol("^(crossinline|noinline)", peek)
+
+internal fun Cursor.parseVarargSymbol() = parseSymbol("^\\.\\.\\.")
+
+internal fun Cursor.parseDefaultArg() = parseSymbol("^=\\.\\.\\.")
+
+internal fun Cursor.parseFunctionReceiver(): AbiType? {
+ val string = parseFunctionReceiverString() ?: return null
+ val subCursor = Cursor(string)
+ subCursor.parseSymbol("\\(")
+ return subCursor.parseAbiType()
+}
+
+internal fun Cursor.parseReturnType(): AbiType? {
+ parseSymbol("^:\\s")
+ return parseAbiType()
+}
+
+internal fun Cursor.parseTargets(): List<String> {
+ parseSymbol("^Targets:")
+ parseSymbol("^\\[")
+ val targets = mutableListOf<String>()
+ while (parseValidIdentifier(peek = true) != null) {
+ targets.add(parseValidIdentifier()!!)
+ parseSymbol("^,")
+ }
+ parseSymbol("^\\]")
+ return targets
+}
+
+/**
+ * Used to check if declarations after a property are getter / setter methods which should be
+ * attached to that property.
+*/
+private fun Cursor.hasPropertyAccessor(type: GetterOrSetter): Boolean {
+ val subCursor = copy()
+ subCursor.parseAbiModality()
+ subCursor.parseFunctionModifiers()
+ subCursor.parseFunctionKind() ?: return false // if it's not a function it's not a getter/setter
+ val mightHaveTypeParams = subCursor.parseGetterOrSetterName(peek = true) == null
+ if (mightHaveTypeParams) {
+ subCursor.parseTypeParams()
+ }
+ subCursor.parseFunctionReceiver()
+ return when (type) {
+ GetterOrSetter.GETTER -> subCursor.parseGetterName() != null
+ GetterOrSetter.SETTER -> subCursor.parseSetterName() != null
+ }
+}
+
+private fun Cursor.subCursor(peek: Boolean) = if (peek) { copy() } else { this }
+
+private fun Cursor.parseTypeParamsString(peek: Boolean = false): String? {
+ if (parseSymbol("^<(get|set)\\-", peek = true) != null) {
+ return null
+ }
+ val cursor = subCursor(peek)
+ val result = StringBuilder()
+ cursor.parseSymbol("^<")?.let { result.append(it) } ?: return null
+ var openBracketCount = 1
+ while (openBracketCount > 0) {
+ val nextSymbol = cursor.parseSymbol(".", skipInlineWhitespace = false).also {
+ result.append(it)
+ }
+ when (nextSymbol) {
+ "<" -> openBracketCount++
+ ">" -> openBracketCount--
+ }
+ }
+ cursor.skipInlineWhitespace()
+ return result.toString()
+}
+
+private fun Cursor.parseFunctionReceiverString() =
+ parseSymbol("^\\([a-zA-Z0-9,\\/<>,#\\.\\s]+?\\)\\.")
+
+private fun Cursor.parseValueParametersString() =
+ parseSymbol("^\\(([a-zA-Z0-9,\\/<>,#\\.\\s\\?=]+)?\\)")
+
+private fun Cursor.parseAbiModalityString(peek: Boolean = false) =
+ parseSymbol("^(final|open|abstract|sealed)", peek)?.uppercase()
+
+private fun Cursor.parsePropertyKindString(peek: Boolean = false) =
+ parseSymbol("^(const\\sval|val|var)", peek)?.uppercase()?.replace(" ", "_")
+
+private fun Cursor.parseClassKindString(peek: Boolean = false) =
+ parseSymbol(
+ "^(class|interface|object|enum\\sclass|annotation\\sclass)",
+ peek
+ )?.uppercase()?.replace(" ", "_")
+
+private enum class GetterOrSetter() {
+ GETTER,
+ SETTER
+}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/CursorTest.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/CursorTest.kt
new file mode 100644
index 0000000..ba6d235
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/CursorTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2024 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.binarycompatibilityvalidator
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class CursorTest {
+
+ @Test
+ fun cursorShowsCurrentLine() {
+ val input = "one\ntwo\nthree"
+ val cursor = Cursor(input)
+ assertThat(cursor.currentLine).isEqualTo("one")
+ cursor.nextLine()
+ assertThat(cursor.currentLine).isEqualTo("two")
+ cursor.nextLine()
+ assertThat(cursor.currentLine).isEqualTo("three")
+ assertThat(cursor.hasNextRow()).isFalse()
+ }
+
+ @Test
+ fun cursorGetsNextWord() {
+ val input = "one two three"
+ val cursor = Cursor(input)
+ val word = cursor.parseWord()
+ assertThat(word).isEqualTo("one")
+ assertThat("two three").isEqualTo(cursor.currentLine)
+ }
+
+ @Test
+ fun parseValidIdentifierValid() {
+ val input = "oneTwo3 four"
+ val cursor = Cursor(input)
+ val symbol = cursor.parseValidIdentifier()
+ assertThat(symbol).isEqualTo("oneTwo3")
+ assertThat("four").isEqualTo(cursor.currentLine)
+ }
+
+ @Test
+ fun parseValidIdentifierValidStartsWithUnderscore() {
+ val input = "_one_Two3 four"
+ val cursor = Cursor(input)
+ val symbol = cursor.parseValidIdentifier()
+ assertThat(symbol).isEqualTo("_one_Two3")
+ assertThat("four").isEqualTo(cursor.currentLine)
+ }
+
+ @Test
+ fun parseValidIdentifierInvalid() {
+ val input = "1twothree"
+ val cursor = Cursor(input)
+ val symbol = cursor.parseValidIdentifier()
+ assertThat(symbol).isNull()
+ }
+
+ @Test
+ fun skipWhitespace() {
+ val input = " test"
+ val cursor = Cursor(input)
+ cursor.skipInlineWhitespace()
+ assertThat(cursor.currentLine).isEqualTo("test")
+ }
+
+ @Test
+ fun skipWhitespaceOnBlankLine() {
+ val input = ""
+ val cursor = Cursor(input)
+ cursor.skipInlineWhitespace()
+ assertThat(cursor.currentLine).isEqualTo("")
+ }
+
+ @Test
+ fun skipWhitespaceSkipsEntireLine() {
+ val input = " "
+ val cursor = Cursor(input)
+ cursor.skipInlineWhitespace()
+ assertThat(cursor.currentLine).isEqualTo("")
+ }
+}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KLibDumpParserTest.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KLibDumpParserTest.kt
new file mode 100644
index 0000000..cf37335
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KLibDumpParserTest.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2024 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.binarycompatibilityvalidator
+
+import com.google.common.truth.Truth.assertThat
+import org.jetbrains.kotlin.library.abi.AbiClassKind
+import org.jetbrains.kotlin.library.abi.AbiCompoundName
+import org.jetbrains.kotlin.library.abi.AbiModality
+import org.jetbrains.kotlin.library.abi.AbiQualifiedName
+import org.jetbrains.kotlin.library.abi.ExperimentalLibraryAbiReader
+import org.junit.Test
+
+@OptIn(ExperimentalLibraryAbiReader::class)
+class KlibDumpParserTest {
+
+ private val collectionDump = getJavaResource("collection.txt").readText()
+ private val datastoreCoreDump = getJavaResource("datastore.txt").readText()
+ private val uniqueTargetDump = getJavaResource("unique_targets.txt").readText()
+
+ @Test
+ fun parseASimpleClass() {
+ val input = "final class <#A: kotlin/Any?, #B: kotlin/Any?> " +
+ "androidx.collection/MutableScatterMap : androidx.collection/ScatterMap<#A, #B>"
+ val parsed = KlibDumpParser(input).parseClass()
+ assertThat(parsed).isNotNull()
+
+ assertThat(parsed.qualifiedName.toString()).isEqualTo(
+ "androidx.collection/MutableScatterMap"
+ )
+ }
+
+ @Test
+ fun parseAClassWithTwoSuperTypes() {
+ val input = "final class <#A: kotlin/Any?> androidx.collection/ArraySet : " +
+ "kotlin.collections/MutableCollection<#A>, kotlin.collections/MutableSet<#A>"
+ val parsed = KlibDumpParser(input).parseClass()
+ assertThat(parsed).isNotNull()
+
+ assertThat(parsed.qualifiedName.toString()).isEqualTo(
+ "androidx.collection/ArraySet"
+ )
+ assertThat(parsed.superTypes).hasSize(2)
+ }
+
+ @Test
+ fun parseAClassWithTypeParams() {
+ val input = "final class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/" +
+ "MutableScatterMap : androidx.collection/ScatterMap<#A, #B>"
+ val parsed = KlibDumpParser(input).parseClass()
+ assertThat(parsed).isNotNull()
+
+ assertThat(parsed.qualifiedName.toString()).isEqualTo(
+ "androidx.collection/MutableScatterMap"
+ )
+ assertThat(parsed.typeParameters).hasSize(2)
+ parsed.typeParameters.forEach {
+ assertThat(it.upperBounds.single().className?.toString()).isEqualTo("kotlin/Any")
+ }
+ }
+
+ @Test
+ fun parseAnAnnotationClass() {
+ val input = "open annotation class my.lib/MyClass : kotlin/Annotation"
+ val parsed = KlibDumpParser(input).parseClass()
+ assertThat(parsed).isNotNull()
+
+ assertThat(parsed.qualifiedName.toString()).isEqualTo(
+ "my.lib/MyClass"
+ )
+ assertThat(parsed.kind).isEqualTo(AbiClassKind.ANNOTATION_CLASS)
+ }
+
+ @Test
+ fun parseAFunction() {
+ val input = "final inline fun <#A1: kotlin/Any?> " +
+ "fold(#A1, kotlin/Function2<#A1, #A, #A1>): #A1"
+ val parentQName = AbiQualifiedName(
+ AbiCompoundName("androidx.collection"),
+ AbiCompoundName("ObjectList")
+ )
+ val parsed = KlibDumpParser(input).parseFunction(parentQName)
+ assertThat(parsed).isNotNull()
+
+ assertThat(parsed.qualifiedName.toString()).isEqualTo(
+ "androidx.collection/ObjectList.fold"
+ )
+ }
+
+ @Test
+ fun parseAFunctionWithTypeArgsOnParams() {
+ val input = "final fun <#A: kotlin/Any?> " +
+ "androidx.collection/arraySetOf(kotlin/Array<out #A>...): " +
+ "androidx.collection/ArraySet<#A>"
+ val parsed = KlibDumpParser(input).parseFunction()
+ assertThat(parsed).isNotNull()
+
+ assertThat(parsed.qualifiedName.toString()).isEqualTo(
+ "androidx.collection/arraySetOf"
+ )
+ val param = parsed.valueParameters.single()
+ assertThat(param.type.arguments).isNotEmpty()
+ }
+
+ @Test
+ fun parseAGetterFunction() {
+ val input = "final inline fun <get-indices>(): kotlin.ranges/IntRange"
+ val parentQName = AbiQualifiedName(
+ AbiCompoundName("androidx.collection"),
+ AbiCompoundName("ObjectList")
+ )
+ val parsed = KlibDumpParser(input).parseFunction(parentQName, isGetterOrSetter = true)
+ assertThat(parsed.qualifiedName.toString()).isEqualTo(
+ "androidx.collection/ObjectList.<get-indices>"
+ )
+ }
+
+ @Test
+ fun parseAGetterFunctionWithReceiver() {
+ val input = "final inline fun <#A1: kotlin/Any?> " +
+ "(androidx.collection/LongSparseArray<#A1>).<get-size>(): kotlin/Int"
+ val parentQName = AbiQualifiedName(
+ AbiCompoundName("androidx.collection"),
+ AbiCompoundName("ObjectList")
+ )
+ val parsed = KlibDumpParser(input).parseFunction(parentQName, isGetterOrSetter = true)
+ assertThat(parsed.hasExtensionReceiverParameter).isTrue()
+ }
+
+ @Test
+ fun parseAFunctionWithTypeArgAsReceiver() {
+ val input = "final inline fun <#A: androidx.datastore.core/Closeable, #B: kotlin/Any?> " +
+ "(#A).androidx.datastore.core/use(kotlin/Function1<#A, #B>): #B"
+ val parsed = KlibDumpParser(input).parseFunction()
+ assertThat(parsed.hasExtensionReceiverParameter).isTrue()
+ assertThat(parsed.typeParameters).hasSize(2)
+ }
+
+ @Test
+ fun parseAComplexFunction() {
+ val input = "final inline fun <#A: kotlin/Any, #B: kotlin/Any> androidx.collection/" +
+ "lruCache(kotlin/Int, crossinline kotlin/Function2<#A, #B, kotlin/Int> =..., " +
+ "crossinline kotlin/Function1<#A, #B?> =..., " +
+ "crossinline kotlin/Function4<kotlin/Boolean, #A, #B, #B?, kotlin/Unit> =...): " +
+ "androidx.collection/LruCache<#A, #B>"
+ val parsed = KlibDumpParser(input).parseFunction()
+ assertThat(parsed.modality).isEqualTo(AbiModality.FINAL)
+ assertThat(parsed.typeParameters).hasSize(2)
+ assertThat(parsed.qualifiedName.toString()).isEqualTo("androidx.collection/lruCache")
+ assertThat(parsed.valueParameters).hasSize(4)
+ }
+
+ @Test
+ fun parseANestedValProperty() {
+ val input = "final val size\n final fun <get-size>(): kotlin/Int"
+ val parsed = KlibDumpParser(input).parseProperty(
+ AbiQualifiedName(
+ AbiCompoundName("androidx.collection"),
+ AbiCompoundName("ScatterMap")
+ )
+ )
+ assertThat(parsed.getter).isNotNull()
+ assertThat(parsed.setter).isNull()
+ }
+
+ @Test
+ fun parseANestedVarProperty() {
+ val input = "final var keys\n" +
+ " final fun <get-keys>(): kotlin/Array<kotlin/Any?>\n" +
+ " final fun <set-keys>(kotlin/Array<kotlin/Any?>)"
+ val parsed = KlibDumpParser(input).parseProperty(
+ AbiQualifiedName(
+ AbiCompoundName("androidx.collection"),
+ AbiCompoundName("ScatterMap")
+ )
+ )
+ assertThat(parsed.getter).isNotNull()
+ assertThat(parsed.setter).isNotNull()
+ }
+
+ @Test
+ fun parseFullCollectionKlibDumpSucceeds() {
+ val parsed = KlibDumpParser(collectionDump).parse()
+ assertThat(parsed).isNotNull()
+ }
+
+ @Test
+ fun parseFullDatastoreKlibDumpSucceeds() {
+ val parsed = KlibDumpParser(datastoreCoreDump).parse()
+ assertThat(parsed).isNotNull()
+ }
+
+ @Test
+ fun parseUniqueTargetsSucceeds() {
+ val parsed = KlibDumpParser(uniqueTargetDump).parse()
+ assertThat(parsed).isNotNull()
+ assertThat(parsed.keys).hasSize(2)
+ assertThat(parsed.keys).containsExactly("iosX64", "linuxX64")
+ val iosQNames = parsed["iosX64"]?.topLevelDeclarations?.declarations?.map {
+ it.qualifiedName.toString()
+ }
+ val linuxQNames = parsed["linuxX64"]?.topLevelDeclarations?.declarations?.map {
+ it.qualifiedName.toString()
+ }
+ assertThat(iosQNames).containsExactly("my.lib/myIosFun", "my.lib/commonFun")
+ assertThat(linuxQNames).containsExactly("my.lib/myLinuxFun", "my.lib/commonFun")
+ }
+}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensionsTest.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensionsTest.kt
new file mode 100644
index 0000000..418e93c
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensionsTest.kt
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalLibraryAbiReader::class)
+
+package androidx.binarycompatibilityvalidator
+
+import com.google.common.truth.Truth.assertThat
+import org.jetbrains.kotlin.library.abi.AbiClassKind
+import org.jetbrains.kotlin.library.abi.AbiModality
+import org.jetbrains.kotlin.library.abi.AbiPropertyKind
+import org.jetbrains.kotlin.library.abi.AbiTypeNullability
+import org.jetbrains.kotlin.library.abi.AbiValueParameter
+import org.jetbrains.kotlin.library.abi.AbiVariance
+import org.jetbrains.kotlin.library.abi.ExperimentalLibraryAbiReader
+import org.junit.Test
+
+class KlibParsingCursorExtensionsTest {
+
+ @Test
+ fun parseModalityFailure() {
+ val input = "something else"
+ val cursor = Cursor(input)
+ val modality = cursor.parseAbiModality()
+ assertThat(modality).isNull()
+ assertThat(cursor.currentLine).isEqualTo("something else")
+ }
+
+ @Test
+ fun parseModalitySuccess() {
+ val input = "final whatever"
+ val cursor = Cursor(input)
+ val modality = cursor.parseAbiModality()
+ assertThat(modality).isEqualTo(AbiModality.FINAL)
+ assertThat(cursor.currentLine).isEqualTo("whatever")
+ }
+
+ @Test
+ fun parseClassModifier() {
+ val input = "inner whatever"
+ val cursor = Cursor(input)
+ val modifier = cursor.parseClassModifier()
+ assertThat(modifier).isEqualTo("inner")
+ assertThat(cursor.currentLine).isEqualTo("whatever")
+ }
+
+ @Test
+ fun parseClassModifiers() {
+ val input = "inner value fun whatever"
+ val cursor = Cursor(input)
+ val modifiers = cursor.parseClassModifiers()
+ assertThat(modifiers).containsExactly("inner", "fun", "value")
+ assertThat(cursor.currentLine).isEqualTo("whatever")
+ }
+
+ @Test
+ fun parseFunctionModifiers() {
+ val input = "final inline suspend fun component1(): kotlin/Long"
+ val cursor = Cursor(input)
+ cursor.parseAbiModality()
+ val modifiers = cursor.parseFunctionModifiers()
+ assertThat(modifiers).containsExactly("inline", "suspend")
+ assertThat(cursor.currentLine).isEqualTo("fun component1(): kotlin/Long")
+ }
+
+ @Test
+ fun parseClassKindSimple() {
+ val input = "class"
+ val cursor = Cursor(input)
+ val kind = cursor.parseClassKind()
+ assertThat(kind).isEqualTo(AbiClassKind.CLASS)
+ }
+
+ @Test
+ fun parseClassKindFalsePositive() {
+ val input = "androidx.collection/objectFloatMap"
+ val cursor = Cursor(input)
+ val kind = cursor.parseClassKind()
+ assertThat(kind).isNull()
+ }
+
+ @Test
+ fun parseClassKindMultiPart() {
+ val input = "annotation class"
+ val cursor = Cursor(input)
+ val kind = cursor.parseClassKind()
+ assertThat(kind).isEqualTo(AbiClassKind.ANNOTATION_CLASS)
+ }
+
+ @Test
+ fun hasClassKind() {
+ val input = "final class my.lib/MyClass"
+ val cursor = Cursor(input)
+ assertThat(cursor.hasClassKind()).isTrue()
+ assertThat(cursor.currentLine).isEqualTo(input)
+ }
+
+ @Test
+ fun parseFunctionKindSimple() {
+ val input = "fun hello"
+ val cursor = Cursor(input)
+ val kind = cursor.parseFunctionKind()
+ assertThat(kind).isEqualTo("fun")
+ assertThat(cursor.currentLine).isEqualTo(cursor.currentLine)
+ }
+
+ @Test fun hasFunctionKind() {
+ val input = " final fun myFun(): kotlin/String "
+ val cursor = Cursor(input)
+ assertThat(cursor.hasFunctionKind()).isTrue()
+ assertThat(cursor.currentLine).isEqualTo(input)
+ }
+
+ @Test fun hasFunctionKindConstructor() {
+ val input = " constructor <init>(kotlin/Int =...)"
+ val cursor = Cursor(input)
+ assertThat(cursor.hasFunctionKind()).isTrue()
+ assertThat(cursor.currentLine).isEqualTo(input)
+ }
+
+ @Test fun parseGetterOrSetterName() {
+ val input = "<get-indices>()"
+ val cursor = Cursor(input)
+ val name = cursor.parseGetterOrSetterName()
+ assertThat(name).isEqualTo("<get-indices>")
+ assertThat(cursor.currentLine).isEqualTo("()")
+ }
+
+ @Test fun hasGetter() {
+ val input = "final inline fun <get-indices>(): kotlin.ranges/IntRange"
+ val cursor = Cursor(input)
+ assertThat(cursor.hasGetter()).isTrue()
+ assertThat(cursor.currentLine).isEqualTo(input)
+ }
+
+ @Test fun hasSetter() {
+ val input = "final inline fun <set-indices>(): kotlin.ranges/IntRange"
+ val cursor = Cursor(input)
+ assertThat(cursor.hasSetter()).isTrue()
+ assertThat(cursor.currentLine).isEqualTo(input)
+ }
+
+ @Test fun hasGetterOrSetter() {
+ val inputs = listOf(
+ "final inline fun <set-indices>(): kotlin.ranges/IntRange",
+ "final inline fun <get-indices>(): kotlin.ranges/IntRange"
+ )
+ inputs.forEach { input ->
+ assertThat(Cursor(input).hasGetterOrSetter()).isTrue()
+ }
+ }
+
+ @Test
+ fun hasPropertyKind() {
+ val input = "final const val my.lib/myProp"
+ val cursor = Cursor(input)
+ assertThat(cursor.hasPropertyKind()).isTrue()
+ assertThat(cursor.currentLine).isEqualTo(input)
+ }
+
+ @Test
+ fun parsePropertyKindConstVal() {
+ val input = "const val something"
+ val cursor = Cursor(input)
+ val kind = cursor.parsePropertyKind()
+ assertThat(kind).isEqualTo(AbiPropertyKind.CONST_VAL)
+ assertThat(cursor.currentLine).isEqualTo("something")
+ }
+
+ @Test
+ fun parsePropertyKindVal() {
+ val input = "val something"
+ val cursor = Cursor(input)
+ val kind = cursor.parsePropertyKind()
+ assertThat(kind).isEqualTo(AbiPropertyKind.VAL)
+ assertThat(cursor.currentLine).isEqualTo("something")
+ }
+
+ @Test
+ fun parseNullability() {
+ val nullable = Cursor("?").parseNullability()
+ val notNull = Cursor("!!").parseNullability()
+ val unspecified = Cursor("another symbol").parseNullability()
+ assertThat(nullable).isEqualTo(AbiTypeNullability.MARKED_NULLABLE)
+ assertThat(notNull).isEqualTo(AbiTypeNullability.DEFINITELY_NOT_NULL)
+ assertThat(unspecified).isEqualTo(AbiTypeNullability.NOT_SPECIFIED)
+ }
+
+ @Test fun parseNullabilityWhenAssumingNotNullable() {
+ val unspecified = Cursor("").parseNullability(assumeNotNull = true)
+ assertThat(unspecified).isEqualTo(AbiTypeNullability.DEFINITELY_NOT_NULL)
+ }
+
+ @Test fun parseQualifiedName() {
+ val input = "androidx.collection/MutableScatterMap something"
+ val cursor = Cursor(input)
+ val qName = cursor.parseAbiQualifiedName()
+ assertThat(qName.toString()).isEqualTo("androidx.collection/MutableScatterMap")
+ assertThat(cursor.currentLine).isEqualTo("something")
+ }
+
+ @Test fun parseQualifiedNameKotlin() {
+ val input = "kotlin/Function2<#A1, #A, #A1>"
+ val cursor = Cursor(input)
+ val qName = cursor.parseAbiQualifiedName()
+ assertThat(qName.toString()).isEqualTo("kotlin/Function2")
+ assertThat(cursor.currentLine).isEqualTo("<#A1, #A, #A1>",)
+ }
+
+ @Test fun parseQualifie0dNameDoesNotGrabNullable() {
+ val input = "androidx.collection/MutableScatterMap? something"
+ val cursor = Cursor(input)
+ val qName = cursor.parseAbiQualifiedName()
+ assertThat(qName.toString()).isEqualTo("androidx.collection/MutableScatterMap")
+ assertThat(cursor.currentLine).isEqualTo("? something")
+ }
+
+ @Test
+ fun parseAbiType() {
+ val input = "androidx.collection/ScatterMap<#A, #B> something"
+ val cursor = Cursor(input)
+ val type = cursor.parseAbiType()
+ assertThat(type?.className?.toString()).isEqualTo(
+ "androidx.collection/ScatterMap"
+ )
+ assertThat(cursor.currentLine).isEqualTo("something")
+ }
+
+ @Test
+ fun parseAbiTypeWithAnotherType() {
+ val input = "androidx.collection/ScatterMap<#A, #B>, androidx.collection/Other<#A, #B> " +
+ "something"
+ val cursor = Cursor(input)
+ val type = cursor.parseAbiType()
+ assertThat(type?.className?.toString()).isEqualTo(
+ "androidx.collection/ScatterMap"
+ )
+ assertThat(cursor.currentLine).isEqualTo(
+ ", androidx.collection/Other<#A, #B> something"
+ )
+ }
+
+ @Test fun parseAbiTypeWithThreeParams() {
+ val input = "kotlin/Function2<#A1, #A, #A1>"
+ val cursor = Cursor(input)
+ val type = cursor.parseAbiType()
+ assertThat(type?.className?.toString()).isEqualTo("kotlin/Function2")
+ }
+
+ @Test
+ fun parseSuperTypes() {
+ val input = ": androidx.collection/ScatterMap<#A, #B>, androidx.collection/Other<#A, #B> " +
+ "something"
+ val cursor = Cursor(input)
+ val superTypes = cursor.parseSuperTypes().toList()
+ assertThat(superTypes).hasSize(2)
+ assertThat(superTypes.first().className?.toString()).isEqualTo(
+ "androidx.collection/ScatterMap"
+ )
+ assertThat(superTypes.last().className?.toString()).isEqualTo(
+ "androidx.collection/Other"
+ )
+ assertThat(cursor.currentLine).isEqualTo("something")
+ }
+
+ @Test fun parseReturnType() {
+ val input = ": androidx.collection/ScatterMap<#A, #B> stuff"
+ val cursor = Cursor(input)
+ val returnType = cursor.parseReturnType()
+ assertThat(returnType?.className?.toString()).isEqualTo(
+ "androidx.collection/ScatterMap"
+ )
+ assertThat(cursor.currentLine).isEqualTo("stuff")
+ }
+
+ @Test fun parseReturnTypeNullableWithTypeParamsNullable() {
+ val input = ": #B? stuff"
+ val cursor = Cursor(input)
+ val returnType = cursor.parseReturnType()
+ assertThat(returnType?.tag).isEqualTo("B")
+ assertThat(returnType?.nullability).isEqualTo(AbiTypeNullability.MARKED_NULLABLE)
+ assertThat(cursor.currentLine).isEqualTo("stuff")
+ }
+
+ @Test fun parseReturnTypeNullableWithTypeParamsNotSpecified() {
+ val input = ": #B stuff"
+ val cursor = Cursor(input)
+ val returnType = cursor.parseReturnType()
+ assertThat(returnType?.tag).isEqualTo("B")
+ assertThat(returnType?.nullability).isEqualTo(AbiTypeNullability.NOT_SPECIFIED)
+ assertThat(cursor.currentLine).isEqualTo("stuff")
+ }
+
+ @Test
+ fun parseFunctionReceiver() {
+ val input = "(androidx.collection/LongSparseArray<#A>).androidx.collection/keyIterator()"
+ val cursor = Cursor(input)
+ val receiver = cursor.parseFunctionReceiver()
+ assertThat(receiver?.className.toString()).isEqualTo(
+ "androidx.collection/LongSparseArray"
+ )
+ assertThat(cursor.currentLine).isEqualTo("androidx.collection/keyIterator()")
+ }
+
+ @Test
+ fun parseFunctionReceiver2() {
+ val input = "(androidx.collection/LongSparseArray<#A1>).<get-size>(): kotlin/Int"
+ val cursor = Cursor(input)
+ val receiver = cursor.parseFunctionReceiver()
+ assertThat(receiver?.className.toString()).isEqualTo(
+ "androidx.collection/LongSparseArray"
+ )
+ assertThat(cursor.currentLine).isEqualTo("<get-size>(): kotlin/Int")
+ }
+
+ @Test fun parseValueParamCrossinlineDefault() {
+ val input = "crossinline kotlin/Function2<#A, #B, kotlin/Int> =..."
+ val cursor = Cursor(input)
+ val valueParam = cursor.parseValueParameter()!!
+ assertThat(
+ valueParam.type.className.toString()
+ ).isEqualTo("kotlin/Function2")
+ assertThat(valueParam.hasDefaultArg).isTrue()
+ assertThat(valueParam.isCrossinline).isTrue()
+ assertThat(valueParam.isVararg).isFalse()
+ }
+
+ @Test
+ fun parseValueParamVararg() {
+ val input = "kotlin/Array<out kotlin/Pair<#A, #B>>..."
+ val cursor = Cursor(input)
+ val valueParam = cursor.parseValueParameter()
+ assertThat(
+ valueParam?.type?.className?.toString()
+ ).isEqualTo("kotlin/Array")
+ assertThat(valueParam?.hasDefaultArg).isFalse()
+ assertThat(valueParam?.isCrossinline).isFalse()
+ assertThat(valueParam?.isVararg).isTrue()
+ }
+
+ @Test fun parseValueParametersWithTypeArgs() {
+ val input = "kotlin/Array<out #A>..."
+ val cursor = Cursor(input)
+ val valueParam = cursor.parseValueParameter()
+ assertThat(valueParam?.type?.arguments).hasSize(1)
+ }
+
+ @Test fun parseValueParametersWithTwoTypeArgs() {
+ val input = "kotlin/Function1<kotlin/Double, kotlin/Boolean>)"
+ val cursor = Cursor(input)
+ val valueParam = cursor.parseValueParameter()
+ assertThat(valueParam?.type?.arguments).hasSize(2)
+ }
+
+ @Test fun parseValueParametersEmpty() {
+ val input = "() thing"
+ val cursor = Cursor(input)
+ val params = cursor.parseValueParameters()
+ assertThat(params).isEqualTo(emptyList<AbiValueParameter>())
+ assertThat(cursor.currentLine).isEqualTo("thing")
+ }
+
+ @Test fun parseValueParamsSimple() {
+ val input = "(kotlin/Function1<#A, kotlin/Boolean>)"
+ val cursor = Cursor(input)
+ val valueParams = cursor.parseValueParameters()
+ assertThat(valueParams).hasSize(1)
+ }
+
+ @Test fun parseValueParamsTwoArgs() {
+ val input = "(#A1, kotlin/Function2<#A1, #A, #A1>)"
+ val cursor = Cursor(input)
+ val valueParams = cursor.parseValueParameters()
+ assertThat(valueParams).hasSize(2)
+ assertThat(valueParams?.first()?.type?.tag).isEqualTo("A1")
+ }
+
+ @Test
+ fun parseValueParamsWithHasDefaultArg() {
+ val input = "(kotlin/Int =...)"
+ val cursor = Cursor(input)
+ val valueParams = cursor.parseValueParameters()
+ assertThat(valueParams).hasSize(1)
+ assertThat(valueParams?.single()?.hasDefaultArg).isTrue()
+ }
+
+ @Test
+ fun parseValueParamsComplex2() {
+ val input = "(kotlin/Int, crossinline kotlin/Function2<#A, #B, kotlin/Int> =..., " +
+ "crossinline kotlin/Function1<#A, #B?> =..., " +
+ "crossinline kotlin/Function4<kotlin/Boolean, #A, #B, #B?, kotlin/Unit> =...)"
+ val cursor = Cursor(input)
+ val valueParams = cursor.parseValueParameters()!!
+ assertThat(valueParams).hasSize(4)
+ assertThat(valueParams.first().type.className?.toString()).isEqualTo("kotlin/Int")
+ val rest = valueParams.subList(1, valueParams.size)
+ assertThat(rest).hasSize(3)
+ assertThat(rest.all { it.hasDefaultArg }).isTrue()
+ assertThat(rest.all { it.isCrossinline }).isTrue()
+ }
+
+ @Test fun parseValueParamsComplex3() {
+ val input = "(kotlin/Array<out kotlin/Pair<#A, #B>>...)"
+ val cursor = Cursor(input)
+ val valueParams = cursor.parseValueParameters()!!
+ assertThat(valueParams).hasSize(1)
+
+ assertThat(valueParams.single().isVararg).isTrue()
+ val type = valueParams.single().type
+ assertThat(type.className.toString()).isEqualTo("kotlin/Array")
+ }
+
+ @Test fun parseTypeParams() {
+ val input = "<#A1: kotlin/Any?>"
+ val cursor = Cursor(input)
+ val typeParams = cursor.parseTypeParams()
+ assertThat(typeParams).hasSize(1)
+ val type = typeParams?.single()?.upperBounds?.single()
+ assertThat(typeParams?.single()?.tag).isEqualTo("A1")
+ assertThat(type?.className?.toString()).isEqualTo("kotlin/Any")
+ assertThat(type?.nullability).isEqualTo(AbiTypeNullability.MARKED_NULLABLE)
+ assertThat(typeParams?.single()?.variance).isEqualTo(AbiVariance.INVARIANT)
+ }
+
+ @Test fun parseTypeParamsWithVariance() {
+ val input = "<#A1: out kotlin/Any?>"
+ val cursor = Cursor(input)
+ val typeParams = cursor.parseTypeParams()
+ assertThat(typeParams).hasSize(1)
+ val type = typeParams?.single()?.upperBounds?.single()
+ assertThat(typeParams?.single()?.tag).isEqualTo("A1")
+ assertThat(type?.className?.toString()).isEqualTo("kotlin/Any")
+ assertThat(type?.nullability).isEqualTo(AbiTypeNullability.MARKED_NULLABLE)
+ assertThat(typeParams?.single()?.variance).isEqualTo(AbiVariance.OUT)
+ }
+
+ @Test fun parseTypeParamsWithTwo() {
+ val input = "<#A: kotlin/Any?, #B: kotlin/Any?>"
+ val cursor = Cursor(input)
+ val typeParams = cursor.parseTypeParams()
+ assertThat(typeParams).hasSize(2)
+ val type1 = typeParams?.first()?.upperBounds?.single()
+ val type2 = typeParams?.first()?.upperBounds?.single()
+ assertThat(typeParams?.first()?.tag).isEqualTo("A")
+ assertThat(typeParams?.last()?.tag).isEqualTo("B")
+ assertThat(type1?.className?.toString()).isEqualTo("kotlin/Any")
+ assertThat(type1?.nullability).isEqualTo(AbiTypeNullability.MARKED_NULLABLE)
+ assertThat(type2?.className?.toString()).isEqualTo("kotlin/Any")
+ assertThat(type2?.nullability).isEqualTo(AbiTypeNullability.MARKED_NULLABLE)
+ }
+
+ @Test fun parseTypeParamsReifed() {
+ val input = "<#A1: reified kotlin/Any?>"
+ val cursor = Cursor(input)
+ val typeParam = cursor.parseTypeParams()?.single()
+ assertThat(typeParam).isNotNull()
+ assertThat(typeParam?.isReified).isTrue()
+ }
+
+ @Test fun parseTypeParamsDoesNotMatchGetter() {
+ val input = "<get-size>"
+ val cursor = Cursor(input)
+ val typeParams = cursor.parseTypeParams()
+ assertThat(typeParams).isNull()
+ }
+
+ @Test
+ fun parseTypeArgs() {
+ val input = "<out #A>"
+ val cursor = Cursor(input)
+ val typeArgs = cursor.parseTypeArgs()
+ assertThat(typeArgs).hasSize(1)
+ val typeArg = typeArgs?.single()
+ assertThat(typeArg?.type?.tag).isEqualTo("A")
+ assertThat(typeArg?.variance).isEqualTo(AbiVariance.OUT)
+ }
+
+ @Test
+ fun parseTwoTypeArgs() {
+ val input = "<kotlin/Double, kotlin/Boolean>"
+ val cursor = Cursor(input)
+ val typeArgs = cursor.parseTypeArgs()
+ assertThat(typeArgs).hasSize(2)
+ assertThat(typeArgs?.first()?.type?.className?.toString()).isEqualTo("kotlin/Double")
+ assertThat(typeArgs?.last()?.type?.className?.toString()).isEqualTo("kotlin/Boolean")
+ }
+
+ @Test
+ fun parseTypeArgsWithNestedBrackets() {
+ val input = "<androidx.collection/ScatterMap<#A, #B>, androidx.collection/Other<#A, #B>>," +
+ " something else"
+ val cursor = Cursor(input)
+ val typeArgs = cursor.parseTypeArgs()
+ assertThat(typeArgs).hasSize(2)
+ assertThat(cursor.currentLine).isEqualTo(", something else")
+ }
+
+ @Test fun parseVarargSymbol() {
+ val input = "..."
+ val cursor = Cursor(input)
+ val vararg = cursor.parseVarargSymbol()
+ assertThat(vararg).isNotNull()
+ }
+
+ @Test fun parseTargets() {
+ val input = "Targets: [iosX64, linuxX64]"
+ val cursor = Cursor(input)
+ val targets = cursor.parseTargets()
+ assertThat(targets).containsExactly("linuxX64", "iosX64")
+ }
+}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/EmptyFragment.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/TestExtensions.kt
similarity index 74%
rename from privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/EmptyFragment.kt
rename to binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/TestExtensions.kt
index 3a27fde..65d449a 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/EmptyFragment.kt
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/TestExtensions.kt
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package androidx.privacysandbox.ui.integration.testapp
+package androidx.binarycompatibilityvalidator
-class EmptyFragment : BaseFragment() {
- override fun handleDrawerStateChange(isDrawerOpen: Boolean) {
- }
+import java.io.File
+import kotlin.io.path.Path
+
+fun getJavaResource(fileName: String): File {
+ return Path("src", "test", "resources", fileName).toFile()
}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/resources/collection.txt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/resources/collection.txt
new file mode 100644
index 0000000..c944f4d
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/resources/collection.txt
@@ -0,0 +1,2129 @@
+// KLib ABI Dump
+// Targets: [macosX64, macosX64.iosArm64, macosX64.iosSimulatorArm64, macosX64.iosX64, macosX64.linuxX64, macosX64.macosArm64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.collection:collection>
+final class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/MutableScatterMap : androidx.collection/ScatterMap<#A, #B> { // androidx.collection/MutableScatterMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableScatterMap.<init>|<init>(kotlin.Int){}[0]
+ final fun asMutableMap(): kotlin.collections/MutableMap<#A, #B> // androidx.collection/MutableScatterMap.asMutableMap|asMutableMap(){}[0]
+ final fun clear() // androidx.collection/MutableScatterMap.clear|clear(){}[0]
+ final fun findInsertIndex(#A): kotlin/Int // androidx.collection/MutableScatterMap.findInsertIndex|findInsertIndex(1:0){}[0]
+ final fun put(#A, #B): #B? // androidx.collection/MutableScatterMap.put|put(1:0;1:1){}[0]
+ final fun putAll(androidx.collection/ScatterMap<#A, #B>) // androidx.collection/MutableScatterMap.putAll|putAll(androidx.collection.ScatterMap<1:0,1:1>){}[0]
+ final fun putAll(kotlin.collections/Iterable<kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.putAll|putAll(kotlin.collections.Iterable<kotlin.Pair<1:0,1:1>>){}[0]
+ final fun putAll(kotlin.collections/Map<#A, #B>) // androidx.collection/MutableScatterMap.putAll|putAll(kotlin.collections.Map<1:0,1:1>){}[0]
+ final fun putAll(kotlin.sequences/Sequence<kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.putAll|putAll(kotlin.sequences.Sequence<kotlin.Pair<1:0,1:1>>){}[0]
+ final fun putAll(kotlin/Array<out kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.putAll|putAll(kotlin.Array<out|kotlin.Pair<1:0,1:1>>){}[0]
+ final fun remove(#A): #B? // androidx.collection/MutableScatterMap.remove|remove(1:0){}[0]
+ final fun remove(#A, #B): kotlin/Boolean // androidx.collection/MutableScatterMap.remove|remove(1:0;1:1){}[0]
+ final fun removeValueAt(kotlin/Int): #B? // androidx.collection/MutableScatterMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(#A, #B) // androidx.collection/MutableScatterMap.set|set(1:0;1:1){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableScatterMap.trim|trim(){}[0]
+ final inline fun compute(#A, kotlin/Function2<#A, #B?, #B>): #B // androidx.collection/MutableScatterMap.compute|compute(1:0;kotlin.Function2<1:0,1:1?,1:1>){}[0]
+ final inline fun getOrPut(#A, kotlin/Function0<#B>): #B // androidx.collection/MutableScatterMap.getOrPut|getOrPut(1:0;kotlin.Function0<1:1>){}[0]
+ final inline fun minusAssign(#A) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(1:0){}[0]
+ final inline fun minusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(androidx.collection.ObjectList<1:0>){}[0]
+ final inline fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+ final inline fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+ final inline fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+ final inline fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+ final inline fun plusAssign(androidx.collection/ScatterMap<#A, #B>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(androidx.collection.ScatterMap<1:0,1:1>){}[0]
+ final inline fun plusAssign(kotlin.collections/Iterable<kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.collections.Iterable<kotlin.Pair<1:0,1:1>>){}[0]
+ final inline fun plusAssign(kotlin.collections/Map<#A, #B>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.collections.Map<1:0,1:1>){}[0]
+ final inline fun plusAssign(kotlin.sequences/Sequence<kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.sequences.Sequence<kotlin.Pair<1:0,1:1>>){}[0]
+ final inline fun plusAssign(kotlin/Array<out kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.Array<out|kotlin.Pair<1:0,1:1>>){}[0]
+ final inline fun plusAssign(kotlin/Pair<#A, #B>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.Pair<1:0,1:1>){}[0]
+ final inline fun removeIf(kotlin/Function2<#A, #B, kotlin/Boolean>) // androidx.collection/MutableScatterMap.removeIf|removeIf(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/ArraySet : kotlin.collections/MutableCollection<#A>, kotlin.collections/MutableSet<#A> { // androidx.collection/ArraySet|null[0]
+ constructor <init>(androidx.collection/ArraySet<out #A>?) // androidx.collection/ArraySet.<init>|<init>(androidx.collection.ArraySet<out|1:0>?){}[0]
+ constructor <init>(kotlin.collections/Collection<#A>?) // androidx.collection/ArraySet.<init>|<init>(kotlin.collections.Collection<1:0>?){}[0]
+ constructor <init>(kotlin/Array<out #A>?) // androidx.collection/ArraySet.<init>|<init>(kotlin.Array<out|1:0>?){}[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/ArraySet.<init>|<init>(kotlin.Int){}[0]
+ final fun add(#A): kotlin/Boolean // androidx.collection/ArraySet.add|add(1:0){}[0]
+ final fun addAll(androidx.collection/ArraySet<out #A>) // androidx.collection/ArraySet.addAll|addAll(androidx.collection.ArraySet<out|1:0>){}[0]
+ final fun addAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/ArraySet.addAll|addAll(kotlin.collections.Collection<1:0>){}[0]
+ final fun clear() // androidx.collection/ArraySet.clear|clear(){}[0]
+ final fun contains(#A): kotlin/Boolean // androidx.collection/ArraySet.contains|contains(1:0){}[0]
+ final fun containsAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/ArraySet.containsAll|containsAll(kotlin.collections.Collection<1:0>){}[0]
+ final fun ensureCapacity(kotlin/Int) // androidx.collection/ArraySet.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+ final fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ArraySet.equals|equals(kotlin.Any?){}[0]
+ final fun hashCode(): kotlin/Int // androidx.collection/ArraySet.hashCode|hashCode(){}[0]
+ final fun indexOf(kotlin/Any?): kotlin/Int // androidx.collection/ArraySet.indexOf|indexOf(kotlin.Any?){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/ArraySet.isEmpty|isEmpty(){}[0]
+ final fun iterator(): kotlin.collections/MutableIterator<#A> // androidx.collection/ArraySet.iterator|iterator(){}[0]
+ final fun remove(#A): kotlin/Boolean // androidx.collection/ArraySet.remove|remove(1:0){}[0]
+ final fun removeAll(androidx.collection/ArraySet<out #A>): kotlin/Boolean // androidx.collection/ArraySet.removeAll|removeAll(androidx.collection.ArraySet<out|1:0>){}[0]
+ final fun removeAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/ArraySet.removeAll|removeAll(kotlin.collections.Collection<1:0>){}[0]
+ final fun removeAt(kotlin/Int): #A // androidx.collection/ArraySet.removeAt|removeAt(kotlin.Int){}[0]
+ final fun retainAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/ArraySet.retainAll|retainAll(kotlin.collections.Collection<1:0>){}[0]
+ final fun toString(): kotlin/String // androidx.collection/ArraySet.toString|toString(){}[0]
+ final fun valueAt(kotlin/Int): #A // androidx.collection/ArraySet.valueAt|valueAt(kotlin.Int){}[0]
+ final val size // androidx.collection/ArraySet.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/ArraySet.size.<get-size>|<get-size>(){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/CircularArray { // androidx.collection/CircularArray|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/CircularArray.<init>|<init>(kotlin.Int){}[0]
+ final fun addFirst(#A) // androidx.collection/CircularArray.addFirst|addFirst(1:0){}[0]
+ final fun addLast(#A) // androidx.collection/CircularArray.addLast|addLast(1:0){}[0]
+ final fun clear() // androidx.collection/CircularArray.clear|clear(){}[0]
+ final fun get(kotlin/Int): #A // androidx.collection/CircularArray.get|get(kotlin.Int){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/CircularArray.isEmpty|isEmpty(){}[0]
+ final fun popFirst(): #A // androidx.collection/CircularArray.popFirst|popFirst(){}[0]
+ final fun popLast(): #A // androidx.collection/CircularArray.popLast|popLast(){}[0]
+ final fun removeFromEnd(kotlin/Int) // androidx.collection/CircularArray.removeFromEnd|removeFromEnd(kotlin.Int){}[0]
+ final fun removeFromStart(kotlin/Int) // androidx.collection/CircularArray.removeFromStart|removeFromStart(kotlin.Int){}[0]
+ final fun size(): kotlin/Int // androidx.collection/CircularArray.size|size(){}[0]
+ final val first // androidx.collection/CircularArray.first|{}first[0]
+ final fun <get-first>(): #A // androidx.collection/CircularArray.first.<get-first>|<get-first>(){}[0]
+ final val last // androidx.collection/CircularArray.last|{}last[0]
+ final fun <get-last>(): #A // androidx.collection/CircularArray.last.<get-last>|<get-last>(){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableFloatObjectMap : androidx.collection/FloatObjectMap<#A> { // androidx.collection/MutableFloatObjectMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatObjectMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableFloatObjectMap.clear|clear(){}[0]
+ final fun put(kotlin/Float, #A): #A? // androidx.collection/MutableFloatObjectMap.put|put(kotlin.Float;1:0){}[0]
+ final fun putAll(androidx.collection/FloatObjectMap<#A>) // androidx.collection/MutableFloatObjectMap.putAll|putAll(androidx.collection.FloatObjectMap<1:0>){}[0]
+ final fun remove(kotlin/Float): #A? // androidx.collection/MutableFloatObjectMap.remove|remove(kotlin.Float){}[0]
+ final fun remove(kotlin/Float, #A): kotlin/Boolean // androidx.collection/MutableFloatObjectMap.remove|remove(kotlin.Float;1:0){}[0]
+ final fun removeValueAt(kotlin/Int): #A? // androidx.collection/MutableFloatObjectMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Float, #A) // androidx.collection/MutableFloatObjectMap.set|set(kotlin.Float;1:0){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableFloatObjectMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Float, kotlin/Function0<#A>): #A // androidx.collection/MutableFloatObjectMap.getOrPut|getOrPut(kotlin.Float;kotlin.Function0<1:0>){}[0]
+ final inline fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatObjectMap.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+ final inline fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatObjectMap.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+ final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatObjectMap.minusAssign|minusAssign(kotlin.Float){}[0]
+ final inline fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatObjectMap.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+ final inline fun plusAssign(androidx.collection/FloatObjectMap<#A>) // androidx.collection/MutableFloatObjectMap.plusAssign|plusAssign(androidx.collection.FloatObjectMap<1:0>){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Float, #A, kotlin/Boolean>) // androidx.collection/MutableFloatObjectMap.removeIf|removeIf(kotlin.Function2<kotlin.Float,1:0,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableIntObjectMap : androidx.collection/IntObjectMap<#A> { // androidx.collection/MutableIntObjectMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntObjectMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableIntObjectMap.clear|clear(){}[0]
+ final fun put(kotlin/Int, #A): #A? // androidx.collection/MutableIntObjectMap.put|put(kotlin.Int;1:0){}[0]
+ final fun putAll(androidx.collection/IntObjectMap<#A>) // androidx.collection/MutableIntObjectMap.putAll|putAll(androidx.collection.IntObjectMap<1:0>){}[0]
+ final fun remove(kotlin/Int): #A? // androidx.collection/MutableIntObjectMap.remove|remove(kotlin.Int){}[0]
+ final fun remove(kotlin/Int, #A): kotlin/Boolean // androidx.collection/MutableIntObjectMap.remove|remove(kotlin.Int;1:0){}[0]
+ final fun removeValueAt(kotlin/Int): #A? // androidx.collection/MutableIntObjectMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Int, #A) // androidx.collection/MutableIntObjectMap.set|set(kotlin.Int;1:0){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableIntObjectMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Int, kotlin/Function0<#A>): #A // androidx.collection/MutableIntObjectMap.getOrPut|getOrPut(kotlin.Int;kotlin.Function0<1:0>){}[0]
+ final inline fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntObjectMap.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+ final inline fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntObjectMap.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+ final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntObjectMap.minusAssign|minusAssign(kotlin.Int){}[0]
+ final inline fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntObjectMap.minusAssign|minusAssign(kotlin.IntArray){}[0]
+ final inline fun plusAssign(androidx.collection/IntObjectMap<#A>) // androidx.collection/MutableIntObjectMap.plusAssign|plusAssign(androidx.collection.IntObjectMap<1:0>){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Int, #A, kotlin/Boolean>) // androidx.collection/MutableIntObjectMap.removeIf|removeIf(kotlin.Function2<kotlin.Int,1:0,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableLongObjectMap : androidx.collection/LongObjectMap<#A> { // androidx.collection/MutableLongObjectMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongObjectMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableLongObjectMap.clear|clear(){}[0]
+ final fun put(kotlin/Long, #A): #A? // androidx.collection/MutableLongObjectMap.put|put(kotlin.Long;1:0){}[0]
+ final fun putAll(androidx.collection/LongObjectMap<#A>) // androidx.collection/MutableLongObjectMap.putAll|putAll(androidx.collection.LongObjectMap<1:0>){}[0]
+ final fun remove(kotlin/Long): #A? // androidx.collection/MutableLongObjectMap.remove|remove(kotlin.Long){}[0]
+ final fun remove(kotlin/Long, #A): kotlin/Boolean // androidx.collection/MutableLongObjectMap.remove|remove(kotlin.Long;1:0){}[0]
+ final fun removeValueAt(kotlin/Int): #A? // androidx.collection/MutableLongObjectMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Long, #A) // androidx.collection/MutableLongObjectMap.set|set(kotlin.Long;1:0){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableLongObjectMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Long, kotlin/Function0<#A>): #A // androidx.collection/MutableLongObjectMap.getOrPut|getOrPut(kotlin.Long;kotlin.Function0<1:0>){}[0]
+ final inline fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongObjectMap.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+ final inline fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongObjectMap.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+ final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongObjectMap.minusAssign|minusAssign(kotlin.Long){}[0]
+ final inline fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongObjectMap.minusAssign|minusAssign(kotlin.LongArray){}[0]
+ final inline fun plusAssign(androidx.collection/LongObjectMap<#A>) // androidx.collection/MutableLongObjectMap.plusAssign|plusAssign(androidx.collection.LongObjectMap<1:0>){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Long, #A, kotlin/Boolean>) // androidx.collection/MutableLongObjectMap.removeIf|removeIf(kotlin.Function2<kotlin.Long,1:0,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableObjectFloatMap : androidx.collection/ObjectFloatMap<#A> { // androidx.collection/MutableObjectFloatMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableObjectFloatMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableObjectFloatMap.clear|clear(){}[0]
+ final fun put(#A, kotlin/Float) // androidx.collection/MutableObjectFloatMap.put|put(1:0;kotlin.Float){}[0]
+ final fun put(#A, kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/MutableObjectFloatMap.put|put(1:0;kotlin.Float;kotlin.Float){}[0]
+ final fun putAll(androidx.collection/ObjectFloatMap<#A>) // androidx.collection/MutableObjectFloatMap.putAll|putAll(androidx.collection.ObjectFloatMap<1:0>){}[0]
+ final fun remove(#A) // androidx.collection/MutableObjectFloatMap.remove|remove(1:0){}[0]
+ final fun remove(#A, kotlin/Float): kotlin/Boolean // androidx.collection/MutableObjectFloatMap.remove|remove(1:0;kotlin.Float){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableObjectFloatMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(#A, kotlin/Float) // androidx.collection/MutableObjectFloatMap.set|set(1:0;kotlin.Float){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableObjectFloatMap.trim|trim(){}[0]
+ final inline fun getOrPut(#A, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/MutableObjectFloatMap.getOrPut|getOrPut(1:0;kotlin.Function0<kotlin.Float>){}[0]
+ final inline fun minusAssign(#A) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(1:0){}[0]
+ final inline fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+ final inline fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+ final inline fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+ final inline fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+ final inline fun plusAssign(androidx.collection/ObjectFloatMap<#A>) // androidx.collection/MutableObjectFloatMap.plusAssign|plusAssign(androidx.collection.ObjectFloatMap<1:0>){}[0]
+ final inline fun removeIf(kotlin/Function2<#A, kotlin/Float, kotlin/Boolean>) // androidx.collection/MutableObjectFloatMap.removeIf|removeIf(kotlin.Function2<1:0,kotlin.Float,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableObjectIntMap : androidx.collection/ObjectIntMap<#A> { // androidx.collection/MutableObjectIntMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableObjectIntMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableObjectIntMap.clear|clear(){}[0]
+ final fun put(#A, kotlin/Int) // androidx.collection/MutableObjectIntMap.put|put(1:0;kotlin.Int){}[0]
+ final fun put(#A, kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableObjectIntMap.put|put(1:0;kotlin.Int;kotlin.Int){}[0]
+ final fun putAll(androidx.collection/ObjectIntMap<#A>) // androidx.collection/MutableObjectIntMap.putAll|putAll(androidx.collection.ObjectIntMap<1:0>){}[0]
+ final fun remove(#A) // androidx.collection/MutableObjectIntMap.remove|remove(1:0){}[0]
+ final fun remove(#A, kotlin/Int): kotlin/Boolean // androidx.collection/MutableObjectIntMap.remove|remove(1:0;kotlin.Int){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableObjectIntMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(#A, kotlin/Int) // androidx.collection/MutableObjectIntMap.set|set(1:0;kotlin.Int){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableObjectIntMap.trim|trim(){}[0]
+ final inline fun getOrPut(#A, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/MutableObjectIntMap.getOrPut|getOrPut(1:0;kotlin.Function0<kotlin.Int>){}[0]
+ final inline fun minusAssign(#A) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(1:0){}[0]
+ final inline fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+ final inline fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+ final inline fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+ final inline fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+ final inline fun plusAssign(androidx.collection/ObjectIntMap<#A>) // androidx.collection/MutableObjectIntMap.plusAssign|plusAssign(androidx.collection.ObjectIntMap<1:0>){}[0]
+ final inline fun removeIf(kotlin/Function2<#A, kotlin/Int, kotlin/Boolean>) // androidx.collection/MutableObjectIntMap.removeIf|removeIf(kotlin.Function2<1:0,kotlin.Int,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableObjectList : androidx.collection/ObjectList<#A> { // androidx.collection/MutableObjectList|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableObjectList.<init>|<init>(kotlin.Int){}[0]
+ final fun add(#A): kotlin/Boolean // androidx.collection/MutableObjectList.add|add(1:0){}[0]
+ final fun add(kotlin/Int, #A) // androidx.collection/MutableObjectList.add|add(kotlin.Int;1:0){}[0]
+ final fun addAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(androidx.collection.ObjectList<1:0>){}[0]
+ final fun addAll(androidx.collection/ScatterSet<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(androidx.collection.ScatterSet<1:0>){}[0]
+ final fun addAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.collections.Iterable<1:0>){}[0]
+ final fun addAll(kotlin.collections/List<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.collections.List<1:0>){}[0]
+ final fun addAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.sequences.Sequence<1:0>){}[0]
+ final fun addAll(kotlin/Array<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.Array<1:0>){}[0]
+ final fun addAll(kotlin/Int, androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.Int;androidx.collection.ObjectList<1:0>){}[0]
+ final fun addAll(kotlin/Int, kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.Int;kotlin.collections.Collection<1:0>){}[0]
+ final fun addAll(kotlin/Int, kotlin/Array<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.Int;kotlin.Array<1:0>){}[0]
+ final fun asList(): kotlin.collections/List<#A> // androidx.collection/MutableObjectList.asList|asList(){}[0]
+ final fun asMutableList(): kotlin.collections/MutableList<#A> // androidx.collection/MutableObjectList.asMutableList|asMutableList(){}[0]
+ final fun clear() // androidx.collection/MutableObjectList.clear|clear(){}[0]
+ final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableObjectList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+ final fun minusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(androidx.collection.ObjectList<1:0>){}[0]
+ final fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+ final fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+ final fun minusAssign(kotlin.collections/List<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(kotlin.collections.List<1:0>){}[0]
+ final fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+ final fun minusAssign(kotlin/Array<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(kotlin.Array<1:0>){}[0]
+ final fun plusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(androidx.collection.ObjectList<1:0>){}[0]
+ final fun plusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+ final fun plusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(kotlin.collections.Iterable<1:0>){}[0]
+ final fun plusAssign(kotlin.collections/List<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(kotlin.collections.List<1:0>){}[0]
+ final fun plusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+ final fun plusAssign(kotlin/Array<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(kotlin.Array<1:0>){}[0]
+ final fun remove(#A): kotlin/Boolean // androidx.collection/MutableObjectList.remove|remove(1:0){}[0]
+ final fun removeAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(androidx.collection.ObjectList<1:0>){}[0]
+ final fun removeAll(androidx.collection/ScatterSet<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(androidx.collection.ScatterSet<1:0>){}[0]
+ final fun removeAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(kotlin.collections.Iterable<1:0>){}[0]
+ final fun removeAll(kotlin.collections/List<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(kotlin.collections.List<1:0>){}[0]
+ final fun removeAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(kotlin.sequences.Sequence<1:0>){}[0]
+ final fun removeAll(kotlin/Array<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(kotlin.Array<1:0>){}[0]
+ final fun removeAt(kotlin/Int): #A // androidx.collection/MutableObjectList.removeAt|removeAt(kotlin.Int){}[0]
+ final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableObjectList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+ final fun retainAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(androidx.collection.ObjectList<1:0>){}[0]
+ final fun retainAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(kotlin.collections.Collection<1:0>){}[0]
+ final fun retainAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(kotlin.collections.Iterable<1:0>){}[0]
+ final fun retainAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(kotlin.sequences.Sequence<1:0>){}[0]
+ final fun retainAll(kotlin/Array<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(kotlin.Array<1:0>){}[0]
+ final fun set(kotlin/Int, #A): #A // androidx.collection/MutableObjectList.set|set(kotlin.Int;1:0){}[0]
+ final fun trim(kotlin/Int =...) // androidx.collection/MutableObjectList.trim|trim(kotlin.Int){}[0]
+ final inline fun minusAssign(#A) // androidx.collection/MutableObjectList.minusAssign|minusAssign(1:0){}[0]
+ final inline fun plusAssign(#A) // androidx.collection/MutableObjectList.plusAssign|plusAssign(1:0){}[0]
+ final inline fun removeIf(kotlin/Function1<#A, kotlin/Boolean>) // androidx.collection/MutableObjectList.removeIf|removeIf(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final val capacity // androidx.collection/MutableObjectList.capacity|{}capacity[0]
+ final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableObjectList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableObjectLongMap : androidx.collection/ObjectLongMap<#A> { // androidx.collection/MutableObjectLongMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableObjectLongMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableObjectLongMap.clear|clear(){}[0]
+ final fun put(#A, kotlin/Long) // androidx.collection/MutableObjectLongMap.put|put(1:0;kotlin.Long){}[0]
+ final fun put(#A, kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/MutableObjectLongMap.put|put(1:0;kotlin.Long;kotlin.Long){}[0]
+ final fun putAll(androidx.collection/ObjectLongMap<#A>) // androidx.collection/MutableObjectLongMap.putAll|putAll(androidx.collection.ObjectLongMap<1:0>){}[0]
+ final fun remove(#A) // androidx.collection/MutableObjectLongMap.remove|remove(1:0){}[0]
+ final fun remove(#A, kotlin/Long): kotlin/Boolean // androidx.collection/MutableObjectLongMap.remove|remove(1:0;kotlin.Long){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableObjectLongMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(#A, kotlin/Long) // androidx.collection/MutableObjectLongMap.set|set(1:0;kotlin.Long){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableObjectLongMap.trim|trim(){}[0]
+ final inline fun getOrPut(#A, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/MutableObjectLongMap.getOrPut|getOrPut(1:0;kotlin.Function0<kotlin.Long>){}[0]
+ final inline fun minusAssign(#A) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(1:0){}[0]
+ final inline fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+ final inline fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+ final inline fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+ final inline fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+ final inline fun plusAssign(androidx.collection/ObjectLongMap<#A>) // androidx.collection/MutableObjectLongMap.plusAssign|plusAssign(androidx.collection.ObjectLongMap<1:0>){}[0]
+ final inline fun removeIf(kotlin/Function2<#A, kotlin/Long, kotlin/Boolean>) // androidx.collection/MutableObjectLongMap.removeIf|removeIf(kotlin.Function2<1:0,kotlin.Long,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableScatterSet : androidx.collection/ScatterSet<#A> { // androidx.collection/MutableScatterSet|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableScatterSet.<init>|<init>(kotlin.Int){}[0]
+ final fun add(#A): kotlin/Boolean // androidx.collection/MutableScatterSet.add|add(1:0){}[0]
+ final fun addAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(androidx.collection.ObjectList<1:0>){}[0]
+ final fun addAll(androidx.collection/ScatterSet<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(androidx.collection.ScatterSet<1:0>){}[0]
+ final fun addAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(kotlin.collections.Iterable<1:0>){}[0]
+ final fun addAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(kotlin.sequences.Sequence<1:0>){}[0]
+ final fun addAll(kotlin/Array<out #A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(kotlin.Array<out|1:0>){}[0]
+ final fun asMutableSet(): kotlin.collections/MutableSet<#A> // androidx.collection/MutableScatterSet.asMutableSet|asMutableSet(){}[0]
+ final fun clear() // androidx.collection/MutableScatterSet.clear|clear(){}[0]
+ final fun minusAssign(#A) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(1:0){}[0]
+ final fun minusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(androidx.collection.ObjectList<1:0>){}[0]
+ final fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+ final fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+ final fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+ final fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+ final fun plusAssign(#A) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(1:0){}[0]
+ final fun plusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(androidx.collection.ObjectList<1:0>){}[0]
+ final fun plusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+ final fun plusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(kotlin.collections.Iterable<1:0>){}[0]
+ final fun plusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+ final fun plusAssign(kotlin/Array<out #A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(kotlin.Array<out|1:0>){}[0]
+ final fun remove(#A): kotlin/Boolean // androidx.collection/MutableScatterSet.remove|remove(1:0){}[0]
+ final fun removeAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(androidx.collection.ObjectList<1:0>){}[0]
+ final fun removeAll(androidx.collection/ScatterSet<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(androidx.collection.ScatterSet<1:0>){}[0]
+ final fun removeAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(kotlin.collections.Iterable<1:0>){}[0]
+ final fun removeAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(kotlin.sequences.Sequence<1:0>){}[0]
+ final fun removeAll(kotlin/Array<out #A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(kotlin.Array<out|1:0>){}[0]
+ final fun removeElementAt(kotlin/Int) // androidx.collection/MutableScatterSet.removeElementAt|removeElementAt(kotlin.Int){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableScatterSet.trim|trim(){}[0]
+ final inline fun removeIf(kotlin/Function1<#A, kotlin/Boolean>) // androidx.collection/MutableScatterSet.removeIf|removeIf(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/CircularIntArray { // androidx.collection/CircularIntArray|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/CircularIntArray.<init>|<init>(kotlin.Int){}[0]
+ final fun addFirst(kotlin/Int) // androidx.collection/CircularIntArray.addFirst|addFirst(kotlin.Int){}[0]
+ final fun addLast(kotlin/Int) // androidx.collection/CircularIntArray.addLast|addLast(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/CircularIntArray.clear|clear(){}[0]
+ final fun get(kotlin/Int): kotlin/Int // androidx.collection/CircularIntArray.get|get(kotlin.Int){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/CircularIntArray.isEmpty|isEmpty(){}[0]
+ final fun popFirst(): kotlin/Int // androidx.collection/CircularIntArray.popFirst|popFirst(){}[0]
+ final fun popLast(): kotlin/Int // androidx.collection/CircularIntArray.popLast|popLast(){}[0]
+ final fun removeFromEnd(kotlin/Int) // androidx.collection/CircularIntArray.removeFromEnd|removeFromEnd(kotlin.Int){}[0]
+ final fun removeFromStart(kotlin/Int) // androidx.collection/CircularIntArray.removeFromStart|removeFromStart(kotlin.Int){}[0]
+ final fun size(): kotlin/Int // androidx.collection/CircularIntArray.size|size(){}[0]
+ final val first // androidx.collection/CircularIntArray.first|{}first[0]
+ final fun <get-first>(): kotlin/Int // androidx.collection/CircularIntArray.first.<get-first>|<get-first>(){}[0]
+ final val last // androidx.collection/CircularIntArray.last|{}last[0]
+ final fun <get-last>(): kotlin/Int // androidx.collection/CircularIntArray.last.<get-last>|<get-last>(){}[0]
+}
+final class androidx.collection/LongLongPair { // androidx.collection/LongLongPair|null[0]
+ constructor <init>(kotlin/Long, kotlin/Long) // androidx.collection/LongLongPair.<init>|<init>(kotlin.Long;kotlin.Long){}[0]
+ final fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongLongPair.equals|equals(kotlin.Any?){}[0]
+ final fun hashCode(): kotlin/Int // androidx.collection/LongLongPair.hashCode|hashCode(){}[0]
+ final fun toString(): kotlin/String // androidx.collection/LongLongPair.toString|toString(){}[0]
+ final inline fun component1(): kotlin/Long // androidx.collection/LongLongPair.component1|component1(){}[0]
+ final inline fun component2(): kotlin/Long // androidx.collection/LongLongPair.component2|component2(){}[0]
+ final val first // androidx.collection/LongLongPair.first|{}first[0]
+ final fun <get-first>(): kotlin/Long // androidx.collection/LongLongPair.first.<get-first>|<get-first>(){}[0]
+ final val second // androidx.collection/LongLongPair.second|{}second[0]
+ final fun <get-second>(): kotlin/Long // androidx.collection/LongLongPair.second.<get-second>|<get-second>(){}[0]
+}
+final class androidx.collection/MutableDoubleList : androidx.collection/DoubleList { // androidx.collection/MutableDoubleList|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableDoubleList.<init>|<init>(kotlin.Int){}[0]
+ final fun add(kotlin/Double): kotlin/Boolean // androidx.collection/MutableDoubleList.add|add(kotlin.Double){}[0]
+ final fun add(kotlin/Int, kotlin/Double) // androidx.collection/MutableDoubleList.add|add(kotlin.Int;kotlin.Double){}[0]
+ final fun addAll(androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/MutableDoubleList.addAll|addAll(androidx.collection.DoubleList){}[0]
+ final fun addAll(kotlin/DoubleArray): kotlin/Boolean // androidx.collection/MutableDoubleList.addAll|addAll(kotlin.DoubleArray){}[0]
+ final fun addAll(kotlin/Int, androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/MutableDoubleList.addAll|addAll(kotlin.Int;androidx.collection.DoubleList){}[0]
+ final fun addAll(kotlin/Int, kotlin/DoubleArray): kotlin/Boolean // androidx.collection/MutableDoubleList.addAll|addAll(kotlin.Int;kotlin.DoubleArray){}[0]
+ final fun clear() // androidx.collection/MutableDoubleList.clear|clear(){}[0]
+ final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableDoubleList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+ final fun minusAssign(androidx.collection/DoubleList) // androidx.collection/MutableDoubleList.minusAssign|minusAssign(androidx.collection.DoubleList){}[0]
+ final fun minusAssign(kotlin/DoubleArray) // androidx.collection/MutableDoubleList.minusAssign|minusAssign(kotlin.DoubleArray){}[0]
+ final fun plusAssign(androidx.collection/DoubleList) // androidx.collection/MutableDoubleList.plusAssign|plusAssign(androidx.collection.DoubleList){}[0]
+ final fun plusAssign(kotlin/DoubleArray) // androidx.collection/MutableDoubleList.plusAssign|plusAssign(kotlin.DoubleArray){}[0]
+ final fun remove(kotlin/Double): kotlin/Boolean // androidx.collection/MutableDoubleList.remove|remove(kotlin.Double){}[0]
+ final fun removeAll(androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/MutableDoubleList.removeAll|removeAll(androidx.collection.DoubleList){}[0]
+ final fun removeAll(kotlin/DoubleArray): kotlin/Boolean // androidx.collection/MutableDoubleList.removeAll|removeAll(kotlin.DoubleArray){}[0]
+ final fun removeAt(kotlin/Int): kotlin/Double // androidx.collection/MutableDoubleList.removeAt|removeAt(kotlin.Int){}[0]
+ final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableDoubleList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+ final fun retainAll(androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/MutableDoubleList.retainAll|retainAll(androidx.collection.DoubleList){}[0]
+ final fun retainAll(kotlin/DoubleArray): kotlin/Boolean // androidx.collection/MutableDoubleList.retainAll|retainAll(kotlin.DoubleArray){}[0]
+ final fun set(kotlin/Int, kotlin/Double): kotlin/Double // androidx.collection/MutableDoubleList.set|set(kotlin.Int;kotlin.Double){}[0]
+ final fun sort() // androidx.collection/MutableDoubleList.sort|sort(){}[0]
+ final fun sortDescending() // androidx.collection/MutableDoubleList.sortDescending|sortDescending(){}[0]
+ final fun trim(kotlin/Int =...) // androidx.collection/MutableDoubleList.trim|trim(kotlin.Int){}[0]
+ final inline fun minusAssign(kotlin/Double) // androidx.collection/MutableDoubleList.minusAssign|minusAssign(kotlin.Double){}[0]
+ final inline fun plusAssign(kotlin/Double) // androidx.collection/MutableDoubleList.plusAssign|plusAssign(kotlin.Double){}[0]
+ final val capacity // androidx.collection/MutableDoubleList.capacity|{}capacity[0]
+ final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableDoubleList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class androidx.collection/MutableFloatFloatMap : androidx.collection/FloatFloatMap { // androidx.collection/MutableFloatFloatMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatFloatMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableFloatFloatMap.clear|clear(){}[0]
+ final fun put(kotlin/Float, kotlin/Float) // androidx.collection/MutableFloatFloatMap.put|put(kotlin.Float;kotlin.Float){}[0]
+ final fun put(kotlin/Float, kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/MutableFloatFloatMap.put|put(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+ final fun putAll(androidx.collection/FloatFloatMap) // androidx.collection/MutableFloatFloatMap.putAll|putAll(androidx.collection.FloatFloatMap){}[0]
+ final fun remove(kotlin/Float) // androidx.collection/MutableFloatFloatMap.remove|remove(kotlin.Float){}[0]
+ final fun remove(kotlin/Float, kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatFloatMap.remove|remove(kotlin.Float;kotlin.Float){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableFloatFloatMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Float, kotlin/Float) // androidx.collection/MutableFloatFloatMap.set|set(kotlin.Float;kotlin.Float){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableFloatFloatMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Float, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/MutableFloatFloatMap.getOrPut|getOrPut(kotlin.Float;kotlin.Function0<kotlin.Float>){}[0]
+ final inline fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatFloatMap.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+ final inline fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatFloatMap.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+ final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatFloatMap.minusAssign|minusAssign(kotlin.Float){}[0]
+ final inline fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatFloatMap.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+ final inline fun plusAssign(androidx.collection/FloatFloatMap) // androidx.collection/MutableFloatFloatMap.plusAssign|plusAssign(androidx.collection.FloatFloatMap){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Boolean>) // androidx.collection/MutableFloatFloatMap.removeIf|removeIf(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableFloatIntMap : androidx.collection/FloatIntMap { // androidx.collection/MutableFloatIntMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatIntMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableFloatIntMap.clear|clear(){}[0]
+ final fun put(kotlin/Float, kotlin/Int) // androidx.collection/MutableFloatIntMap.put|put(kotlin.Float;kotlin.Int){}[0]
+ final fun put(kotlin/Float, kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableFloatIntMap.put|put(kotlin.Float;kotlin.Int;kotlin.Int){}[0]
+ final fun putAll(androidx.collection/FloatIntMap) // androidx.collection/MutableFloatIntMap.putAll|putAll(androidx.collection.FloatIntMap){}[0]
+ final fun remove(kotlin/Float) // androidx.collection/MutableFloatIntMap.remove|remove(kotlin.Float){}[0]
+ final fun remove(kotlin/Float, kotlin/Int): kotlin/Boolean // androidx.collection/MutableFloatIntMap.remove|remove(kotlin.Float;kotlin.Int){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableFloatIntMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Float, kotlin/Int) // androidx.collection/MutableFloatIntMap.set|set(kotlin.Float;kotlin.Int){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableFloatIntMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Float, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/MutableFloatIntMap.getOrPut|getOrPut(kotlin.Float;kotlin.Function0<kotlin.Int>){}[0]
+ final inline fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatIntMap.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+ final inline fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatIntMap.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+ final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatIntMap.minusAssign|minusAssign(kotlin.Float){}[0]
+ final inline fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatIntMap.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+ final inline fun plusAssign(androidx.collection/FloatIntMap) // androidx.collection/MutableFloatIntMap.plusAssign|plusAssign(androidx.collection.FloatIntMap){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Boolean>) // androidx.collection/MutableFloatIntMap.removeIf|removeIf(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableFloatList : androidx.collection/FloatList { // androidx.collection/MutableFloatList|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatList.<init>|<init>(kotlin.Int){}[0]
+ final fun add(kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatList.add|add(kotlin.Float){}[0]
+ final fun add(kotlin/Int, kotlin/Float) // androidx.collection/MutableFloatList.add|add(kotlin.Int;kotlin.Float){}[0]
+ final fun addAll(androidx.collection/FloatList): kotlin/Boolean // androidx.collection/MutableFloatList.addAll|addAll(androidx.collection.FloatList){}[0]
+ final fun addAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatList.addAll|addAll(kotlin.FloatArray){}[0]
+ final fun addAll(kotlin/Int, androidx.collection/FloatList): kotlin/Boolean // androidx.collection/MutableFloatList.addAll|addAll(kotlin.Int;androidx.collection.FloatList){}[0]
+ final fun addAll(kotlin/Int, kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatList.addAll|addAll(kotlin.Int;kotlin.FloatArray){}[0]
+ final fun clear() // androidx.collection/MutableFloatList.clear|clear(){}[0]
+ final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableFloatList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+ final fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatList.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+ final fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatList.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+ final fun plusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatList.plusAssign|plusAssign(androidx.collection.FloatList){}[0]
+ final fun plusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatList.plusAssign|plusAssign(kotlin.FloatArray){}[0]
+ final fun remove(kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatList.remove|remove(kotlin.Float){}[0]
+ final fun removeAll(androidx.collection/FloatList): kotlin/Boolean // androidx.collection/MutableFloatList.removeAll|removeAll(androidx.collection.FloatList){}[0]
+ final fun removeAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatList.removeAll|removeAll(kotlin.FloatArray){}[0]
+ final fun removeAt(kotlin/Int): kotlin/Float // androidx.collection/MutableFloatList.removeAt|removeAt(kotlin.Int){}[0]
+ final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableFloatList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+ final fun retainAll(androidx.collection/FloatList): kotlin/Boolean // androidx.collection/MutableFloatList.retainAll|retainAll(androidx.collection.FloatList){}[0]
+ final fun retainAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatList.retainAll|retainAll(kotlin.FloatArray){}[0]
+ final fun set(kotlin/Int, kotlin/Float): kotlin/Float // androidx.collection/MutableFloatList.set|set(kotlin.Int;kotlin.Float){}[0]
+ final fun sort() // androidx.collection/MutableFloatList.sort|sort(){}[0]
+ final fun sortDescending() // androidx.collection/MutableFloatList.sortDescending|sortDescending(){}[0]
+ final fun trim(kotlin/Int =...) // androidx.collection/MutableFloatList.trim|trim(kotlin.Int){}[0]
+ final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatList.minusAssign|minusAssign(kotlin.Float){}[0]
+ final inline fun plusAssign(kotlin/Float) // androidx.collection/MutableFloatList.plusAssign|plusAssign(kotlin.Float){}[0]
+ final val capacity // androidx.collection/MutableFloatList.capacity|{}capacity[0]
+ final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableFloatList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class androidx.collection/MutableFloatLongMap : androidx.collection/FloatLongMap { // androidx.collection/MutableFloatLongMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatLongMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableFloatLongMap.clear|clear(){}[0]
+ final fun put(kotlin/Float, kotlin/Long) // androidx.collection/MutableFloatLongMap.put|put(kotlin.Float;kotlin.Long){}[0]
+ final fun put(kotlin/Float, kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/MutableFloatLongMap.put|put(kotlin.Float;kotlin.Long;kotlin.Long){}[0]
+ final fun putAll(androidx.collection/FloatLongMap) // androidx.collection/MutableFloatLongMap.putAll|putAll(androidx.collection.FloatLongMap){}[0]
+ final fun remove(kotlin/Float) // androidx.collection/MutableFloatLongMap.remove|remove(kotlin.Float){}[0]
+ final fun remove(kotlin/Float, kotlin/Long): kotlin/Boolean // androidx.collection/MutableFloatLongMap.remove|remove(kotlin.Float;kotlin.Long){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableFloatLongMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Float, kotlin/Long) // androidx.collection/MutableFloatLongMap.set|set(kotlin.Float;kotlin.Long){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableFloatLongMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Float, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/MutableFloatLongMap.getOrPut|getOrPut(kotlin.Float;kotlin.Function0<kotlin.Long>){}[0]
+ final inline fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatLongMap.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+ final inline fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatLongMap.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+ final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatLongMap.minusAssign|minusAssign(kotlin.Float){}[0]
+ final inline fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatLongMap.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+ final inline fun plusAssign(androidx.collection/FloatLongMap) // androidx.collection/MutableFloatLongMap.plusAssign|plusAssign(androidx.collection.FloatLongMap){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Boolean>) // androidx.collection/MutableFloatLongMap.removeIf|removeIf(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableFloatSet : androidx.collection/FloatSet { // androidx.collection/MutableFloatSet|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatSet.<init>|<init>(kotlin.Int){}[0]
+ final fun add(kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatSet.add|add(kotlin.Float){}[0]
+ final fun addAll(androidx.collection/FloatSet): kotlin/Boolean // androidx.collection/MutableFloatSet.addAll|addAll(androidx.collection.FloatSet){}[0]
+ final fun addAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatSet.addAll|addAll(kotlin.FloatArray){}[0]
+ final fun clear() // androidx.collection/MutableFloatSet.clear|clear(){}[0]
+ final fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatSet.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+ final fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatSet.minusAssign|minusAssign(kotlin.Float){}[0]
+ final fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatSet.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+ final fun plusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatSet.plusAssign|plusAssign(androidx.collection.FloatSet){}[0]
+ final fun plusAssign(kotlin/Float) // androidx.collection/MutableFloatSet.plusAssign|plusAssign(kotlin.Float){}[0]
+ final fun plusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatSet.plusAssign|plusAssign(kotlin.FloatArray){}[0]
+ final fun remove(kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatSet.remove|remove(kotlin.Float){}[0]
+ final fun removeAll(androidx.collection/FloatSet): kotlin/Boolean // androidx.collection/MutableFloatSet.removeAll|removeAll(androidx.collection.FloatSet){}[0]
+ final fun removeAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatSet.removeAll|removeAll(kotlin.FloatArray){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableFloatSet.trim|trim(){}[0]
+}
+final class androidx.collection/MutableIntFloatMap : androidx.collection/IntFloatMap { // androidx.collection/MutableIntFloatMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntFloatMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableIntFloatMap.clear|clear(){}[0]
+ final fun put(kotlin/Int, kotlin/Float) // androidx.collection/MutableIntFloatMap.put|put(kotlin.Int;kotlin.Float){}[0]
+ final fun put(kotlin/Int, kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/MutableIntFloatMap.put|put(kotlin.Int;kotlin.Float;kotlin.Float){}[0]
+ final fun putAll(androidx.collection/IntFloatMap) // androidx.collection/MutableIntFloatMap.putAll|putAll(androidx.collection.IntFloatMap){}[0]
+ final fun remove(kotlin/Int) // androidx.collection/MutableIntFloatMap.remove|remove(kotlin.Int){}[0]
+ final fun remove(kotlin/Int, kotlin/Float): kotlin/Boolean // androidx.collection/MutableIntFloatMap.remove|remove(kotlin.Int;kotlin.Float){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableIntFloatMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Int, kotlin/Float) // androidx.collection/MutableIntFloatMap.set|set(kotlin.Int;kotlin.Float){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableIntFloatMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Int, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/MutableIntFloatMap.getOrPut|getOrPut(kotlin.Int;kotlin.Function0<kotlin.Float>){}[0]
+ final inline fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntFloatMap.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+ final inline fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntFloatMap.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+ final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntFloatMap.minusAssign|minusAssign(kotlin.Int){}[0]
+ final inline fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntFloatMap.minusAssign|minusAssign(kotlin.IntArray){}[0]
+ final inline fun plusAssign(androidx.collection/IntFloatMap) // androidx.collection/MutableIntFloatMap.plusAssign|plusAssign(androidx.collection.IntFloatMap){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Boolean>) // androidx.collection/MutableIntFloatMap.removeIf|removeIf(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableIntIntMap : androidx.collection/IntIntMap { // androidx.collection/MutableIntIntMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntIntMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableIntIntMap.clear|clear(){}[0]
+ final fun put(kotlin/Int, kotlin/Int) // androidx.collection/MutableIntIntMap.put|put(kotlin.Int;kotlin.Int){}[0]
+ final fun put(kotlin/Int, kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableIntIntMap.put|put(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+ final fun putAll(androidx.collection/IntIntMap) // androidx.collection/MutableIntIntMap.putAll|putAll(androidx.collection.IntIntMap){}[0]
+ final fun remove(kotlin/Int) // androidx.collection/MutableIntIntMap.remove|remove(kotlin.Int){}[0]
+ final fun remove(kotlin/Int, kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntIntMap.remove|remove(kotlin.Int;kotlin.Int){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableIntIntMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Int, kotlin/Int) // androidx.collection/MutableIntIntMap.set|set(kotlin.Int;kotlin.Int){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableIntIntMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Int, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/MutableIntIntMap.getOrPut|getOrPut(kotlin.Int;kotlin.Function0<kotlin.Int>){}[0]
+ final inline fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntIntMap.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+ final inline fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntIntMap.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+ final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntIntMap.minusAssign|minusAssign(kotlin.Int){}[0]
+ final inline fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntIntMap.minusAssign|minusAssign(kotlin.IntArray){}[0]
+ final inline fun plusAssign(androidx.collection/IntIntMap) // androidx.collection/MutableIntIntMap.plusAssign|plusAssign(androidx.collection.IntIntMap){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Boolean>) // androidx.collection/MutableIntIntMap.removeIf|removeIf(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableIntList : androidx.collection/IntList { // androidx.collection/MutableIntList|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntList.<init>|<init>(kotlin.Int){}[0]
+ final fun add(kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntList.add|add(kotlin.Int){}[0]
+ final fun add(kotlin/Int, kotlin/Int) // androidx.collection/MutableIntList.add|add(kotlin.Int;kotlin.Int){}[0]
+ final fun addAll(androidx.collection/IntList): kotlin/Boolean // androidx.collection/MutableIntList.addAll|addAll(androidx.collection.IntList){}[0]
+ final fun addAll(kotlin/Int, androidx.collection/IntList): kotlin/Boolean // androidx.collection/MutableIntList.addAll|addAll(kotlin.Int;androidx.collection.IntList){}[0]
+ final fun addAll(kotlin/Int, kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntList.addAll|addAll(kotlin.Int;kotlin.IntArray){}[0]
+ final fun addAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntList.addAll|addAll(kotlin.IntArray){}[0]
+ final fun clear() // androidx.collection/MutableIntList.clear|clear(){}[0]
+ final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableIntList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+ final fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntList.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+ final fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntList.minusAssign|minusAssign(kotlin.IntArray){}[0]
+ final fun plusAssign(androidx.collection/IntList) // androidx.collection/MutableIntList.plusAssign|plusAssign(androidx.collection.IntList){}[0]
+ final fun plusAssign(kotlin/IntArray) // androidx.collection/MutableIntList.plusAssign|plusAssign(kotlin.IntArray){}[0]
+ final fun remove(kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntList.remove|remove(kotlin.Int){}[0]
+ final fun removeAll(androidx.collection/IntList): kotlin/Boolean // androidx.collection/MutableIntList.removeAll|removeAll(androidx.collection.IntList){}[0]
+ final fun removeAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntList.removeAll|removeAll(kotlin.IntArray){}[0]
+ final fun removeAt(kotlin/Int): kotlin/Int // androidx.collection/MutableIntList.removeAt|removeAt(kotlin.Int){}[0]
+ final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableIntList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+ final fun retainAll(androidx.collection/IntList): kotlin/Boolean // androidx.collection/MutableIntList.retainAll|retainAll(androidx.collection.IntList){}[0]
+ final fun retainAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntList.retainAll|retainAll(kotlin.IntArray){}[0]
+ final fun set(kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableIntList.set|set(kotlin.Int;kotlin.Int){}[0]
+ final fun sort() // androidx.collection/MutableIntList.sort|sort(){}[0]
+ final fun sortDescending() // androidx.collection/MutableIntList.sortDescending|sortDescending(){}[0]
+ final fun trim(kotlin/Int =...) // androidx.collection/MutableIntList.trim|trim(kotlin.Int){}[0]
+ final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntList.minusAssign|minusAssign(kotlin.Int){}[0]
+ final inline fun plusAssign(kotlin/Int) // androidx.collection/MutableIntList.plusAssign|plusAssign(kotlin.Int){}[0]
+ final val capacity // androidx.collection/MutableIntList.capacity|{}capacity[0]
+ final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableIntList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class androidx.collection/MutableIntLongMap : androidx.collection/IntLongMap { // androidx.collection/MutableIntLongMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntLongMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableIntLongMap.clear|clear(){}[0]
+ final fun put(kotlin/Int, kotlin/Long) // androidx.collection/MutableIntLongMap.put|put(kotlin.Int;kotlin.Long){}[0]
+ final fun put(kotlin/Int, kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/MutableIntLongMap.put|put(kotlin.Int;kotlin.Long;kotlin.Long){}[0]
+ final fun putAll(androidx.collection/IntLongMap) // androidx.collection/MutableIntLongMap.putAll|putAll(androidx.collection.IntLongMap){}[0]
+ final fun remove(kotlin/Int) // androidx.collection/MutableIntLongMap.remove|remove(kotlin.Int){}[0]
+ final fun remove(kotlin/Int, kotlin/Long): kotlin/Boolean // androidx.collection/MutableIntLongMap.remove|remove(kotlin.Int;kotlin.Long){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableIntLongMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Int, kotlin/Long) // androidx.collection/MutableIntLongMap.set|set(kotlin.Int;kotlin.Long){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableIntLongMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Int, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/MutableIntLongMap.getOrPut|getOrPut(kotlin.Int;kotlin.Function0<kotlin.Long>){}[0]
+ final inline fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntLongMap.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+ final inline fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntLongMap.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+ final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntLongMap.minusAssign|minusAssign(kotlin.Int){}[0]
+ final inline fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntLongMap.minusAssign|minusAssign(kotlin.IntArray){}[0]
+ final inline fun plusAssign(androidx.collection/IntLongMap) // androidx.collection/MutableIntLongMap.plusAssign|plusAssign(androidx.collection.IntLongMap){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Boolean>) // androidx.collection/MutableIntLongMap.removeIf|removeIf(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableIntSet : androidx.collection/IntSet { // androidx.collection/MutableIntSet|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntSet.<init>|<init>(kotlin.Int){}[0]
+ final fun add(kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntSet.add|add(kotlin.Int){}[0]
+ final fun addAll(androidx.collection/IntSet): kotlin/Boolean // androidx.collection/MutableIntSet.addAll|addAll(androidx.collection.IntSet){}[0]
+ final fun addAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntSet.addAll|addAll(kotlin.IntArray){}[0]
+ final fun clear() // androidx.collection/MutableIntSet.clear|clear(){}[0]
+ final fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntSet.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+ final fun minusAssign(kotlin/Int) // androidx.collection/MutableIntSet.minusAssign|minusAssign(kotlin.Int){}[0]
+ final fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntSet.minusAssign|minusAssign(kotlin.IntArray){}[0]
+ final fun plusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntSet.plusAssign|plusAssign(androidx.collection.IntSet){}[0]
+ final fun plusAssign(kotlin/Int) // androidx.collection/MutableIntSet.plusAssign|plusAssign(kotlin.Int){}[0]
+ final fun plusAssign(kotlin/IntArray) // androidx.collection/MutableIntSet.plusAssign|plusAssign(kotlin.IntArray){}[0]
+ final fun remove(kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntSet.remove|remove(kotlin.Int){}[0]
+ final fun removeAll(androidx.collection/IntSet): kotlin/Boolean // androidx.collection/MutableIntSet.removeAll|removeAll(androidx.collection.IntSet){}[0]
+ final fun removeAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntSet.removeAll|removeAll(kotlin.IntArray){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableIntSet.trim|trim(){}[0]
+}
+final class androidx.collection/MutableLongFloatMap : androidx.collection/LongFloatMap { // androidx.collection/MutableLongFloatMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongFloatMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableLongFloatMap.clear|clear(){}[0]
+ final fun put(kotlin/Long, kotlin/Float) // androidx.collection/MutableLongFloatMap.put|put(kotlin.Long;kotlin.Float){}[0]
+ final fun put(kotlin/Long, kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/MutableLongFloatMap.put|put(kotlin.Long;kotlin.Float;kotlin.Float){}[0]
+ final fun putAll(androidx.collection/LongFloatMap) // androidx.collection/MutableLongFloatMap.putAll|putAll(androidx.collection.LongFloatMap){}[0]
+ final fun remove(kotlin/Long) // androidx.collection/MutableLongFloatMap.remove|remove(kotlin.Long){}[0]
+ final fun remove(kotlin/Long, kotlin/Float): kotlin/Boolean // androidx.collection/MutableLongFloatMap.remove|remove(kotlin.Long;kotlin.Float){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableLongFloatMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Long, kotlin/Float) // androidx.collection/MutableLongFloatMap.set|set(kotlin.Long;kotlin.Float){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableLongFloatMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Long, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/MutableLongFloatMap.getOrPut|getOrPut(kotlin.Long;kotlin.Function0<kotlin.Float>){}[0]
+ final inline fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongFloatMap.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+ final inline fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongFloatMap.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+ final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongFloatMap.minusAssign|minusAssign(kotlin.Long){}[0]
+ final inline fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongFloatMap.minusAssign|minusAssign(kotlin.LongArray){}[0]
+ final inline fun plusAssign(androidx.collection/LongFloatMap) // androidx.collection/MutableLongFloatMap.plusAssign|plusAssign(androidx.collection.LongFloatMap){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Boolean>) // androidx.collection/MutableLongFloatMap.removeIf|removeIf(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableLongIntMap : androidx.collection/LongIntMap { // androidx.collection/MutableLongIntMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongIntMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableLongIntMap.clear|clear(){}[0]
+ final fun put(kotlin/Long, kotlin/Int) // androidx.collection/MutableLongIntMap.put|put(kotlin.Long;kotlin.Int){}[0]
+ final fun put(kotlin/Long, kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableLongIntMap.put|put(kotlin.Long;kotlin.Int;kotlin.Int){}[0]
+ final fun putAll(androidx.collection/LongIntMap) // androidx.collection/MutableLongIntMap.putAll|putAll(androidx.collection.LongIntMap){}[0]
+ final fun remove(kotlin/Long) // androidx.collection/MutableLongIntMap.remove|remove(kotlin.Long){}[0]
+ final fun remove(kotlin/Long, kotlin/Int): kotlin/Boolean // androidx.collection/MutableLongIntMap.remove|remove(kotlin.Long;kotlin.Int){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableLongIntMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Long, kotlin/Int) // androidx.collection/MutableLongIntMap.set|set(kotlin.Long;kotlin.Int){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableLongIntMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Long, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/MutableLongIntMap.getOrPut|getOrPut(kotlin.Long;kotlin.Function0<kotlin.Int>){}[0]
+ final inline fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongIntMap.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+ final inline fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongIntMap.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+ final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongIntMap.minusAssign|minusAssign(kotlin.Long){}[0]
+ final inline fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongIntMap.minusAssign|minusAssign(kotlin.LongArray){}[0]
+ final inline fun plusAssign(androidx.collection/LongIntMap) // androidx.collection/MutableLongIntMap.plusAssign|plusAssign(androidx.collection.LongIntMap){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Boolean>) // androidx.collection/MutableLongIntMap.removeIf|removeIf(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableLongList : androidx.collection/LongList { // androidx.collection/MutableLongList|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongList.<init>|<init>(kotlin.Int){}[0]
+ final fun add(kotlin/Int, kotlin/Long) // androidx.collection/MutableLongList.add|add(kotlin.Int;kotlin.Long){}[0]
+ final fun add(kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongList.add|add(kotlin.Long){}[0]
+ final fun addAll(androidx.collection/LongList): kotlin/Boolean // androidx.collection/MutableLongList.addAll|addAll(androidx.collection.LongList){}[0]
+ final fun addAll(kotlin/Int, androidx.collection/LongList): kotlin/Boolean // androidx.collection/MutableLongList.addAll|addAll(kotlin.Int;androidx.collection.LongList){}[0]
+ final fun addAll(kotlin/Int, kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongList.addAll|addAll(kotlin.Int;kotlin.LongArray){}[0]
+ final fun addAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongList.addAll|addAll(kotlin.LongArray){}[0]
+ final fun clear() // androidx.collection/MutableLongList.clear|clear(){}[0]
+ final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableLongList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+ final fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongList.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+ final fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongList.minusAssign|minusAssign(kotlin.LongArray){}[0]
+ final fun plusAssign(androidx.collection/LongList) // androidx.collection/MutableLongList.plusAssign|plusAssign(androidx.collection.LongList){}[0]
+ final fun plusAssign(kotlin/LongArray) // androidx.collection/MutableLongList.plusAssign|plusAssign(kotlin.LongArray){}[0]
+ final fun remove(kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongList.remove|remove(kotlin.Long){}[0]
+ final fun removeAll(androidx.collection/LongList): kotlin/Boolean // androidx.collection/MutableLongList.removeAll|removeAll(androidx.collection.LongList){}[0]
+ final fun removeAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongList.removeAll|removeAll(kotlin.LongArray){}[0]
+ final fun removeAt(kotlin/Int): kotlin/Long // androidx.collection/MutableLongList.removeAt|removeAt(kotlin.Int){}[0]
+ final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableLongList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+ final fun retainAll(androidx.collection/LongList): kotlin/Boolean // androidx.collection/MutableLongList.retainAll|retainAll(androidx.collection.LongList){}[0]
+ final fun retainAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongList.retainAll|retainAll(kotlin.LongArray){}[0]
+ final fun set(kotlin/Int, kotlin/Long): kotlin/Long // androidx.collection/MutableLongList.set|set(kotlin.Int;kotlin.Long){}[0]
+ final fun sort() // androidx.collection/MutableLongList.sort|sort(){}[0]
+ final fun sortDescending() // androidx.collection/MutableLongList.sortDescending|sortDescending(){}[0]
+ final fun trim(kotlin/Int =...) // androidx.collection/MutableLongList.trim|trim(kotlin.Int){}[0]
+ final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongList.minusAssign|minusAssign(kotlin.Long){}[0]
+ final inline fun plusAssign(kotlin/Long) // androidx.collection/MutableLongList.plusAssign|plusAssign(kotlin.Long){}[0]
+ final val capacity // androidx.collection/MutableLongList.capacity|{}capacity[0]
+ final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableLongList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class androidx.collection/MutableLongLongMap : androidx.collection/LongLongMap { // androidx.collection/MutableLongLongMap|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongLongMap.<init>|<init>(kotlin.Int){}[0]
+ final fun clear() // androidx.collection/MutableLongLongMap.clear|clear(){}[0]
+ final fun put(kotlin/Long, kotlin/Long) // androidx.collection/MutableLongLongMap.put|put(kotlin.Long;kotlin.Long){}[0]
+ final fun put(kotlin/Long, kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/MutableLongLongMap.put|put(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+ final fun putAll(androidx.collection/LongLongMap) // androidx.collection/MutableLongLongMap.putAll|putAll(androidx.collection.LongLongMap){}[0]
+ final fun remove(kotlin/Long) // androidx.collection/MutableLongLongMap.remove|remove(kotlin.Long){}[0]
+ final fun remove(kotlin/Long, kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongLongMap.remove|remove(kotlin.Long;kotlin.Long){}[0]
+ final fun removeValueAt(kotlin/Int) // androidx.collection/MutableLongLongMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+ final fun set(kotlin/Long, kotlin/Long) // androidx.collection/MutableLongLongMap.set|set(kotlin.Long;kotlin.Long){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableLongLongMap.trim|trim(){}[0]
+ final inline fun getOrPut(kotlin/Long, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/MutableLongLongMap.getOrPut|getOrPut(kotlin.Long;kotlin.Function0<kotlin.Long>){}[0]
+ final inline fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongLongMap.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+ final inline fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongLongMap.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+ final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongLongMap.minusAssign|minusAssign(kotlin.Long){}[0]
+ final inline fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongLongMap.minusAssign|minusAssign(kotlin.LongArray){}[0]
+ final inline fun plusAssign(androidx.collection/LongLongMap) // androidx.collection/MutableLongLongMap.plusAssign|plusAssign(androidx.collection.LongLongMap){}[0]
+ final inline fun removeIf(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Boolean>) // androidx.collection/MutableLongLongMap.removeIf|removeIf(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableLongSet : androidx.collection/LongSet { // androidx.collection/MutableLongSet|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongSet.<init>|<init>(kotlin.Int){}[0]
+ final fun add(kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongSet.add|add(kotlin.Long){}[0]
+ final fun addAll(androidx.collection/LongSet): kotlin/Boolean // androidx.collection/MutableLongSet.addAll|addAll(androidx.collection.LongSet){}[0]
+ final fun addAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongSet.addAll|addAll(kotlin.LongArray){}[0]
+ final fun clear() // androidx.collection/MutableLongSet.clear|clear(){}[0]
+ final fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongSet.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+ final fun minusAssign(kotlin/Long) // androidx.collection/MutableLongSet.minusAssign|minusAssign(kotlin.Long){}[0]
+ final fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongSet.minusAssign|minusAssign(kotlin.LongArray){}[0]
+ final fun plusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongSet.plusAssign|plusAssign(androidx.collection.LongSet){}[0]
+ final fun plusAssign(kotlin/Long) // androidx.collection/MutableLongSet.plusAssign|plusAssign(kotlin.Long){}[0]
+ final fun plusAssign(kotlin/LongArray) // androidx.collection/MutableLongSet.plusAssign|plusAssign(kotlin.LongArray){}[0]
+ final fun remove(kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongSet.remove|remove(kotlin.Long){}[0]
+ final fun removeAll(androidx.collection/LongSet): kotlin/Boolean // androidx.collection/MutableLongSet.removeAll|removeAll(androidx.collection.LongSet){}[0]
+ final fun removeAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongSet.removeAll|removeAll(kotlin.LongArray){}[0]
+ final fun trim(): kotlin/Int // androidx.collection/MutableLongSet.trim|trim(){}[0]
+}
+final const val androidx.collection/BitmaskLsb // androidx.collection/BitmaskLsb|{}BitmaskLsb[0]
+ final fun <get-BitmaskLsb>(): kotlin/Long // androidx.collection/BitmaskLsb.<get-BitmaskLsb>|<get-BitmaskLsb>(){}[0]
+final const val androidx.collection/BitmaskMsb // androidx.collection/BitmaskMsb|<get-BitmaskMsb>(){}[0]
+ final fun <get-BitmaskMsb>(): kotlin/Long // androidx.collection/BitmaskMsb.<get-BitmaskMsb>|<get-BitmaskMsb>(){}[0]
+final const val androidx.collection/Sentinel // androidx.collection/Sentinel|{}Sentinel[0]
+ final fun <get-Sentinel>(): kotlin/Long // androidx.collection/Sentinel.<get-Sentinel>|<get-Sentinel>(){}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/emptyScatterMap(): androidx.collection/ScatterMap<#A, #B> // androidx.collection/emptyScatterMap|emptyScatterMap(){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/mutableScatterMapOf(): androidx.collection/MutableScatterMap<#A, #B> // androidx.collection/mutableScatterMapOf|mutableScatterMapOf(){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/mutableScatterMapOf(kotlin/Array<out kotlin/Pair<#A, #B>>...): androidx.collection/MutableScatterMap<#A, #B> // androidx.collection/mutableScatterMapOf|mutableScatterMapOf(kotlin.Array<out|kotlin.Pair<0:0,0:1>>...){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/keyIterator(): kotlin.collections/LongIterator // androidx.collection/keyIterator|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/plus(androidx.collection/LongSparseArray<#A>): androidx.collection/LongSparseArray<#A> // androidx.collection/plus|[email protected]<0:0>(androidx.collection.LongSparseArray<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/remove(kotlin/Long, #A): kotlin/Boolean // androidx.collection/remove|[email protected]<0:0>(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/valueIterator(): kotlin.collections/Iterator<#A> // androidx.collection/valueIterator|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/keyIterator(): kotlin.collections/IntIterator // androidx.collection/keyIterator|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/plus(androidx.collection/SparseArrayCompat<#A>): androidx.collection/SparseArrayCompat<#A> // androidx.collection/plus|[email protected]<0:0>(androidx.collection.SparseArrayCompat<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/remove(kotlin/Int, #A): kotlin/Boolean // androidx.collection/remove|[email protected]<0:0>(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/valueIterator(): kotlin.collections/Iterator<#A> // androidx.collection/valueIterator|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/arraySetOf(kotlin/Array<out #A>...): androidx.collection/ArraySet<#A> // androidx.collection/arraySetOf|arraySetOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyFloatObjectMap(): androidx.collection/FloatObjectMap<#A> // androidx.collection/emptyFloatObjectMap|emptyFloatObjectMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyIntObjectMap(): androidx.collection/IntObjectMap<#A> // androidx.collection/emptyIntObjectMap|emptyIntObjectMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyLongObjectMap(): androidx.collection/LongObjectMap<#A> // androidx.collection/emptyLongObjectMap|emptyLongObjectMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyObjectFloatMap(): androidx.collection/ObjectFloatMap<#A> // androidx.collection/emptyObjectFloatMap|emptyObjectFloatMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyObjectIntMap(): androidx.collection/ObjectIntMap<#A> // androidx.collection/emptyObjectIntMap|emptyObjectIntMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyObjectList(): androidx.collection/ObjectList<#A> // androidx.collection/emptyObjectList|emptyObjectList(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyObjectLongMap(): androidx.collection/ObjectLongMap<#A> // androidx.collection/emptyObjectLongMap|emptyObjectLongMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyScatterSet(): androidx.collection/ScatterSet<#A> // androidx.collection/emptyScatterSet|emptyScatterSet(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A, kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A, kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A, kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A, kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int, #A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(#A): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(#A, #A): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(#A, #A, #A): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(0:0;0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long, #A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(#A): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(#A, #A): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(#A, #A, #A): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(0:0;0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(kotlin/Array<out #A>...): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMap(): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMap|objectFloatMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMap(): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMap|objectIntMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int, #A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(#A): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(#A, #A): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(#A, #A, #A): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(0:0;0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(kotlin/Array<out #A>...): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMap(): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMap|objectLongMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long, #A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(#A): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(#A, #A): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(#A, #A, #A): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(0:0;0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(kotlin/Array<out #A>...): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final fun androidx.collection/doubleListOf(): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(){}[0]
+final fun androidx.collection/doubleListOf(kotlin/Double): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(kotlin.Double){}[0]
+final fun androidx.collection/doubleListOf(kotlin/Double, kotlin/Double): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(kotlin.Double;kotlin.Double){}[0]
+final fun androidx.collection/doubleListOf(kotlin/Double, kotlin/Double, kotlin/Double): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(kotlin.Double;kotlin.Double;kotlin.Double){}[0]
+final fun androidx.collection/doubleListOf(kotlin/DoubleArray...): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(kotlin.DoubleArray...){}[0]
+final fun androidx.collection/emptyDoubleList(): androidx.collection/DoubleList // androidx.collection/emptyDoubleList|emptyDoubleList(){}[0]
+final fun androidx.collection/emptyFloatFloatMap(): androidx.collection/FloatFloatMap // androidx.collection/emptyFloatFloatMap|emptyFloatFloatMap(){}[0]
+final fun androidx.collection/emptyFloatIntMap(): androidx.collection/FloatIntMap // androidx.collection/emptyFloatIntMap|emptyFloatIntMap(){}[0]
+final fun androidx.collection/emptyFloatList(): androidx.collection/FloatList // androidx.collection/emptyFloatList|emptyFloatList(){}[0]
+final fun androidx.collection/emptyFloatLongMap(): androidx.collection/FloatLongMap // androidx.collection/emptyFloatLongMap|emptyFloatLongMap(){}[0]
+final fun androidx.collection/emptyFloatSet(): androidx.collection/FloatSet // androidx.collection/emptyFloatSet|emptyFloatSet(){}[0]
+final fun androidx.collection/emptyIntFloatMap(): androidx.collection/IntFloatMap // androidx.collection/emptyIntFloatMap|emptyIntFloatMap(){}[0]
+final fun androidx.collection/emptyIntIntMap(): androidx.collection/IntIntMap // androidx.collection/emptyIntIntMap|emptyIntIntMap(){}[0]
+final fun androidx.collection/emptyIntList(): androidx.collection/IntList // androidx.collection/emptyIntList|emptyIntList(){}[0]
+final fun androidx.collection/emptyIntLongMap(): androidx.collection/IntLongMap // androidx.collection/emptyIntLongMap|emptyIntLongMap(){}[0]
+final fun androidx.collection/emptyIntSet(): androidx.collection/IntSet // androidx.collection/emptyIntSet|emptyIntSet(){}[0]
+final fun androidx.collection/emptyLongFloatMap(): androidx.collection/LongFloatMap // androidx.collection/emptyLongFloatMap|emptyLongFloatMap(){}[0]
+final fun androidx.collection/emptyLongIntMap(): androidx.collection/LongIntMap // androidx.collection/emptyLongIntMap|emptyLongIntMap(){}[0]
+final fun androidx.collection/emptyLongList(): androidx.collection/LongList // androidx.collection/emptyLongList|emptyLongList(){}[0]
+final fun androidx.collection/emptyLongLongMap(): androidx.collection/LongLongMap // androidx.collection/emptyLongLongMap|emptyLongLongMap(){}[0]
+final fun androidx.collection/emptyLongSet(): androidx.collection/LongSet // androidx.collection/emptyLongSet|emptyLongSet(){}[0]
+final fun androidx.collection/floatFloatMapOf(): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatIntMapOf(): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatListOf(): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(){}[0]
+final fun androidx.collection/floatListOf(kotlin/Float): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(kotlin.Float){}[0]
+final fun androidx.collection/floatListOf(kotlin/Float, kotlin/Float): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatListOf(kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatListOf(kotlin/FloatArray...): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(kotlin.FloatArray...){}[0]
+final fun androidx.collection/floatLongMapOf(): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatSetOf(): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(){}[0]
+final fun androidx.collection/floatSetOf(kotlin/Float): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(kotlin.Float){}[0]
+final fun androidx.collection/floatSetOf(kotlin/Float, kotlin/Float): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatSetOf(kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatSetOf(kotlin/FloatArray...): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(kotlin.FloatArray...){}[0]
+final fun androidx.collection/intFloatMapOf(): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intIntMapOf(): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intListOf(): androidx.collection/IntList // androidx.collection/intListOf|intListOf(){}[0]
+final fun androidx.collection/intListOf(kotlin/Int): androidx.collection/IntList // androidx.collection/intListOf|intListOf(kotlin.Int){}[0]
+final fun androidx.collection/intListOf(kotlin/Int, kotlin/Int): androidx.collection/IntList // androidx.collection/intListOf|intListOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intListOf(kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntList // androidx.collection/intListOf|intListOf(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intListOf(kotlin/IntArray...): androidx.collection/IntList // androidx.collection/intListOf|intListOf(kotlin.IntArray...){}[0]
+final fun androidx.collection/intLongMapOf(): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intSetOf(): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(){}[0]
+final fun androidx.collection/intSetOf(kotlin/Int): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(kotlin.Int){}[0]
+final fun androidx.collection/intSetOf(kotlin/Int, kotlin/Int): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intSetOf(kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intSetOf(kotlin/IntArray...): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(kotlin.IntArray...){}[0]
+final fun androidx.collection/longFloatMapOf(): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longIntMapOf(): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longListOf(): androidx.collection/LongList // androidx.collection/longListOf|longListOf(){}[0]
+final fun androidx.collection/longListOf(kotlin/Long): androidx.collection/LongList // androidx.collection/longListOf|longListOf(kotlin.Long){}[0]
+final fun androidx.collection/longListOf(kotlin/Long, kotlin/Long): androidx.collection/LongList // androidx.collection/longListOf|longListOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longListOf(kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongList // androidx.collection/longListOf|longListOf(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longListOf(kotlin/LongArray...): androidx.collection/LongList // androidx.collection/longListOf|longListOf(kotlin.LongArray...){}[0]
+final fun androidx.collection/longLongMapOf(): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longSetOf(): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(){}[0]
+final fun androidx.collection/longSetOf(kotlin/Long): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(kotlin.Long){}[0]
+final fun androidx.collection/longSetOf(kotlin/Long, kotlin/Long): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longSetOf(kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longSetOf(kotlin/LongArray...): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(kotlin.LongArray...){}[0]
+final fun androidx.collection/mutableDoubleListOf(kotlin/Double): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(kotlin.Double){}[0]
+final fun androidx.collection/mutableDoubleListOf(kotlin/Double, kotlin/Double): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(kotlin.Double;kotlin.Double){}[0]
+final fun androidx.collection/mutableDoubleListOf(kotlin/Double, kotlin/Double, kotlin/Double): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(kotlin.Double;kotlin.Double;kotlin.Double){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatListOf(kotlin/Float): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatListOf(kotlin/Float, kotlin/Float): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatListOf(kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatSetOf(): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(){}[0]
+final fun androidx.collection/mutableFloatSetOf(kotlin/Float): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatSetOf(kotlin/Float, kotlin/Float): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatSetOf(kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatSetOf(kotlin/FloatArray...): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(kotlin.FloatArray...){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntIntMapOf(): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntListOf(kotlin/Int): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(kotlin.Int){}[0]
+final fun androidx.collection/mutableIntListOf(kotlin/Int, kotlin/Int): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntListOf(kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntLongMapOf(): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntSetOf(): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(){}[0]
+final fun androidx.collection/mutableIntSetOf(kotlin/Int): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(kotlin.Int){}[0]
+final fun androidx.collection/mutableIntSetOf(kotlin/Int, kotlin/Int): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntSetOf(kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntSetOf(kotlin/IntArray...): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(kotlin.IntArray...){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongIntMapOf(): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongListOf(kotlin/Long): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(kotlin.Long){}[0]
+final fun androidx.collection/mutableLongListOf(kotlin/Long, kotlin/Long): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongListOf(kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongSetOf(): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(){}[0]
+final fun androidx.collection/mutableLongSetOf(kotlin/Long): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(kotlin.Long){}[0]
+final fun androidx.collection/mutableLongSetOf(kotlin/Long, kotlin/Long): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongSetOf(kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongSetOf(kotlin/LongArray...): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(kotlin.LongArray...){}[0]
+final inline fun (kotlin/Long).androidx.collection/lowestBitSet(): kotlin/Int // androidx.collection/lowestBitSet|[email protected](){}[0]
+final inline fun (kotlin/Long).androidx.collection/maskEmptyOrDeleted(): kotlin/Long // androidx.collection/maskEmptyOrDeleted|[email protected](){}[0]
+final inline fun (kotlin/Long).androidx.collection/match(kotlin/Int): kotlin/Long // androidx.collection/match|[email protected](kotlin.Int){}[0]
+final inline fun <#A: kotlin/Any, #B: kotlin/Any> androidx.collection/lruCache(kotlin/Int, crossinline kotlin/Function2<#A, #B, kotlin/Int> =..., crossinline kotlin/Function1<#A, #B?> =..., crossinline kotlin/Function4<kotlin/Boolean, #A, #B, #B?, kotlin/Unit> =...): androidx.collection/LruCache<#A, #B> // androidx.collection/lruCache|lruCache(kotlin.Int;kotlin.Function2<0:0,0:1,kotlin.Int>;kotlin.Function1<0:0,0:1?>;kotlin.Function4<kotlin.Boolean,0:0,0:1,0:1?,kotlin.Unit>){0§<kotlin.Any>;1§<kotlin.Any>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/contains(kotlin/Long): kotlin/Boolean // androidx.collection/contains|[email protected]<0:0>(kotlin.Long){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/forEach(kotlin/Function2<kotlin/Long, #A, kotlin/Unit>) // androidx.collection/forEach|[email protected]<0:0>(kotlin.Function2<kotlin.Long,0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/getOrDefault(kotlin/Long, #A): #A // androidx.collection/getOrDefault|[email protected]<0:0>(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/getOrElse(kotlin/Long, kotlin/Function0<#A>): #A // androidx.collection/getOrElse|[email protected]<0:0>(kotlin.Long;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/isNotEmpty(): kotlin/Boolean // androidx.collection/isNotEmpty|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/set(kotlin/Long, #A) // androidx.collection/set|[email protected]<0:0>(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/contains(kotlin/Int): kotlin/Boolean // androidx.collection/contains|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/forEach(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.collection/forEach|[email protected]<0:0>(kotlin.Function2<kotlin.Int,0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/getOrDefault(kotlin/Int, #A): #A // androidx.collection/getOrDefault|[email protected]<0:0>(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/getOrElse(kotlin/Int, kotlin/Function0<#A>): #A // androidx.collection/getOrElse|[email protected]<0:0>(kotlin.Int;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/isNotEmpty(): kotlin/Boolean // androidx.collection/isNotEmpty|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/set(kotlin/Int, #A) // androidx.collection/set|[email protected]<0:0>(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> androidx.collection/arraySetOf(): androidx.collection/ArraySet<#A> // androidx.collection/arraySetOf|arraySetOf(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(kotlin/Array<out #A>...): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final inline fun androidx.collection.internal/floatFromBits(kotlin/Int): kotlin/Float // androidx.collection.internal/floatFromBits|floatFromBits(kotlin.Int){}[0]
+final inline fun androidx.collection/isFull(kotlin/Long): kotlin/Boolean // androidx.collection/isFull|isFull(kotlin.Long){}[0]
+final inline fun androidx.collection/mutableDoubleListOf(): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(){}[0]
+final inline fun androidx.collection/mutableDoubleListOf(kotlin/DoubleArray...): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(kotlin.DoubleArray...){}[0]
+final inline fun androidx.collection/mutableFloatListOf(): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(){}[0]
+final inline fun androidx.collection/mutableFloatListOf(kotlin/FloatArray...): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(kotlin.FloatArray...){}[0]
+final inline fun androidx.collection/mutableIntListOf(): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(){}[0]
+final inline fun androidx.collection/mutableIntListOf(kotlin/IntArray...): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(kotlin.IntArray...){}[0]
+final inline fun androidx.collection/mutableLongListOf(): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(){}[0]
+final inline fun androidx.collection/mutableLongListOf(kotlin/LongArray...): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(kotlin.LongArray...){}[0]
+final inline fun androidx.collection/readRawMetadata(kotlin/LongArray, kotlin/Int): kotlin/Long // androidx.collection/readRawMetadata|readRawMetadata(kotlin.LongArray;kotlin.Int){}[0]
+final val androidx.collection/size // androidx.collection/size|@androidx.collection.LongSparseArray<0:0>{0§<kotlin.Any?>}size[0]
+ final inline fun <#A1: kotlin/Any?> (androidx.collection/LongSparseArray<#A1>).<get-size>(): kotlin/Int // androidx.collection/size.<get-size>|<get-size>@androidx.collection.LongSparseArray<0:0>(){0§<kotlin.Any?>}[0]
+final val androidx.collection/size // androidx.collection/size|@androidx.collection.SparseArrayCompat<0:0>{0§<kotlin.Any?>}size[0]
+ final inline fun <#A1: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A1>).<get-size>(): kotlin/Int // androidx.collection/size.<get-size>|<get-size>@androidx.collection.SparseArrayCompat<0:0>(){0§<kotlin.Any?>}[0]
+final value class androidx.collection/FloatFloatPair { // androidx.collection/FloatFloatPair|null[0]
+ constructor <init>(kotlin/Float, kotlin/Float) // androidx.collection/FloatFloatPair.<init>|<init>(kotlin.Float;kotlin.Float){}[0]
+ final fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatFloatPair.equals|equals(kotlin.Any?){}[0]
+ final fun hashCode(): kotlin/Int // androidx.collection/FloatFloatPair.hashCode|hashCode(){}[0]
+ final fun toString(): kotlin/String // androidx.collection/FloatFloatPair.toString|toString(){}[0]
+ final inline fun component1(): kotlin/Float // androidx.collection/FloatFloatPair.component1|component1(){}[0]
+ final inline fun component2(): kotlin/Float // androidx.collection/FloatFloatPair.component2|component2(){}[0]
+ final val first // androidx.collection/FloatFloatPair.first|{}first[0]
+ final inline fun <get-first>(): kotlin/Float // androidx.collection/FloatFloatPair.first.<get-first>|<get-first>(){}[0]
+ final val packedValue // androidx.collection/FloatFloatPair.packedValue|{}packedValue[0]
+ final fun <get-packedValue>(): kotlin/Long // androidx.collection/FloatFloatPair.packedValue.<get-packedValue>|<get-packedValue>(){}[0]
+ final val second // androidx.collection/FloatFloatPair.second|{}second[0]
+ final inline fun <get-second>(): kotlin/Float // androidx.collection/FloatFloatPair.second.<get-second>|<get-second>(){}[0]
+}
+final value class androidx.collection/IntIntPair { // androidx.collection/IntIntPair|null[0]
+ constructor <init>(kotlin/Int, kotlin/Int) // androidx.collection/IntIntPair.<init>|<init>(kotlin.Int;kotlin.Int){}[0]
+ final fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntIntPair.equals|equals(kotlin.Any?){}[0]
+ final fun hashCode(): kotlin/Int // androidx.collection/IntIntPair.hashCode|hashCode(){}[0]
+ final fun toString(): kotlin/String // androidx.collection/IntIntPair.toString|toString(){}[0]
+ final inline fun component1(): kotlin/Int // androidx.collection/IntIntPair.component1|component1(){}[0]
+ final inline fun component2(): kotlin/Int // androidx.collection/IntIntPair.component2|component2(){}[0]
+ final val first // androidx.collection/IntIntPair.first|{}first[0]
+ final fun <get-first>(): kotlin/Int // androidx.collection/IntIntPair.first.<get-first>|<get-first>(){}[0]
+ final val packedValue // androidx.collection/IntIntPair.packedValue|{}packedValue[0]
+ final fun <get-packedValue>(): kotlin/Long // androidx.collection/IntIntPair.packedValue.<get-packedValue>|<get-packedValue>(){}[0]
+ final val second // androidx.collection/IntIntPair.second|{}second[0]
+ final fun <get-second>(): kotlin/Int // androidx.collection/IntIntPair.second.<get-second>|<get-second>(){}[0]
+}
+open class <#A: kotlin/Any, #B: kotlin/Any> androidx.collection/LruCache { // androidx.collection/LruCache|null[0]
+ constructor <init>(kotlin/Int) // androidx.collection/LruCache.<init>|<init>(kotlin.Int){}[0]
+ final fun createCount(): kotlin/Int // androidx.collection/LruCache.createCount|createCount(){}[0]
+ final fun evictAll() // androidx.collection/LruCache.evictAll|evictAll(){}[0]
+ final fun evictionCount(): kotlin/Int // androidx.collection/LruCache.evictionCount|evictionCount(){}[0]
+ final fun get(#A): #B? // androidx.collection/LruCache.get|get(1:0){}[0]
+ final fun hitCount(): kotlin/Int // androidx.collection/LruCache.hitCount|hitCount(){}[0]
+ final fun maxSize(): kotlin/Int // androidx.collection/LruCache.maxSize|maxSize(){}[0]
+ final fun missCount(): kotlin/Int // androidx.collection/LruCache.missCount|missCount(){}[0]
+ final fun put(#A, #B): #B? // androidx.collection/LruCache.put|put(1:0;1:1){}[0]
+ final fun putCount(): kotlin/Int // androidx.collection/LruCache.putCount|putCount(){}[0]
+ final fun remove(#A): #B? // androidx.collection/LruCache.remove|remove(1:0){}[0]
+ final fun size(): kotlin/Int // androidx.collection/LruCache.size|size(){}[0]
+ final fun snapshot(): kotlin.collections/MutableMap<#A, #B> // androidx.collection/LruCache.snapshot|snapshot(){}[0]
+ open fun create(#A): #B? // androidx.collection/LruCache.create|create(1:0){}[0]
+ open fun entryRemoved(kotlin/Boolean, #A, #B, #B?) // androidx.collection/LruCache.entryRemoved|entryRemoved(kotlin.Boolean;1:0;1:1;1:1?){}[0]
+ open fun resize(kotlin/Int) // androidx.collection/LruCache.resize|resize(kotlin.Int){}[0]
+ open fun sizeOf(#A, #B): kotlin/Int // androidx.collection/LruCache.sizeOf|sizeOf(1:0;1:1){}[0]
+ open fun toString(): kotlin/String // androidx.collection/LruCache.toString|toString(){}[0]
+ open fun trimToSize(kotlin/Int) // androidx.collection/LruCache.trimToSize|trimToSize(kotlin.Int){}[0]
+}
+open class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/SimpleArrayMap { // androidx.collection/SimpleArrayMap|null[0]
+ constructor <init>(androidx.collection/SimpleArrayMap<out #A, out #B>?) // androidx.collection/SimpleArrayMap.<init>|<init>(androidx.collection.SimpleArrayMap<out|1:0,out|1:1>?){}[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/SimpleArrayMap.<init>|<init>(kotlin.Int){}[0]
+ open fun clear() // androidx.collection/SimpleArrayMap.clear|clear(){}[0]
+ open fun containsKey(#A): kotlin/Boolean // androidx.collection/SimpleArrayMap.containsKey|containsKey(1:0){}[0]
+ open fun containsValue(#B): kotlin/Boolean // androidx.collection/SimpleArrayMap.containsValue|containsValue(1:1){}[0]
+ open fun ensureCapacity(kotlin/Int) // androidx.collection/SimpleArrayMap.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/SimpleArrayMap.equals|equals(kotlin.Any?){}[0]
+ open fun get(#A): #B? // androidx.collection/SimpleArrayMap.get|get(1:0){}[0]
+ open fun getOrDefault(kotlin/Any?, #B): #B // androidx.collection/SimpleArrayMap.getOrDefault|getOrDefault(kotlin.Any?;1:1){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/SimpleArrayMap.hashCode|hashCode(){}[0]
+ open fun indexOfKey(#A): kotlin/Int // androidx.collection/SimpleArrayMap.indexOfKey|indexOfKey(1:0){}[0]
+ open fun isEmpty(): kotlin/Boolean // androidx.collection/SimpleArrayMap.isEmpty|isEmpty(){}[0]
+ open fun keyAt(kotlin/Int): #A // androidx.collection/SimpleArrayMap.keyAt|keyAt(kotlin.Int){}[0]
+ open fun put(#A, #B): #B? // androidx.collection/SimpleArrayMap.put|put(1:0;1:1){}[0]
+ open fun putAll(androidx.collection/SimpleArrayMap<out #A, out #B>) // androidx.collection/SimpleArrayMap.putAll|putAll(androidx.collection.SimpleArrayMap<out|1:0,out|1:1>){}[0]
+ open fun putIfAbsent(#A, #B): #B? // androidx.collection/SimpleArrayMap.putIfAbsent|putIfAbsent(1:0;1:1){}[0]
+ open fun remove(#A): #B? // androidx.collection/SimpleArrayMap.remove|remove(1:0){}[0]
+ open fun remove(#A, #B): kotlin/Boolean // androidx.collection/SimpleArrayMap.remove|remove(1:0;1:1){}[0]
+ open fun removeAt(kotlin/Int): #B // androidx.collection/SimpleArrayMap.removeAt|removeAt(kotlin.Int){}[0]
+ open fun replace(#A, #B): #B? // androidx.collection/SimpleArrayMap.replace|replace(1:0;1:1){}[0]
+ open fun replace(#A, #B, #B): kotlin/Boolean // androidx.collection/SimpleArrayMap.replace|replace(1:0;1:1;1:1){}[0]
+ open fun setValueAt(kotlin/Int, #B): #B // androidx.collection/SimpleArrayMap.setValueAt|setValueAt(kotlin.Int;1:1){}[0]
+ open fun size(): kotlin/Int // androidx.collection/SimpleArrayMap.size|size(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/SimpleArrayMap.toString|toString(){}[0]
+ open fun valueAt(kotlin/Int): #B // androidx.collection/SimpleArrayMap.valueAt|valueAt(kotlin.Int){}[0]
+}
+open class <#A: kotlin/Any?> androidx.collection/LongSparseArray { // androidx.collection/LongSparseArray|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/LongSparseArray.<init>|<init>(kotlin.Int){}[0]
+ open fun append(kotlin/Long, #A) // androidx.collection/LongSparseArray.append|append(kotlin.Long;1:0){}[0]
+ open fun clear() // androidx.collection/LongSparseArray.clear|clear(){}[0]
+ open fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongSparseArray.containsKey|containsKey(kotlin.Long){}[0]
+ open fun containsValue(#A): kotlin/Boolean // androidx.collection/LongSparseArray.containsValue|containsValue(1:0){}[0]
+ open fun delete(kotlin/Long) // androidx.collection/LongSparseArray.delete|delete(kotlin.Long){}[0]
+ open fun get(kotlin/Long): #A? // androidx.collection/LongSparseArray.get|get(kotlin.Long){}[0]
+ open fun get(kotlin/Long, #A): #A // androidx.collection/LongSparseArray.get|get(kotlin.Long;1:0){}[0]
+ open fun indexOfKey(kotlin/Long): kotlin/Int // androidx.collection/LongSparseArray.indexOfKey|indexOfKey(kotlin.Long){}[0]
+ open fun indexOfValue(#A): kotlin/Int // androidx.collection/LongSparseArray.indexOfValue|indexOfValue(1:0){}[0]
+ open fun isEmpty(): kotlin/Boolean // androidx.collection/LongSparseArray.isEmpty|isEmpty(){}[0]
+ open fun keyAt(kotlin/Int): kotlin/Long // androidx.collection/LongSparseArray.keyAt|keyAt(kotlin.Int){}[0]
+ open fun put(kotlin/Long, #A) // androidx.collection/LongSparseArray.put|put(kotlin.Long;1:0){}[0]
+ open fun putAll(androidx.collection/LongSparseArray<out #A>) // androidx.collection/LongSparseArray.putAll|putAll(androidx.collection.LongSparseArray<out|1:0>){}[0]
+ open fun putIfAbsent(kotlin/Long, #A): #A? // androidx.collection/LongSparseArray.putIfAbsent|putIfAbsent(kotlin.Long;1:0){}[0]
+ open fun remove(kotlin/Long) // androidx.collection/LongSparseArray.remove|remove(kotlin.Long){}[0]
+ open fun remove(kotlin/Long, #A): kotlin/Boolean // androidx.collection/LongSparseArray.remove|remove(kotlin.Long;1:0){}[0]
+ open fun removeAt(kotlin/Int) // androidx.collection/LongSparseArray.removeAt|removeAt(kotlin.Int){}[0]
+ open fun replace(kotlin/Long, #A): #A? // androidx.collection/LongSparseArray.replace|replace(kotlin.Long;1:0){}[0]
+ open fun replace(kotlin/Long, #A, #A): kotlin/Boolean // androidx.collection/LongSparseArray.replace|replace(kotlin.Long;1:0;1:0){}[0]
+ open fun setValueAt(kotlin/Int, #A) // androidx.collection/LongSparseArray.setValueAt|setValueAt(kotlin.Int;1:0){}[0]
+ open fun size(): kotlin/Int // androidx.collection/LongSparseArray.size|size(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/LongSparseArray.toString|toString(){}[0]
+ open fun valueAt(kotlin/Int): #A // androidx.collection/LongSparseArray.valueAt|valueAt(kotlin.Int){}[0]
+}
+open class <#A: kotlin/Any?> androidx.collection/SparseArrayCompat { // androidx.collection/SparseArrayCompat|null[0]
+ constructor <init>(kotlin/Int =...) // androidx.collection/SparseArrayCompat.<init>|<init>(kotlin.Int){}[0]
+ open fun append(kotlin/Int, #A) // androidx.collection/SparseArrayCompat.append|append(kotlin.Int;1:0){}[0]
+ open fun clear() // androidx.collection/SparseArrayCompat.clear|clear(){}[0]
+ open fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/SparseArrayCompat.containsKey|containsKey(kotlin.Int){}[0]
+ open fun containsValue(#A): kotlin/Boolean // androidx.collection/SparseArrayCompat.containsValue|containsValue(1:0){}[0]
+ open fun get(kotlin/Int): #A? // androidx.collection/SparseArrayCompat.get|get(kotlin.Int){}[0]
+ open fun get(kotlin/Int, #A): #A // androidx.collection/SparseArrayCompat.get|get(kotlin.Int;1:0){}[0]
+ open fun indexOfKey(kotlin/Int): kotlin/Int // androidx.collection/SparseArrayCompat.indexOfKey|indexOfKey(kotlin.Int){}[0]
+ open fun indexOfValue(#A): kotlin/Int // androidx.collection/SparseArrayCompat.indexOfValue|indexOfValue(1:0){}[0]
+ open fun isEmpty(): kotlin/Boolean // androidx.collection/SparseArrayCompat.isEmpty|isEmpty(){}[0]
+ open fun keyAt(kotlin/Int): kotlin/Int // androidx.collection/SparseArrayCompat.keyAt|keyAt(kotlin.Int){}[0]
+ open fun put(kotlin/Int, #A) // androidx.collection/SparseArrayCompat.put|put(kotlin.Int;1:0){}[0]
+ open fun putAll(androidx.collection/SparseArrayCompat<out #A>) // androidx.collection/SparseArrayCompat.putAll|putAll(androidx.collection.SparseArrayCompat<out|1:0>){}[0]
+ open fun putIfAbsent(kotlin/Int, #A): #A? // androidx.collection/SparseArrayCompat.putIfAbsent|putIfAbsent(kotlin.Int;1:0){}[0]
+ open fun remove(kotlin/Int) // androidx.collection/SparseArrayCompat.remove|remove(kotlin.Int){}[0]
+ open fun remove(kotlin/Int, kotlin/Any?): kotlin/Boolean // androidx.collection/SparseArrayCompat.remove|remove(kotlin.Int;kotlin.Any?){}[0]
+ open fun removeAt(kotlin/Int) // androidx.collection/SparseArrayCompat.removeAt|removeAt(kotlin.Int){}[0]
+ open fun removeAtRange(kotlin/Int, kotlin/Int) // androidx.collection/SparseArrayCompat.removeAtRange|removeAtRange(kotlin.Int;kotlin.Int){}[0]
+ open fun replace(kotlin/Int, #A): #A? // androidx.collection/SparseArrayCompat.replace|replace(kotlin.Int;1:0){}[0]
+ open fun replace(kotlin/Int, #A, #A): kotlin/Boolean // androidx.collection/SparseArrayCompat.replace|replace(kotlin.Int;1:0;1:0){}[0]
+ open fun setValueAt(kotlin/Int, #A) // androidx.collection/SparseArrayCompat.setValueAt|setValueAt(kotlin.Int;1:0){}[0]
+ open fun size(): kotlin/Int // androidx.collection/SparseArrayCompat.size|size(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/SparseArrayCompat.toString|toString(){}[0]
+ open fun valueAt(kotlin/Int): #A // androidx.collection/SparseArrayCompat.valueAt|valueAt(kotlin.Int){}[0]
+}
+sealed class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/ScatterMap { // androidx.collection/ScatterMap|null[0]
+ constructor <init>() // androidx.collection/ScatterMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/ScatterMap.any|any(){}[0]
+ final fun asMap(): kotlin.collections/Map<#A, #B> // androidx.collection/ScatterMap.asMap|asMap(){}[0]
+ final fun contains(#A): kotlin/Boolean // androidx.collection/ScatterMap.contains|contains(1:0){}[0]
+ final fun containsKey(#A): kotlin/Boolean // androidx.collection/ScatterMap.containsKey|containsKey(1:0){}[0]
+ final fun containsValue(#B): kotlin/Boolean // androidx.collection/ScatterMap.containsValue|containsValue(1:1){}[0]
+ final fun count(): kotlin/Int // androidx.collection/ScatterMap.count|count(){}[0]
+ final fun get(#A): #B? // androidx.collection/ScatterMap.get|get(1:0){}[0]
+ final fun getOrDefault(#A, #B): #B // androidx.collection/ScatterMap.getOrDefault|getOrDefault(1:0;1:1){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/ScatterMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ScatterMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., kotlin/Function2<#A, #B, kotlin/CharSequence>? =...): kotlin/String // androidx.collection/ScatterMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<1:0,1:1,kotlin.CharSequence>?){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/ScatterMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<#A, #B, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ScatterMap.all|all(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<#A, #B, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ScatterMap.any|any(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<#A, #B, kotlin/Boolean>): kotlin/Int // androidx.collection/ScatterMap.count|count(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<#A, #B, kotlin/Unit>) // androidx.collection/ScatterMap.forEach|forEach(kotlin.Function2<1:0,1:1,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ScatterMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ScatterMap.forEachKey|forEachKey(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<#B, kotlin/Unit>) // androidx.collection/ScatterMap.forEachValue|forEachValue(kotlin.Function1<1:1,kotlin.Unit>){}[0]
+ final inline fun getOrElse(#A, kotlin/Function0<#B>): #B // androidx.collection/ScatterMap.getOrElse|getOrElse(1:0;kotlin.Function0<1:1>){}[0]
+ final val capacity // androidx.collection/ScatterMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/ScatterMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/ScatterMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/ScatterMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/ScatterMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/Array<kotlin/Any?> // androidx.collection/ScatterMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/Array<kotlin/Any?>) // androidx.collection/ScatterMap.keys.<set-keys>|<set-keys>(kotlin.Array<kotlin.Any?>){}[0]
+ final var metadata // androidx.collection/ScatterMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ScatterMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ScatterMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/ScatterMap.values|{}values[0]
+ final fun <get-values>(): kotlin/Array<kotlin/Any?> // androidx.collection/ScatterMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/Array<kotlin/Any?>) // androidx.collection/ScatterMap.values.<set-values>|<set-values>(kotlin.Array<kotlin.Any?>){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ScatterMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/ScatterMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/ScatterMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/FloatObjectMap { // androidx.collection/FloatObjectMap|null[0]
+ constructor <init>() // androidx.collection/FloatObjectMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/FloatObjectMap.any|any(){}[0]
+ final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatObjectMap.contains|contains(kotlin.Float){}[0]
+ final fun containsKey(kotlin/Float): kotlin/Boolean // androidx.collection/FloatObjectMap.containsKey|containsKey(kotlin.Float){}[0]
+ final fun containsValue(#A): kotlin/Boolean // androidx.collection/FloatObjectMap.containsValue|containsValue(1:0){}[0]
+ final fun count(): kotlin/Int // androidx.collection/FloatObjectMap.count|count(){}[0]
+ final fun get(kotlin/Float): #A? // androidx.collection/FloatObjectMap.get|get(kotlin.Float){}[0]
+ final fun getOrDefault(kotlin/Float, #A): #A // androidx.collection/FloatObjectMap.getOrDefault|getOrDefault(kotlin.Float;1:0){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatObjectMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatObjectMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/FloatObjectMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Float, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatObjectMap.all|all(kotlin.Function2<kotlin.Float,1:0,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Float, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatObjectMap.any|any(kotlin.Function2<kotlin.Float,1:0,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Float, #A, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatObjectMap.count|count(kotlin.Function2<kotlin.Float,1:0,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Float, #A, kotlin/Unit>) // androidx.collection/FloatObjectMap.forEach|forEach(kotlin.Function2<kotlin.Float,1:0,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatObjectMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatObjectMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/FloatObjectMap.forEachValue|forEachValue(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Float, kotlin/Function0<#A>): #A // androidx.collection/FloatObjectMap.getOrElse|getOrElse(kotlin.Float;kotlin.Function0<1:0>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Float, #A, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Float,1:0,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/FloatObjectMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/FloatObjectMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/FloatObjectMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/FloatObjectMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/FloatArray // androidx.collection/FloatObjectMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/FloatArray) // androidx.collection/FloatObjectMap.keys.<set-keys>|<set-keys>(kotlin.FloatArray){}[0]
+ final var metadata // androidx.collection/FloatObjectMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatObjectMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatObjectMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/FloatObjectMap.values|{}values[0]
+ final fun <get-values>(): kotlin/Array<kotlin/Any?> // androidx.collection/FloatObjectMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/Array<kotlin/Any?>) // androidx.collection/FloatObjectMap.values.<set-values>|<set-values>(kotlin.Array<kotlin.Any?>){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatObjectMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/FloatObjectMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/FloatObjectMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/IntObjectMap { // androidx.collection/IntObjectMap|null[0]
+ constructor <init>() // androidx.collection/IntObjectMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/IntObjectMap.any|any(){}[0]
+ final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntObjectMap.contains|contains(kotlin.Int){}[0]
+ final fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/IntObjectMap.containsKey|containsKey(kotlin.Int){}[0]
+ final fun containsValue(#A): kotlin/Boolean // androidx.collection/IntObjectMap.containsValue|containsValue(1:0){}[0]
+ final fun count(): kotlin/Int // androidx.collection/IntObjectMap.count|count(){}[0]
+ final fun get(kotlin/Int): #A? // androidx.collection/IntObjectMap.get|get(kotlin.Int){}[0]
+ final fun getOrDefault(kotlin/Int, #A): #A // androidx.collection/IntObjectMap.getOrDefault|getOrDefault(kotlin.Int;1:0){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/IntObjectMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntObjectMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/IntObjectMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Int, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntObjectMap.all|all(kotlin.Function2<kotlin.Int,1:0,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Int, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntObjectMap.any|any(kotlin.Function2<kotlin.Int,1:0,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Int, #A, kotlin/Boolean>): kotlin/Int // androidx.collection/IntObjectMap.count|count(kotlin.Function2<kotlin.Int,1:0,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.collection/IntObjectMap.forEach|forEach(kotlin.Function2<kotlin.Int,1:0,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntObjectMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntObjectMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/IntObjectMap.forEachValue|forEachValue(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Int, kotlin/Function0<#A>): #A // androidx.collection/IntObjectMap.getOrElse|getOrElse(kotlin.Int;kotlin.Function0<1:0>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Int, #A, kotlin/CharSequence>): kotlin/String // androidx.collection/IntObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Int,1:0,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/IntObjectMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/IntObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/IntObjectMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/IntObjectMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/IntObjectMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/IntArray // androidx.collection/IntObjectMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/IntArray) // androidx.collection/IntObjectMap.keys.<set-keys>|<set-keys>(kotlin.IntArray){}[0]
+ final var metadata // androidx.collection/IntObjectMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntObjectMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntObjectMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/IntObjectMap.values|{}values[0]
+ final fun <get-values>(): kotlin/Array<kotlin/Any?> // androidx.collection/IntObjectMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/Array<kotlin/Any?>) // androidx.collection/IntObjectMap.values.<set-values>|<set-values>(kotlin.Array<kotlin.Any?>){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntObjectMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/IntObjectMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/IntObjectMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/LongObjectMap { // androidx.collection/LongObjectMap|null[0]
+ constructor <init>() // androidx.collection/LongObjectMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/LongObjectMap.any|any(){}[0]
+ final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongObjectMap.contains|contains(kotlin.Long){}[0]
+ final fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongObjectMap.containsKey|containsKey(kotlin.Long){}[0]
+ final fun containsValue(#A): kotlin/Boolean // androidx.collection/LongObjectMap.containsValue|containsValue(1:0){}[0]
+ final fun count(): kotlin/Int // androidx.collection/LongObjectMap.count|count(){}[0]
+ final fun get(kotlin/Long): #A? // androidx.collection/LongObjectMap.get|get(kotlin.Long){}[0]
+ final fun getOrDefault(kotlin/Long, #A): #A // androidx.collection/LongObjectMap.getOrDefault|getOrDefault(kotlin.Long;1:0){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/LongObjectMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongObjectMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/LongObjectMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Long, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongObjectMap.all|all(kotlin.Function2<kotlin.Long,1:0,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Long, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongObjectMap.any|any(kotlin.Function2<kotlin.Long,1:0,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Long, #A, kotlin/Boolean>): kotlin/Int // androidx.collection/LongObjectMap.count|count(kotlin.Function2<kotlin.Long,1:0,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Long, #A, kotlin/Unit>) // androidx.collection/LongObjectMap.forEach|forEach(kotlin.Function2<kotlin.Long,1:0,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongObjectMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongObjectMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/LongObjectMap.forEachValue|forEachValue(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Long, kotlin/Function0<#A>): #A // androidx.collection/LongObjectMap.getOrElse|getOrElse(kotlin.Long;kotlin.Function0<1:0>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Long, #A, kotlin/CharSequence>): kotlin/String // androidx.collection/LongObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Long,1:0,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/LongObjectMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/LongObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/LongObjectMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/LongObjectMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/LongObjectMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/LongArray // androidx.collection/LongObjectMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/LongArray) // androidx.collection/LongObjectMap.keys.<set-keys>|<set-keys>(kotlin.LongArray){}[0]
+ final var metadata // androidx.collection/LongObjectMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongObjectMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongObjectMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/LongObjectMap.values|{}values[0]
+ final fun <get-values>(): kotlin/Array<kotlin/Any?> // androidx.collection/LongObjectMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/Array<kotlin/Any?>) // androidx.collection/LongObjectMap.values.<set-values>|<set-values>(kotlin.Array<kotlin.Any?>){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongObjectMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/LongObjectMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/LongObjectMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ObjectFloatMap { // androidx.collection/ObjectFloatMap|null[0]
+ constructor <init>() // androidx.collection/ObjectFloatMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/ObjectFloatMap.any|any(){}[0]
+ final fun contains(#A): kotlin/Boolean // androidx.collection/ObjectFloatMap.contains|contains(1:0){}[0]
+ final fun containsKey(#A): kotlin/Boolean // androidx.collection/ObjectFloatMap.containsKey|containsKey(1:0){}[0]
+ final fun containsValue(kotlin/Float): kotlin/Boolean // androidx.collection/ObjectFloatMap.containsValue|containsValue(kotlin.Float){}[0]
+ final fun count(): kotlin/Int // androidx.collection/ObjectFloatMap.count|count(){}[0]
+ final fun findKeyIndex(#A): kotlin/Int // androidx.collection/ObjectFloatMap.findKeyIndex|findKeyIndex(1:0){}[0]
+ final fun get(#A): kotlin/Float // androidx.collection/ObjectFloatMap.get|get(1:0){}[0]
+ final fun getOrDefault(#A, kotlin/Float): kotlin/Float // androidx.collection/ObjectFloatMap.getOrDefault|getOrDefault(1:0;kotlin.Float){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/ObjectFloatMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ObjectFloatMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/ObjectFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/ObjectFloatMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<#A, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectFloatMap.all|all(kotlin.Function2<1:0,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<#A, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectFloatMap.any|any(kotlin.Function2<1:0,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<#A, kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectFloatMap.count|count(kotlin.Function2<1:0,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<#A, kotlin/Float, kotlin/Unit>) // androidx.collection/ObjectFloatMap.forEach|forEach(kotlin.Function2<1:0,kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectFloatMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectFloatMap.forEachKey|forEachKey(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/ObjectFloatMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun getOrElse(#A, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/ObjectFloatMap.getOrElse|getOrElse(1:0;kotlin.Function0<kotlin.Float>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<#A, kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/ObjectFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<1:0,kotlin.Float,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/ObjectFloatMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/ObjectFloatMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/ObjectFloatMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/ObjectFloatMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/Array<kotlin/Any?> // androidx.collection/ObjectFloatMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/Array<kotlin/Any?>) // androidx.collection/ObjectFloatMap.keys.<set-keys>|<set-keys>(kotlin.Array<kotlin.Any?>){}[0]
+ final var metadata // androidx.collection/ObjectFloatMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ObjectFloatMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ObjectFloatMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/ObjectFloatMap.values|{}values[0]
+ final fun <get-values>(): kotlin/FloatArray // androidx.collection/ObjectFloatMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/FloatArray) // androidx.collection/ObjectFloatMap.values.<set-values>|<set-values>(kotlin.FloatArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ObjectFloatMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/ObjectFloatMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/ObjectFloatMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ObjectIntMap { // androidx.collection/ObjectIntMap|null[0]
+ constructor <init>() // androidx.collection/ObjectIntMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/ObjectIntMap.any|any(){}[0]
+ final fun contains(#A): kotlin/Boolean // androidx.collection/ObjectIntMap.contains|contains(1:0){}[0]
+ final fun containsKey(#A): kotlin/Boolean // androidx.collection/ObjectIntMap.containsKey|containsKey(1:0){}[0]
+ final fun containsValue(kotlin/Int): kotlin/Boolean // androidx.collection/ObjectIntMap.containsValue|containsValue(kotlin.Int){}[0]
+ final fun count(): kotlin/Int // androidx.collection/ObjectIntMap.count|count(){}[0]
+ final fun findKeyIndex(#A): kotlin/Int // androidx.collection/ObjectIntMap.findKeyIndex|findKeyIndex(1:0){}[0]
+ final fun get(#A): kotlin/Int // androidx.collection/ObjectIntMap.get|get(1:0){}[0]
+ final fun getOrDefault(#A, kotlin/Int): kotlin/Int // androidx.collection/ObjectIntMap.getOrDefault|getOrDefault(1:0;kotlin.Int){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/ObjectIntMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ObjectIntMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/ObjectIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/ObjectIntMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<#A, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectIntMap.all|all(kotlin.Function2<1:0,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<#A, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectIntMap.any|any(kotlin.Function2<1:0,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<#A, kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectIntMap.count|count(kotlin.Function2<1:0,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<#A, kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectIntMap.forEach|forEach(kotlin.Function2<1:0,kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectIntMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectIntMap.forEachKey|forEachKey(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectIntMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun getOrElse(#A, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/ObjectIntMap.getOrElse|getOrElse(1:0;kotlin.Function0<kotlin.Int>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<#A, kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/ObjectIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<1:0,kotlin.Int,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/ObjectIntMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/ObjectIntMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/ObjectIntMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/ObjectIntMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/Array<kotlin/Any?> // androidx.collection/ObjectIntMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/Array<kotlin/Any?>) // androidx.collection/ObjectIntMap.keys.<set-keys>|<set-keys>(kotlin.Array<kotlin.Any?>){}[0]
+ final var metadata // androidx.collection/ObjectIntMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ObjectIntMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ObjectIntMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/ObjectIntMap.values|{}values[0]
+ final fun <get-values>(): kotlin/IntArray // androidx.collection/ObjectIntMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/IntArray) // androidx.collection/ObjectIntMap.values.<set-values>|<set-values>(kotlin.IntArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ObjectIntMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/ObjectIntMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/ObjectIntMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ObjectList { // androidx.collection/ObjectList|null[0]
+ abstract fun asList(): kotlin.collections/List<#A> // androidx.collection/ObjectList.asList|asList(){}[0]
+ constructor <init>(kotlin/Int) // androidx.collection/ObjectList.<init>|<init>(kotlin.Int){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/ObjectList.any|any(){}[0]
+ final fun contains(#A): kotlin/Boolean // androidx.collection/ObjectList.contains|contains(1:0){}[0]
+ final fun containsAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/ObjectList.containsAll|containsAll(androidx.collection.ObjectList<1:0>){}[0]
+ final fun containsAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/ObjectList.containsAll|containsAll(kotlin.collections.Iterable<1:0>){}[0]
+ final fun containsAll(kotlin.collections/List<#A>): kotlin/Boolean // androidx.collection/ObjectList.containsAll|containsAll(kotlin.collections.List<1:0>){}[0]
+ final fun containsAll(kotlin/Array<#A>): kotlin/Boolean // androidx.collection/ObjectList.containsAll|containsAll(kotlin.Array<1:0>){}[0]
+ final fun count(): kotlin/Int // androidx.collection/ObjectList.count|count(){}[0]
+ final fun elementAt(kotlin/Int): #A // androidx.collection/ObjectList.elementAt|elementAt(kotlin.Int){}[0]
+ final fun first(): #A // androidx.collection/ObjectList.first|first(){}[0]
+ final fun get(kotlin/Int): #A // androidx.collection/ObjectList.get|get(kotlin.Int){}[0]
+ final fun indexOf(#A): kotlin/Int // androidx.collection/ObjectList.indexOf|indexOf(1:0){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/ObjectList.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ObjectList.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., kotlin/Function1<#A, kotlin/CharSequence>? =...): kotlin/String // androidx.collection/ObjectList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<1:0,kotlin.CharSequence>?){}[0]
+ final fun last(): #A // androidx.collection/ObjectList.last|last(){}[0]
+ final fun lastIndexOf(#A): kotlin/Int // androidx.collection/ObjectList.lastIndexOf|lastIndexOf(1:0){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/ObjectList.none|none(){}[0]
+ final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, #A, #A1>): #A1 // androidx.collection/ObjectList.fold|fold(0:0;kotlin.Function2<0:0,1:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, #A, #A1>): #A1 // androidx.collection/ObjectList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,1:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<#A, #A1, #A1>): #A1 // androidx.collection/ObjectList.foldRight|foldRight(0:0;kotlin.Function2<1:0,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, #A, #A1, #A1>): #A1 // androidx.collection/ObjectList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,1:0,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun any(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectList.any|any(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectList.count|count(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, #A>): #A // androidx.collection/ObjectList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,1:0>){}[0]
+ final inline fun first(kotlin/Function1<#A, kotlin/Boolean>): #A // androidx.collection/ObjectList.first|first(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun firstOrNull(): #A? // androidx.collection/ObjectList.firstOrNull|firstOrNull(){}[0]
+ final inline fun firstOrNull(kotlin/Function1<#A, kotlin/Boolean>): #A? // androidx.collection/ObjectList.firstOrNull|firstOrNull(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectList.forEach|forEach(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.collection/ObjectList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,1:0,kotlin.Unit>){}[0]
+ final inline fun forEachReversed(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectList.forEachReversed|forEachReversed(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.collection/ObjectList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,1:0,kotlin.Unit>){}[0]
+ final inline fun indexOfFirst(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectList.indexOfFirst|indexOfFirst(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun indexOfLast(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectList.indexOfLast|indexOfLast(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun last(kotlin/Function1<#A, kotlin/Boolean>): #A // androidx.collection/ObjectList.last|last(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun lastOrNull(): #A? // androidx.collection/ObjectList.lastOrNull|lastOrNull(){}[0]
+ final inline fun lastOrNull(kotlin/Function1<#A, kotlin/Boolean>): #A? // androidx.collection/ObjectList.lastOrNull|lastOrNull(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun reversedAny(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectList.reversedAny|reversedAny(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final val indices // androidx.collection/ObjectList.indices|{}indices[0]
+ final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/ObjectList.indices.<get-indices>|<get-indices>(){}[0]
+ final val lastIndex // androidx.collection/ObjectList.lastIndex|{}lastIndex[0]
+ final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/ObjectList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+ final val size // androidx.collection/ObjectList.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/ObjectList.size.<get-size>|<get-size>(){}[0]
+ final var _size // androidx.collection/ObjectList._size|{}_size[0]
+ final fun <get-_size>(): kotlin/Int // androidx.collection/ObjectList._size.<get-_size>|<get-_size>(){}[0]
+ final fun <set-_size>(kotlin/Int) // androidx.collection/ObjectList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+ final var content // androidx.collection/ObjectList.content|{}content[0]
+ final fun <get-content>(): kotlin/Array<kotlin/Any?> // androidx.collection/ObjectList.content.<get-content>|<get-content>(){}[0]
+ final fun <set-content>(kotlin/Array<kotlin/Any?>) // androidx.collection/ObjectList.content.<set-content>|<set-content>(kotlin.Array<kotlin.Any?>){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ObjectList.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/ObjectList.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/ObjectList.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ObjectLongMap { // androidx.collection/ObjectLongMap|null[0]
+ constructor <init>() // androidx.collection/ObjectLongMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/ObjectLongMap.any|any(){}[0]
+ final fun contains(#A): kotlin/Boolean // androidx.collection/ObjectLongMap.contains|contains(1:0){}[0]
+ final fun containsKey(#A): kotlin/Boolean // androidx.collection/ObjectLongMap.containsKey|containsKey(1:0){}[0]
+ final fun containsValue(kotlin/Long): kotlin/Boolean // androidx.collection/ObjectLongMap.containsValue|containsValue(kotlin.Long){}[0]
+ final fun count(): kotlin/Int // androidx.collection/ObjectLongMap.count|count(){}[0]
+ final fun findKeyIndex(#A): kotlin/Int // androidx.collection/ObjectLongMap.findKeyIndex|findKeyIndex(1:0){}[0]
+ final fun get(#A): kotlin/Long // androidx.collection/ObjectLongMap.get|get(1:0){}[0]
+ final fun getOrDefault(#A, kotlin/Long): kotlin/Long // androidx.collection/ObjectLongMap.getOrDefault|getOrDefault(1:0;kotlin.Long){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/ObjectLongMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ObjectLongMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/ObjectLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/ObjectLongMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<#A, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectLongMap.all|all(kotlin.Function2<1:0,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<#A, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectLongMap.any|any(kotlin.Function2<1:0,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<#A, kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectLongMap.count|count(kotlin.Function2<1:0,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<#A, kotlin/Long, kotlin/Unit>) // androidx.collection/ObjectLongMap.forEach|forEach(kotlin.Function2<1:0,kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectLongMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectLongMap.forEachKey|forEachKey(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/ObjectLongMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun getOrElse(#A, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/ObjectLongMap.getOrElse|getOrElse(1:0;kotlin.Function0<kotlin.Long>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<#A, kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/ObjectLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<1:0,kotlin.Long,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/ObjectLongMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/ObjectLongMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/ObjectLongMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/ObjectLongMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/Array<kotlin/Any?> // androidx.collection/ObjectLongMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/Array<kotlin/Any?>) // androidx.collection/ObjectLongMap.keys.<set-keys>|<set-keys>(kotlin.Array<kotlin.Any?>){}[0]
+ final var metadata // androidx.collection/ObjectLongMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ObjectLongMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ObjectLongMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/ObjectLongMap.values|{}values[0]
+ final fun <get-values>(): kotlin/LongArray // androidx.collection/ObjectLongMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/LongArray) // androidx.collection/ObjectLongMap.values.<set-values>|<set-values>(kotlin.LongArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ObjectLongMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/ObjectLongMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/ObjectLongMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ScatterSet { // androidx.collection/ScatterSet|null[0]
+ constructor <init>() // androidx.collection/ScatterSet.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/ScatterSet.any|any(){}[0]
+ final fun asSet(): kotlin.collections/Set<#A> // androidx.collection/ScatterSet.asSet|asSet(){}[0]
+ final fun contains(#A): kotlin/Boolean // androidx.collection/ScatterSet.contains|contains(1:0){}[0]
+ final fun count(): kotlin/Int // androidx.collection/ScatterSet.count|count(){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/ScatterSet.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ScatterSet.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., kotlin/Function1<#A, kotlin/CharSequence>? =...): kotlin/String // androidx.collection/ScatterSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<1:0,kotlin.CharSequence>?){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/ScatterSet.none|none(){}[0]
+ final inline fun all(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ScatterSet.all|all(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ScatterSet.any|any(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Int // androidx.collection/ScatterSet.count|count(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun first(): #A // androidx.collection/ScatterSet.first|first(){}[0]
+ final inline fun first(kotlin/Function1<#A, kotlin/Boolean>): #A // androidx.collection/ScatterSet.first|first(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun firstOrNull(kotlin/Function1<#A, kotlin/Boolean>): #A? // androidx.collection/ScatterSet.firstOrNull|firstOrNull(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ScatterSet.forEach|forEach(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+ final inline fun forEachIndex(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ScatterSet.forEachIndex|forEachIndex(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final val capacity // androidx.collection/ScatterSet.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/ScatterSet.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/ScatterSet.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/ScatterSet.size.<get-size>|<get-size>(){}[0]
+ final var elements // androidx.collection/ScatterSet.elements|{}elements[0]
+ final fun <get-elements>(): kotlin/Array<kotlin/Any?> // androidx.collection/ScatterSet.elements.<get-elements>|<get-elements>(){}[0]
+ final fun <set-elements>(kotlin/Array<kotlin/Any?>) // androidx.collection/ScatterSet.elements.<set-elements>|<set-elements>(kotlin.Array<kotlin.Any?>){}[0]
+ final var metadata // androidx.collection/ScatterSet.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ScatterSet.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ScatterSet.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ScatterSet.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/ScatterSet.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/ScatterSet.toString|toString(){}[0]
+}
+sealed class androidx.collection/DoubleList { // androidx.collection/DoubleList|null[0]
+ constructor <init>(kotlin/Int) // androidx.collection/DoubleList.<init>|<init>(kotlin.Int){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/DoubleList.any|any(){}[0]
+ final fun contains(kotlin/Double): kotlin/Boolean // androidx.collection/DoubleList.contains|contains(kotlin.Double){}[0]
+ final fun containsAll(androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/DoubleList.containsAll|containsAll(androidx.collection.DoubleList){}[0]
+ final fun count(): kotlin/Int // androidx.collection/DoubleList.count|count(){}[0]
+ final fun elementAt(kotlin/Int): kotlin/Double // androidx.collection/DoubleList.elementAt|elementAt(kotlin.Int){}[0]
+ final fun first(): kotlin/Double // androidx.collection/DoubleList.first|first(){}[0]
+ final fun get(kotlin/Int): kotlin/Double // androidx.collection/DoubleList.get|get(kotlin.Int){}[0]
+ final fun indexOf(kotlin/Double): kotlin/Int // androidx.collection/DoubleList.indexOf|indexOf(kotlin.Double){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/DoubleList.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/DoubleList.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/DoubleList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun last(): kotlin/Double // androidx.collection/DoubleList.last|last(){}[0]
+ final fun lastIndexOf(kotlin/Double): kotlin/Int // androidx.collection/DoubleList.lastIndexOf|lastIndexOf(kotlin.Double){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/DoubleList.none|none(){}[0]
+ final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, kotlin/Double, #A1>): #A1 // androidx.collection/DoubleList.fold|fold(0:0;kotlin.Function2<0:0,kotlin.Double,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, kotlin/Double, #A1>): #A1 // androidx.collection/DoubleList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,kotlin.Double,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<kotlin/Double, #A1, #A1>): #A1 // androidx.collection/DoubleList.foldRight|foldRight(0:0;kotlin.Function2<kotlin.Double,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, kotlin/Double, #A1, #A1>): #A1 // androidx.collection/DoubleList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,kotlin.Double,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun any(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Boolean // androidx.collection/DoubleList.any|any(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Int // androidx.collection/DoubleList.count|count(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+ final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, kotlin/Double>): kotlin/Double // androidx.collection/DoubleList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,kotlin.Double>){}[0]
+ final inline fun first(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Double // androidx.collection/DoubleList.first|first(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function1<kotlin/Double, kotlin/Unit>) // androidx.collection/DoubleList.forEach|forEach(kotlin.Function1<kotlin.Double,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, kotlin/Double, kotlin/Unit>) // androidx.collection/DoubleList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,kotlin.Double,kotlin.Unit>){}[0]
+ final inline fun forEachReversed(kotlin/Function1<kotlin/Double, kotlin/Unit>) // androidx.collection/DoubleList.forEachReversed|forEachReversed(kotlin.Function1<kotlin.Double,kotlin.Unit>){}[0]
+ final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, kotlin/Double, kotlin/Unit>) // androidx.collection/DoubleList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,kotlin.Double,kotlin.Unit>){}[0]
+ final inline fun indexOfFirst(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Int // androidx.collection/DoubleList.indexOfFirst|indexOfFirst(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+ final inline fun indexOfLast(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Int // androidx.collection/DoubleList.indexOfLast|indexOfLast(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Double, kotlin/CharSequence>): kotlin/String // androidx.collection/DoubleList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Double,kotlin.CharSequence>){}[0]
+ final inline fun last(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Double // androidx.collection/DoubleList.last|last(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+ final inline fun reversedAny(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Boolean // androidx.collection/DoubleList.reversedAny|reversedAny(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+ final val indices // androidx.collection/DoubleList.indices|{}indices[0]
+ final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/DoubleList.indices.<get-indices>|<get-indices>(){}[0]
+ final val lastIndex // androidx.collection/DoubleList.lastIndex|{}lastIndex[0]
+ final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/DoubleList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+ final val size // androidx.collection/DoubleList.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/DoubleList.size.<get-size>|<get-size>(){}[0]
+ final var _size // androidx.collection/DoubleList._size|{}_size[0]
+ final fun <get-_size>(): kotlin/Int // androidx.collection/DoubleList._size.<get-_size>|<get-_size>(){}[0]
+ final fun <set-_size>(kotlin/Int) // androidx.collection/DoubleList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+ final var content // androidx.collection/DoubleList.content|{}content[0]
+ final fun <get-content>(): kotlin/DoubleArray // androidx.collection/DoubleList.content.<get-content>|<get-content>(){}[0]
+ final fun <set-content>(kotlin/DoubleArray) // androidx.collection/DoubleList.content.<set-content>|<set-content>(kotlin.DoubleArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/DoubleList.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/DoubleList.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/DoubleList.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatFloatMap { // androidx.collection/FloatFloatMap|null[0]
+ constructor <init>() // androidx.collection/FloatFloatMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/FloatFloatMap.any|any(){}[0]
+ final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatFloatMap.contains|contains(kotlin.Float){}[0]
+ final fun containsKey(kotlin/Float): kotlin/Boolean // androidx.collection/FloatFloatMap.containsKey|containsKey(kotlin.Float){}[0]
+ final fun containsValue(kotlin/Float): kotlin/Boolean // androidx.collection/FloatFloatMap.containsValue|containsValue(kotlin.Float){}[0]
+ final fun count(): kotlin/Int // androidx.collection/FloatFloatMap.count|count(){}[0]
+ final fun findKeyIndex(kotlin/Float): kotlin/Int // androidx.collection/FloatFloatMap.findKeyIndex|findKeyIndex(kotlin.Float){}[0]
+ final fun get(kotlin/Float): kotlin/Float // androidx.collection/FloatFloatMap.get|get(kotlin.Float){}[0]
+ final fun getOrDefault(kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/FloatFloatMap.getOrDefault|getOrDefault(kotlin.Float;kotlin.Float){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatFloatMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatFloatMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/FloatFloatMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatFloatMap.all|all(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatFloatMap.any|any(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatFloatMap.count|count(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Unit>) // androidx.collection/FloatFloatMap.forEach|forEach(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatFloatMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatFloatMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatFloatMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Float, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/FloatFloatMap.getOrElse|getOrElse(kotlin.Float;kotlin.Function0<kotlin.Float>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/FloatFloatMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/FloatFloatMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/FloatFloatMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/FloatFloatMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/FloatArray // androidx.collection/FloatFloatMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/FloatArray) // androidx.collection/FloatFloatMap.keys.<set-keys>|<set-keys>(kotlin.FloatArray){}[0]
+ final var metadata // androidx.collection/FloatFloatMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatFloatMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatFloatMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/FloatFloatMap.values|{}values[0]
+ final fun <get-values>(): kotlin/FloatArray // androidx.collection/FloatFloatMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/FloatArray) // androidx.collection/FloatFloatMap.values.<set-values>|<set-values>(kotlin.FloatArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatFloatMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/FloatFloatMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/FloatFloatMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatIntMap { // androidx.collection/FloatIntMap|null[0]
+ constructor <init>() // androidx.collection/FloatIntMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/FloatIntMap.any|any(){}[0]
+ final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatIntMap.contains|contains(kotlin.Float){}[0]
+ final fun containsKey(kotlin/Float): kotlin/Boolean // androidx.collection/FloatIntMap.containsKey|containsKey(kotlin.Float){}[0]
+ final fun containsValue(kotlin/Int): kotlin/Boolean // androidx.collection/FloatIntMap.containsValue|containsValue(kotlin.Int){}[0]
+ final fun count(): kotlin/Int // androidx.collection/FloatIntMap.count|count(){}[0]
+ final fun findKeyIndex(kotlin/Float): kotlin/Int // androidx.collection/FloatIntMap.findKeyIndex|findKeyIndex(kotlin.Float){}[0]
+ final fun get(kotlin/Float): kotlin/Int // androidx.collection/FloatIntMap.get|get(kotlin.Float){}[0]
+ final fun getOrDefault(kotlin/Float, kotlin/Int): kotlin/Int // androidx.collection/FloatIntMap.getOrDefault|getOrDefault(kotlin.Float;kotlin.Int){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatIntMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatIntMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/FloatIntMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatIntMap.all|all(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatIntMap.any|any(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatIntMap.count|count(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Unit>) // androidx.collection/FloatIntMap.forEach|forEach(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatIntMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatIntMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatIntMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Float, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/FloatIntMap.getOrElse|getOrElse(kotlin.Float;kotlin.Function0<kotlin.Int>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/FloatIntMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/FloatIntMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/FloatIntMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/FloatIntMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/FloatArray // androidx.collection/FloatIntMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/FloatArray) // androidx.collection/FloatIntMap.keys.<set-keys>|<set-keys>(kotlin.FloatArray){}[0]
+ final var metadata // androidx.collection/FloatIntMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatIntMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatIntMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/FloatIntMap.values|{}values[0]
+ final fun <get-values>(): kotlin/IntArray // androidx.collection/FloatIntMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/IntArray) // androidx.collection/FloatIntMap.values.<set-values>|<set-values>(kotlin.IntArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatIntMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/FloatIntMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/FloatIntMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatList { // androidx.collection/FloatList|null[0]
+ constructor <init>(kotlin/Int) // androidx.collection/FloatList.<init>|<init>(kotlin.Int){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/FloatList.any|any(){}[0]
+ final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatList.contains|contains(kotlin.Float){}[0]
+ final fun containsAll(androidx.collection/FloatList): kotlin/Boolean // androidx.collection/FloatList.containsAll|containsAll(androidx.collection.FloatList){}[0]
+ final fun count(): kotlin/Int // androidx.collection/FloatList.count|count(){}[0]
+ final fun elementAt(kotlin/Int): kotlin/Float // androidx.collection/FloatList.elementAt|elementAt(kotlin.Int){}[0]
+ final fun first(): kotlin/Float // androidx.collection/FloatList.first|first(){}[0]
+ final fun get(kotlin/Int): kotlin/Float // androidx.collection/FloatList.get|get(kotlin.Int){}[0]
+ final fun indexOf(kotlin/Float): kotlin/Int // androidx.collection/FloatList.indexOf|indexOf(kotlin.Float){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatList.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatList.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun last(): kotlin/Float // androidx.collection/FloatList.last|last(){}[0]
+ final fun lastIndexOf(kotlin/Float): kotlin/Int // androidx.collection/FloatList.lastIndexOf|lastIndexOf(kotlin.Float){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/FloatList.none|none(){}[0]
+ final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, kotlin/Float, #A1>): #A1 // androidx.collection/FloatList.fold|fold(0:0;kotlin.Function2<0:0,kotlin.Float,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, kotlin/Float, #A1>): #A1 // androidx.collection/FloatList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,kotlin.Float,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<kotlin/Float, #A1, #A1>): #A1 // androidx.collection/FloatList.foldRight|foldRight(0:0;kotlin.Function2<kotlin.Float,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, kotlin/Float, #A1, #A1>): #A1 // androidx.collection/FloatList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,kotlin.Float,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun any(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatList.any|any(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatList.count|count(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, kotlin/Float>): kotlin/Float // androidx.collection/FloatList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,kotlin.Float>){}[0]
+ final inline fun first(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Float // androidx.collection/FloatList.first|first(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatList.forEach|forEach(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Unit>) // androidx.collection/FloatList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachReversed(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatList.forEachReversed|forEachReversed(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Unit>) // androidx.collection/FloatList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun indexOfFirst(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatList.indexOfFirst|indexOfFirst(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun indexOfLast(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatList.indexOfLast|indexOfLast(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Float,kotlin.CharSequence>){}[0]
+ final inline fun last(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Float // androidx.collection/FloatList.last|last(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun reversedAny(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatList.reversedAny|reversedAny(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final val indices // androidx.collection/FloatList.indices|{}indices[0]
+ final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/FloatList.indices.<get-indices>|<get-indices>(){}[0]
+ final val lastIndex // androidx.collection/FloatList.lastIndex|{}lastIndex[0]
+ final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/FloatList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+ final val size // androidx.collection/FloatList.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/FloatList.size.<get-size>|<get-size>(){}[0]
+ final var _size // androidx.collection/FloatList._size|{}_size[0]
+ final fun <get-_size>(): kotlin/Int // androidx.collection/FloatList._size.<get-_size>|<get-_size>(){}[0]
+ final fun <set-_size>(kotlin/Int) // androidx.collection/FloatList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+ final var content // androidx.collection/FloatList.content|{}content[0]
+ final fun <get-content>(): kotlin/FloatArray // androidx.collection/FloatList.content.<get-content>|<get-content>(){}[0]
+ final fun <set-content>(kotlin/FloatArray) // androidx.collection/FloatList.content.<set-content>|<set-content>(kotlin.FloatArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatList.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/FloatList.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/FloatList.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatLongMap { // androidx.collection/FloatLongMap|null[0]
+ constructor <init>() // androidx.collection/FloatLongMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/FloatLongMap.any|any(){}[0]
+ final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatLongMap.contains|contains(kotlin.Float){}[0]
+ final fun containsKey(kotlin/Float): kotlin/Boolean // androidx.collection/FloatLongMap.containsKey|containsKey(kotlin.Float){}[0]
+ final fun containsValue(kotlin/Long): kotlin/Boolean // androidx.collection/FloatLongMap.containsValue|containsValue(kotlin.Long){}[0]
+ final fun count(): kotlin/Int // androidx.collection/FloatLongMap.count|count(){}[0]
+ final fun findKeyIndex(kotlin/Float): kotlin/Int // androidx.collection/FloatLongMap.findKeyIndex|findKeyIndex(kotlin.Float){}[0]
+ final fun get(kotlin/Float): kotlin/Long // androidx.collection/FloatLongMap.get|get(kotlin.Float){}[0]
+ final fun getOrDefault(kotlin/Float, kotlin/Long): kotlin/Long // androidx.collection/FloatLongMap.getOrDefault|getOrDefault(kotlin.Float;kotlin.Long){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatLongMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatLongMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/FloatLongMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatLongMap.all|all(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatLongMap.any|any(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatLongMap.count|count(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Unit>) // androidx.collection/FloatLongMap.forEach|forEach(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatLongMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatLongMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/FloatLongMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Float, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/FloatLongMap.getOrElse|getOrElse(kotlin.Float;kotlin.Function0<kotlin.Long>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/FloatLongMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/FloatLongMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/FloatLongMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/FloatLongMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/FloatArray // androidx.collection/FloatLongMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/FloatArray) // androidx.collection/FloatLongMap.keys.<set-keys>|<set-keys>(kotlin.FloatArray){}[0]
+ final var metadata // androidx.collection/FloatLongMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatLongMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatLongMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/FloatLongMap.values|{}values[0]
+ final fun <get-values>(): kotlin/LongArray // androidx.collection/FloatLongMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/LongArray) // androidx.collection/FloatLongMap.values.<set-values>|<set-values>(kotlin.LongArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatLongMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/FloatLongMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/FloatLongMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatSet { // androidx.collection/FloatSet|null[0]
+ constructor <init>() // androidx.collection/FloatSet.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/FloatSet.any|any(){}[0]
+ final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatSet.contains|contains(kotlin.Float){}[0]
+ final fun count(): kotlin/Int // androidx.collection/FloatSet.count|count(){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatSet.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatSet.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/FloatSet.none|none(){}[0]
+ final inline fun all(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatSet.all|all(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatSet.any|any(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatSet.count|count(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun first(): kotlin/Float // androidx.collection/FloatSet.first|first(){}[0]
+ final inline fun first(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Float // androidx.collection/FloatSet.first|first(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatSet.forEach|forEach(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachIndex(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatSet.forEachIndex|forEachIndex(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Float,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/FloatSet.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatSet.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/FloatSet.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/FloatSet.size.<get-size>|<get-size>(){}[0]
+ final var elements // androidx.collection/FloatSet.elements|{}elements[0]
+ final fun <get-elements>(): kotlin/FloatArray // androidx.collection/FloatSet.elements.<get-elements>|<get-elements>(){}[0]
+ final fun <set-elements>(kotlin/FloatArray) // androidx.collection/FloatSet.elements.<set-elements>|<set-elements>(kotlin.FloatArray){}[0]
+ final var metadata // androidx.collection/FloatSet.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatSet.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatSet.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatSet.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/FloatSet.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/FloatSet.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntFloatMap { // androidx.collection/IntFloatMap|null[0]
+ constructor <init>() // androidx.collection/IntFloatMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/IntFloatMap.any|any(){}[0]
+ final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntFloatMap.contains|contains(kotlin.Int){}[0]
+ final fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/IntFloatMap.containsKey|containsKey(kotlin.Int){}[0]
+ final fun containsValue(kotlin/Float): kotlin/Boolean // androidx.collection/IntFloatMap.containsValue|containsValue(kotlin.Float){}[0]
+ final fun count(): kotlin/Int // androidx.collection/IntFloatMap.count|count(){}[0]
+ final fun findKeyIndex(kotlin/Int): kotlin/Int // androidx.collection/IntFloatMap.findKeyIndex|findKeyIndex(kotlin.Int){}[0]
+ final fun get(kotlin/Int): kotlin/Float // androidx.collection/IntFloatMap.get|get(kotlin.Int){}[0]
+ final fun getOrDefault(kotlin/Int, kotlin/Float): kotlin/Float // androidx.collection/IntFloatMap.getOrDefault|getOrDefault(kotlin.Int;kotlin.Float){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/IntFloatMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntFloatMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/IntFloatMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntFloatMap.all|all(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntFloatMap.any|any(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/IntFloatMap.count|count(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Unit>) // androidx.collection/IntFloatMap.forEach|forEach(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntFloatMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntFloatMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/IntFloatMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Int, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/IntFloatMap.getOrElse|getOrElse(kotlin.Int;kotlin.Function0<kotlin.Float>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/IntFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/IntFloatMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/IntFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/IntFloatMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/IntFloatMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/IntFloatMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/IntArray // androidx.collection/IntFloatMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/IntArray) // androidx.collection/IntFloatMap.keys.<set-keys>|<set-keys>(kotlin.IntArray){}[0]
+ final var metadata // androidx.collection/IntFloatMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntFloatMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntFloatMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/IntFloatMap.values|{}values[0]
+ final fun <get-values>(): kotlin/FloatArray // androidx.collection/IntFloatMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/FloatArray) // androidx.collection/IntFloatMap.values.<set-values>|<set-values>(kotlin.FloatArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntFloatMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/IntFloatMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/IntFloatMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntIntMap { // androidx.collection/IntIntMap|null[0]
+ constructor <init>() // androidx.collection/IntIntMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/IntIntMap.any|any(){}[0]
+ final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntIntMap.contains|contains(kotlin.Int){}[0]
+ final fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/IntIntMap.containsKey|containsKey(kotlin.Int){}[0]
+ final fun containsValue(kotlin/Int): kotlin/Boolean // androidx.collection/IntIntMap.containsValue|containsValue(kotlin.Int){}[0]
+ final fun count(): kotlin/Int // androidx.collection/IntIntMap.count|count(){}[0]
+ final fun findKeyIndex(kotlin/Int): kotlin/Int // androidx.collection/IntIntMap.findKeyIndex|findKeyIndex(kotlin.Int){}[0]
+ final fun get(kotlin/Int): kotlin/Int // androidx.collection/IntIntMap.get|get(kotlin.Int){}[0]
+ final fun getOrDefault(kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/IntIntMap.getOrDefault|getOrDefault(kotlin.Int;kotlin.Int){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/IntIntMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntIntMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/IntIntMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntIntMap.all|all(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntIntMap.any|any(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntIntMap.count|count(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Unit>) // androidx.collection/IntIntMap.forEach|forEach(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntIntMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntIntMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntIntMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Int, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/IntIntMap.getOrElse|getOrElse(kotlin.Int;kotlin.Function0<kotlin.Int>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/IntIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/IntIntMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/IntIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/IntIntMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/IntIntMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/IntIntMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/IntArray // androidx.collection/IntIntMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/IntArray) // androidx.collection/IntIntMap.keys.<set-keys>|<set-keys>(kotlin.IntArray){}[0]
+ final var metadata // androidx.collection/IntIntMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntIntMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntIntMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/IntIntMap.values|{}values[0]
+ final fun <get-values>(): kotlin/IntArray // androidx.collection/IntIntMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/IntArray) // androidx.collection/IntIntMap.values.<set-values>|<set-values>(kotlin.IntArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntIntMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/IntIntMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/IntIntMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntList { // androidx.collection/IntList|null[0]
+ constructor <init>(kotlin/Int) // androidx.collection/IntList.<init>|<init>(kotlin.Int){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/IntList.any|any(){}[0]
+ final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntList.contains|contains(kotlin.Int){}[0]
+ final fun containsAll(androidx.collection/IntList): kotlin/Boolean // androidx.collection/IntList.containsAll|containsAll(androidx.collection.IntList){}[0]
+ final fun count(): kotlin/Int // androidx.collection/IntList.count|count(){}[0]
+ final fun elementAt(kotlin/Int): kotlin/Int // androidx.collection/IntList.elementAt|elementAt(kotlin.Int){}[0]
+ final fun first(): kotlin/Int // androidx.collection/IntList.first|first(){}[0]
+ final fun get(kotlin/Int): kotlin/Int // androidx.collection/IntList.get|get(kotlin.Int){}[0]
+ final fun indexOf(kotlin/Int): kotlin/Int // androidx.collection/IntList.indexOf|indexOf(kotlin.Int){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/IntList.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntList.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun last(): kotlin/Int // androidx.collection/IntList.last|last(){}[0]
+ final fun lastIndexOf(kotlin/Int): kotlin/Int // androidx.collection/IntList.lastIndexOf|lastIndexOf(kotlin.Int){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/IntList.none|none(){}[0]
+ final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, kotlin/Int, #A1>): #A1 // androidx.collection/IntList.fold|fold(0:0;kotlin.Function2<0:0,kotlin.Int,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, kotlin/Int, #A1>): #A1 // androidx.collection/IntList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,kotlin.Int,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<kotlin/Int, #A1, #A1>): #A1 // androidx.collection/IntList.foldRight|foldRight(0:0;kotlin.Function2<kotlin.Int,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, kotlin/Int, #A1, #A1>): #A1 // androidx.collection/IntList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,kotlin.Int,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun any(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntList.any|any(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.count|count(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, kotlin/Int>): kotlin/Int // androidx.collection/IntList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,kotlin.Int>){}[0]
+ final inline fun first(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.first|first(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntList.forEach|forEach(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Unit>) // androidx.collection/IntList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachReversed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntList.forEachReversed|forEachReversed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Unit>) // androidx.collection/IntList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun indexOfFirst(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.indexOfFirst|indexOfFirst(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun indexOfLast(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.indexOfLast|indexOfLast(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/IntList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Int,kotlin.CharSequence>){}[0]
+ final inline fun last(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.last|last(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun reversedAny(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntList.reversedAny|reversedAny(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final val indices // androidx.collection/IntList.indices|{}indices[0]
+ final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/IntList.indices.<get-indices>|<get-indices>(){}[0]
+ final val lastIndex // androidx.collection/IntList.lastIndex|{}lastIndex[0]
+ final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/IntList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+ final val size // androidx.collection/IntList.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/IntList.size.<get-size>|<get-size>(){}[0]
+ final var _size // androidx.collection/IntList._size|{}_size[0]
+ final fun <get-_size>(): kotlin/Int // androidx.collection/IntList._size.<get-_size>|<get-_size>(){}[0]
+ final fun <set-_size>(kotlin/Int) // androidx.collection/IntList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+ final var content // androidx.collection/IntList.content|{}content[0]
+ final fun <get-content>(): kotlin/IntArray // androidx.collection/IntList.content.<get-content>|<get-content>(){}[0]
+ final fun <set-content>(kotlin/IntArray) // androidx.collection/IntList.content.<set-content>|<set-content>(kotlin.IntArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntList.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/IntList.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/IntList.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntLongMap { // androidx.collection/IntLongMap|null[0]
+ constructor <init>() // androidx.collection/IntLongMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/IntLongMap.any|any(){}[0]
+ final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntLongMap.contains|contains(kotlin.Int){}[0]
+ final fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/IntLongMap.containsKey|containsKey(kotlin.Int){}[0]
+ final fun containsValue(kotlin/Long): kotlin/Boolean // androidx.collection/IntLongMap.containsValue|containsValue(kotlin.Long){}[0]
+ final fun count(): kotlin/Int // androidx.collection/IntLongMap.count|count(){}[0]
+ final fun findKeyIndex(kotlin/Int): kotlin/Int // androidx.collection/IntLongMap.findKeyIndex|findKeyIndex(kotlin.Int){}[0]
+ final fun get(kotlin/Int): kotlin/Long // androidx.collection/IntLongMap.get|get(kotlin.Int){}[0]
+ final fun getOrDefault(kotlin/Int, kotlin/Long): kotlin/Long // androidx.collection/IntLongMap.getOrDefault|getOrDefault(kotlin.Int;kotlin.Long){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/IntLongMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntLongMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/IntLongMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntLongMap.all|all(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntLongMap.any|any(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/IntLongMap.count|count(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Unit>) // androidx.collection/IntLongMap.forEach|forEach(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntLongMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntLongMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/IntLongMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Int, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/IntLongMap.getOrElse|getOrElse(kotlin.Int;kotlin.Function0<kotlin.Long>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/IntLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/IntLongMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/IntLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/IntLongMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/IntLongMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/IntLongMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/IntArray // androidx.collection/IntLongMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/IntArray) // androidx.collection/IntLongMap.keys.<set-keys>|<set-keys>(kotlin.IntArray){}[0]
+ final var metadata // androidx.collection/IntLongMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntLongMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntLongMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/IntLongMap.values|{}values[0]
+ final fun <get-values>(): kotlin/LongArray // androidx.collection/IntLongMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/LongArray) // androidx.collection/IntLongMap.values.<set-values>|<set-values>(kotlin.LongArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntLongMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/IntLongMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/IntLongMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntSet { // androidx.collection/IntSet|null[0]
+ constructor <init>() // androidx.collection/IntSet.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/IntSet.any|any(){}[0]
+ final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntSet.contains|contains(kotlin.Int){}[0]
+ final fun count(): kotlin/Int // androidx.collection/IntSet.count|count(){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/IntSet.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntSet.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/IntSet.none|none(){}[0]
+ final inline fun all(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntSet.all|all(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntSet.any|any(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntSet.count|count(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun first(): kotlin/Int // androidx.collection/IntSet.first|first(){}[0]
+ final inline fun first(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntSet.first|first(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntSet.forEach|forEach(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachIndex(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntSet.forEachIndex|forEachIndex(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/IntSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Int,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/IntSet.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/IntSet.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/IntSet.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/IntSet.size.<get-size>|<get-size>(){}[0]
+ final var elements // androidx.collection/IntSet.elements|{}elements[0]
+ final fun <get-elements>(): kotlin/IntArray // androidx.collection/IntSet.elements.<get-elements>|<get-elements>(){}[0]
+ final fun <set-elements>(kotlin/IntArray) // androidx.collection/IntSet.elements.<set-elements>|<set-elements>(kotlin.IntArray){}[0]
+ final var metadata // androidx.collection/IntSet.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntSet.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntSet.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntSet.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/IntSet.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/IntSet.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongFloatMap { // androidx.collection/LongFloatMap|null[0]
+ constructor <init>() // androidx.collection/LongFloatMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/LongFloatMap.any|any(){}[0]
+ final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongFloatMap.contains|contains(kotlin.Long){}[0]
+ final fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongFloatMap.containsKey|containsKey(kotlin.Long){}[0]
+ final fun containsValue(kotlin/Float): kotlin/Boolean // androidx.collection/LongFloatMap.containsValue|containsValue(kotlin.Float){}[0]
+ final fun count(): kotlin/Int // androidx.collection/LongFloatMap.count|count(){}[0]
+ final fun findKeyIndex(kotlin/Long): kotlin/Int // androidx.collection/LongFloatMap.findKeyIndex|findKeyIndex(kotlin.Long){}[0]
+ final fun get(kotlin/Long): kotlin/Float // androidx.collection/LongFloatMap.get|get(kotlin.Long){}[0]
+ final fun getOrDefault(kotlin/Long, kotlin/Float): kotlin/Float // androidx.collection/LongFloatMap.getOrDefault|getOrDefault(kotlin.Long;kotlin.Float){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/LongFloatMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongFloatMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/LongFloatMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongFloatMap.all|all(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongFloatMap.any|any(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/LongFloatMap.count|count(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Unit>) // androidx.collection/LongFloatMap.forEach|forEach(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongFloatMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongFloatMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/LongFloatMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Long, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/LongFloatMap.getOrElse|getOrElse(kotlin.Long;kotlin.Function0<kotlin.Float>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/LongFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/LongFloatMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/LongFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/LongFloatMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/LongFloatMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/LongFloatMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/LongArray // androidx.collection/LongFloatMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/LongArray) // androidx.collection/LongFloatMap.keys.<set-keys>|<set-keys>(kotlin.LongArray){}[0]
+ final var metadata // androidx.collection/LongFloatMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongFloatMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongFloatMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/LongFloatMap.values|{}values[0]
+ final fun <get-values>(): kotlin/FloatArray // androidx.collection/LongFloatMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/FloatArray) // androidx.collection/LongFloatMap.values.<set-values>|<set-values>(kotlin.FloatArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongFloatMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/LongFloatMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/LongFloatMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongIntMap { // androidx.collection/LongIntMap|null[0]
+ constructor <init>() // androidx.collection/LongIntMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/LongIntMap.any|any(){}[0]
+ final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongIntMap.contains|contains(kotlin.Long){}[0]
+ final fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongIntMap.containsKey|containsKey(kotlin.Long){}[0]
+ final fun containsValue(kotlin/Int): kotlin/Boolean // androidx.collection/LongIntMap.containsValue|containsValue(kotlin.Int){}[0]
+ final fun count(): kotlin/Int // androidx.collection/LongIntMap.count|count(){}[0]
+ final fun findKeyIndex(kotlin/Long): kotlin/Int // androidx.collection/LongIntMap.findKeyIndex|findKeyIndex(kotlin.Long){}[0]
+ final fun get(kotlin/Long): kotlin/Int // androidx.collection/LongIntMap.get|get(kotlin.Long){}[0]
+ final fun getOrDefault(kotlin/Long, kotlin/Int): kotlin/Int // androidx.collection/LongIntMap.getOrDefault|getOrDefault(kotlin.Long;kotlin.Int){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/LongIntMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongIntMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/LongIntMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongIntMap.all|all(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongIntMap.any|any(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/LongIntMap.count|count(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Unit>) // androidx.collection/LongIntMap.forEach|forEach(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongIntMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongIntMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongIntMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Long, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/LongIntMap.getOrElse|getOrElse(kotlin.Long;kotlin.Function0<kotlin.Int>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/LongIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/LongIntMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/LongIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/LongIntMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/LongIntMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/LongIntMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/LongArray // androidx.collection/LongIntMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/LongArray) // androidx.collection/LongIntMap.keys.<set-keys>|<set-keys>(kotlin.LongArray){}[0]
+ final var metadata // androidx.collection/LongIntMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongIntMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongIntMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/LongIntMap.values|{}values[0]
+ final fun <get-values>(): kotlin/IntArray // androidx.collection/LongIntMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/IntArray) // androidx.collection/LongIntMap.values.<set-values>|<set-values>(kotlin.IntArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongIntMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/LongIntMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/LongIntMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongList { // androidx.collection/LongList|null[0]
+ constructor <init>(kotlin/Int) // androidx.collection/LongList.<init>|<init>(kotlin.Int){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/LongList.any|any(){}[0]
+ final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongList.contains|contains(kotlin.Long){}[0]
+ final fun containsAll(androidx.collection/LongList): kotlin/Boolean // androidx.collection/LongList.containsAll|containsAll(androidx.collection.LongList){}[0]
+ final fun count(): kotlin/Int // androidx.collection/LongList.count|count(){}[0]
+ final fun elementAt(kotlin/Int): kotlin/Long // androidx.collection/LongList.elementAt|elementAt(kotlin.Int){}[0]
+ final fun first(): kotlin/Long // androidx.collection/LongList.first|first(){}[0]
+ final fun get(kotlin/Int): kotlin/Long // androidx.collection/LongList.get|get(kotlin.Int){}[0]
+ final fun indexOf(kotlin/Long): kotlin/Int // androidx.collection/LongList.indexOf|indexOf(kotlin.Long){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/LongList.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongList.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun last(): kotlin/Long // androidx.collection/LongList.last|last(){}[0]
+ final fun lastIndexOf(kotlin/Long): kotlin/Int // androidx.collection/LongList.lastIndexOf|lastIndexOf(kotlin.Long){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/LongList.none|none(){}[0]
+ final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, kotlin/Long, #A1>): #A1 // androidx.collection/LongList.fold|fold(0:0;kotlin.Function2<0:0,kotlin.Long,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, kotlin/Long, #A1>): #A1 // androidx.collection/LongList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,kotlin.Long,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<kotlin/Long, #A1, #A1>): #A1 // androidx.collection/LongList.foldRight|foldRight(0:0;kotlin.Function2<kotlin.Long,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, kotlin/Long, #A1, #A1>): #A1 // androidx.collection/LongList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,kotlin.Long,0:0,0:0>){0§<kotlin.Any?>}[0]
+ final inline fun any(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongList.any|any(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongList.count|count(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, kotlin/Long>): kotlin/Long // androidx.collection/LongList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,kotlin.Long>){}[0]
+ final inline fun first(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Long // androidx.collection/LongList.first|first(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongList.forEach|forEach(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Unit>) // androidx.collection/LongList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachReversed(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongList.forEachReversed|forEachReversed(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Unit>) // androidx.collection/LongList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun indexOfFirst(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongList.indexOfFirst|indexOfFirst(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun indexOfLast(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongList.indexOfLast|indexOfLast(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/LongList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Long,kotlin.CharSequence>){}[0]
+ final inline fun last(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Long // androidx.collection/LongList.last|last(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun reversedAny(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongList.reversedAny|reversedAny(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final val indices // androidx.collection/LongList.indices|{}indices[0]
+ final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/LongList.indices.<get-indices>|<get-indices>(){}[0]
+ final val lastIndex // androidx.collection/LongList.lastIndex|{}lastIndex[0]
+ final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/LongList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+ final val size // androidx.collection/LongList.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/LongList.size.<get-size>|<get-size>(){}[0]
+ final var _size // androidx.collection/LongList._size|{}_size[0]
+ final fun <get-_size>(): kotlin/Int // androidx.collection/LongList._size.<get-_size>|<get-_size>(){}[0]
+ final fun <set-_size>(kotlin/Int) // androidx.collection/LongList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+ final var content // androidx.collection/LongList.content|{}content[0]
+ final fun <get-content>(): kotlin/LongArray // androidx.collection/LongList.content.<get-content>|<get-content>(){}[0]
+ final fun <set-content>(kotlin/LongArray) // androidx.collection/LongList.content.<set-content>|<set-content>(kotlin.LongArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongList.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/LongList.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/LongList.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongLongMap { // androidx.collection/LongLongMap|null[0]
+ constructor <init>() // androidx.collection/LongLongMap.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/LongLongMap.any|any(){}[0]
+ final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongLongMap.contains|contains(kotlin.Long){}[0]
+ final fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongLongMap.containsKey|containsKey(kotlin.Long){}[0]
+ final fun containsValue(kotlin/Long): kotlin/Boolean // androidx.collection/LongLongMap.containsValue|containsValue(kotlin.Long){}[0]
+ final fun count(): kotlin/Int // androidx.collection/LongLongMap.count|count(){}[0]
+ final fun findKeyIndex(kotlin/Long): kotlin/Int // androidx.collection/LongLongMap.findKeyIndex|findKeyIndex(kotlin.Long){}[0]
+ final fun get(kotlin/Long): kotlin/Long // androidx.collection/LongLongMap.get|get(kotlin.Long){}[0]
+ final fun getOrDefault(kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/LongLongMap.getOrDefault|getOrDefault(kotlin.Long;kotlin.Long){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/LongLongMap.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongLongMap.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/LongLongMap.none|none(){}[0]
+ final inline fun all(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongLongMap.all|all(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongLongMap.any|any(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongLongMap.count|count(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Unit>) // androidx.collection/LongLongMap.forEach|forEach(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongLongMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun forEachKey(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongLongMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachValue(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongLongMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun getOrElse(kotlin/Long, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/LongLongMap.getOrElse|getOrElse(kotlin.Long;kotlin.Function0<kotlin.Long>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/LongLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/LongLongMap.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/LongLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/LongLongMap.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/LongLongMap.size.<get-size>|<get-size>(){}[0]
+ final var keys // androidx.collection/LongLongMap.keys|{}keys[0]
+ final fun <get-keys>(): kotlin/LongArray // androidx.collection/LongLongMap.keys.<get-keys>|<get-keys>(){}[0]
+ final fun <set-keys>(kotlin/LongArray) // androidx.collection/LongLongMap.keys.<set-keys>|<set-keys>(kotlin.LongArray){}[0]
+ final var metadata // androidx.collection/LongLongMap.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongLongMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongLongMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ final var values // androidx.collection/LongLongMap.values|{}values[0]
+ final fun <get-values>(): kotlin/LongArray // androidx.collection/LongLongMap.values.<get-values>|<get-values>(){}[0]
+ final fun <set-values>(kotlin/LongArray) // androidx.collection/LongLongMap.values.<set-values>|<set-values>(kotlin.LongArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongLongMap.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/LongLongMap.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/LongLongMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongSet { // androidx.collection/LongSet|null[0]
+ constructor <init>() // androidx.collection/LongSet.<init>|<init>(){}[0]
+ final fun any(): kotlin/Boolean // androidx.collection/LongSet.any|any(){}[0]
+ final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongSet.contains|contains(kotlin.Long){}[0]
+ final fun count(): kotlin/Int // androidx.collection/LongSet.count|count(){}[0]
+ final fun isEmpty(): kotlin/Boolean // androidx.collection/LongSet.isEmpty|isEmpty(){}[0]
+ final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongSet.isNotEmpty|isNotEmpty(){}[0]
+ final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+ final fun none(): kotlin/Boolean // androidx.collection/LongSet.none|none(){}[0]
+ final inline fun all(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongSet.all|all(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun any(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongSet.any|any(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun count(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongSet.count|count(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun first(): kotlin/Long // androidx.collection/LongSet.first|first(){}[0]
+ final inline fun first(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Long // androidx.collection/LongSet.first|first(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+ final inline fun forEach(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongSet.forEach|forEach(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+ final inline fun forEachIndex(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongSet.forEachIndex|forEachIndex(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+ final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/LongSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Long,kotlin.CharSequence>){}[0]
+ final val capacity // androidx.collection/LongSet.capacity|{}capacity[0]
+ final fun <get-capacity>(): kotlin/Int // androidx.collection/LongSet.capacity.<get-capacity>|<get-capacity>(){}[0]
+ final val size // androidx.collection/LongSet.size|{}size[0]
+ final fun <get-size>(): kotlin/Int // androidx.collection/LongSet.size.<get-size>|<get-size>(){}[0]
+ final var elements // androidx.collection/LongSet.elements|{}elements[0]
+ final fun <get-elements>(): kotlin/LongArray // androidx.collection/LongSet.elements.<get-elements>|<get-elements>(){}[0]
+ final fun <set-elements>(kotlin/LongArray) // androidx.collection/LongSet.elements.<set-elements>|<set-elements>(kotlin.LongArray){}[0]
+ final var metadata // androidx.collection/LongSet.metadata|{}metadata[0]
+ final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongSet.metadata.<get-metadata>|<get-metadata>(){}[0]
+ final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongSet.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+ open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongSet.equals|equals(kotlin.Any?){}[0]
+ open fun hashCode(): kotlin/Int // androidx.collection/LongSet.hashCode|hashCode(){}[0]
+ open fun toString(): kotlin/String // androidx.collection/LongSet.toString|toString(){}[0]
+}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/resources/datastore.txt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/resources/datastore.txt
new file mode 100644
index 0000000..3bc92f9
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/resources/datastore.txt
@@ -0,0 +1,62 @@
+// KLib ABI Dump
+// Targets: [macosX64, macosX64.iosArm64, macosX64.iosSimulatorArm64, macosX64.iosX64, macosX64.linuxX64, macosX64.macosArm64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.datastore:datastore-core>
+abstract interface <#A: kotlin/Any?> androidx.datastore.core/DataMigration { // androidx.datastore.core/DataMigration|null[0]
+ abstract suspend fun cleanUp() // androidx.datastore.core/DataMigration.cleanUp|cleanUp(){}[0]
+ abstract suspend fun migrate(#A): #A // androidx.datastore.core/DataMigration.migrate|migrate(1:0){}[0]
+ abstract suspend fun shouldMigrate(#A): kotlin/Boolean // androidx.datastore.core/DataMigration.shouldMigrate|shouldMigrate(1:0){}[0]
+}
+abstract interface <#A: kotlin/Any?> androidx.datastore.core/DataStore { // androidx.datastore.core/DataStore|null[0]
+ abstract suspend fun updateData(kotlin.coroutines/SuspendFunction1<#A, #A>): #A // androidx.datastore.core/DataStore.updateData|updateData(kotlin.coroutines.SuspendFunction1<1:0,1:0>){}[0]
+ abstract val data // androidx.datastore.core/DataStore.data|{}data[0]
+ abstract fun <get-data>(): kotlinx.coroutines.flow/Flow<#A> // androidx.datastore.core/DataStore.data.<get-data>|<get-data>(){}[0]
+}
+abstract interface <#A: kotlin/Any?> androidx.datastore.core/ReadScope : androidx.datastore.core/Closeable { // androidx.datastore.core/ReadScope|null[0]
+ abstract suspend fun readData(): #A // androidx.datastore.core/ReadScope.readData|readData(){}[0]
+}
+abstract interface <#A: kotlin/Any?> androidx.datastore.core/Storage { // androidx.datastore.core/Storage|null[0]
+ abstract fun createConnection(): androidx.datastore.core/StorageConnection<#A> // androidx.datastore.core/Storage.createConnection|createConnection(){}[0]
+}
+abstract interface <#A: kotlin/Any?> androidx.datastore.core/StorageConnection : androidx.datastore.core/Closeable { // androidx.datastore.core/StorageConnection|null[0]
+ abstract suspend fun <#A1: kotlin/Any?> readScope(kotlin.coroutines/SuspendFunction2<androidx.datastore.core/ReadScope<#A>, kotlin/Boolean, #A1>): #A1 // androidx.datastore.core/StorageConnection.readScope|readScope(kotlin.coroutines.SuspendFunction2<androidx.datastore.core.ReadScope<1:0>,kotlin.Boolean,0:0>){0§<kotlin.Any?>}[0]
+ abstract suspend fun writeScope(kotlin.coroutines/SuspendFunction1<androidx.datastore.core/WriteScope<#A>, kotlin/Unit>) // androidx.datastore.core/StorageConnection.writeScope|writeScope(kotlin.coroutines.SuspendFunction1<androidx.datastore.core.WriteScope<1:0>,kotlin.Unit>){}[0]
+ abstract val coordinator // androidx.datastore.core/StorageConnection.coordinator|{}coordinator[0]
+ abstract fun <get-coordinator>(): androidx.datastore.core/InterProcessCoordinator // androidx.datastore.core/StorageConnection.coordinator.<get-coordinator>|<get-coordinator>(){}[0]
+}
+abstract interface <#A: kotlin/Any?> androidx.datastore.core/WriteScope : androidx.datastore.core/ReadScope<#A> { // androidx.datastore.core/WriteScope|null[0]
+ abstract suspend fun writeData(#A) // androidx.datastore.core/WriteScope.writeData|writeData(1:0){}[0]
+}
+abstract interface androidx.datastore.core/Closeable { // androidx.datastore.core/Closeable|null[0]
+ abstract fun close() // androidx.datastore.core/Closeable.close|close(){}[0]
+}
+abstract interface androidx.datastore.core/InterProcessCoordinator { // androidx.datastore.core/InterProcessCoordinator|null[0]
+ abstract suspend fun <#A1: kotlin/Any?> lock(kotlin.coroutines/SuspendFunction0<#A1>): #A1 // androidx.datastore.core/InterProcessCoordinator.lock|lock(kotlin.coroutines.SuspendFunction0<0:0>){0§<kotlin.Any?>}[0]
+ abstract suspend fun <#A1: kotlin/Any?> tryLock(kotlin.coroutines/SuspendFunction1<kotlin/Boolean, #A1>): #A1 // androidx.datastore.core/InterProcessCoordinator.tryLock|tryLock(kotlin.coroutines.SuspendFunction1<kotlin.Boolean,0:0>){0§<kotlin.Any?>}[0]
+ abstract suspend fun getVersion(): kotlin/Int // androidx.datastore.core/InterProcessCoordinator.getVersion|getVersion(){}[0]
+ abstract suspend fun incrementAndGetVersion(): kotlin/Int // androidx.datastore.core/InterProcessCoordinator.incrementAndGetVersion|incrementAndGetVersion(){}[0]
+ abstract val updateNotifications // androidx.datastore.core/InterProcessCoordinator.updateNotifications|{}updateNotifications[0]
+ abstract fun <get-updateNotifications>(): kotlinx.coroutines.flow/Flow<kotlin/Unit> // androidx.datastore.core/InterProcessCoordinator.updateNotifications.<get-updateNotifications>|<get-updateNotifications>(){}[0]
+}
+final class <#A: kotlin/Any?> androidx.datastore.core.handlers/ReplaceFileCorruptionHandler : androidx.datastore.core/CorruptionHandler<#A> { // androidx.datastore.core.handlers/ReplaceFileCorruptionHandler|null[0]
+ constructor <init>(kotlin/Function1<androidx.datastore.core/CorruptionException, #A>) // androidx.datastore.core.handlers/ReplaceFileCorruptionHandler.<init>|<init>(kotlin.Function1<androidx.datastore.core.CorruptionException,1:0>){}[0]
+ final suspend fun handleCorruption(androidx.datastore.core/CorruptionException): #A // androidx.datastore.core.handlers/ReplaceFileCorruptionHandler.handleCorruption|handleCorruption(androidx.datastore.core.CorruptionException){}[0]
+}
+final class androidx.datastore.core/CorruptionException : androidx.datastore.core/IOException { // androidx.datastore.core/CorruptionException|null[0]
+ constructor <init>(kotlin/String, kotlin/Throwable? =...) // androidx.datastore.core/CorruptionException.<init>|<init>(kotlin.String;kotlin.Throwable?){}[0]
+}
+final fun androidx.datastore.core/createSingleProcessCoordinator(kotlin/String): androidx.datastore.core/InterProcessCoordinator // androidx.datastore.core/createSingleProcessCoordinator|createSingleProcessCoordinator(kotlin.String){}[0]
+final inline fun <#A: androidx.datastore.core/Closeable, #B: kotlin/Any?> (#A).androidx.datastore.core/use(kotlin/Function1<#A, #B>): #B // androidx.datastore.core/use|use@0:0(kotlin.Function1<0:0,0:1>){0§<androidx.datastore.core.Closeable>;1§<kotlin.Any?>}[0]
+final object androidx.datastore.core/DataStoreFactory { // androidx.datastore.core/DataStoreFactory|null[0]
+ final fun <#A1: kotlin/Any?> create(androidx.datastore.core/Storage<#A1>, androidx.datastore.core.handlers/ReplaceFileCorruptionHandler<#A1>? =..., kotlin.collections/List<androidx.datastore.core/DataMigration<#A1>> =..., kotlinx.coroutines/CoroutineScope =...): androidx.datastore.core/DataStore<#A1> // androidx.datastore.core/DataStoreFactory.create|create(androidx.datastore.core.Storage<0:0>;androidx.datastore.core.handlers.ReplaceFileCorruptionHandler<0:0>?;kotlin.collections.List<androidx.datastore.core.DataMigration<0:0>>;kotlinx.coroutines.CoroutineScope){0§<kotlin.Any?>}[0]
+}
+final suspend fun <#A: kotlin/Any?> (androidx.datastore.core/StorageConnection<#A>).androidx.datastore.core/readData(): #A // androidx.datastore.core/readData|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final suspend fun <#A: kotlin/Any?> (androidx.datastore.core/StorageConnection<#A>).androidx.datastore.core/writeData(#A) // androidx.datastore.core/writeData|[email protected]<0:0>(0:0){0§<kotlin.Any?>}[0]
+open class androidx.datastore.core/IOException : kotlin/Exception { // androidx.datastore.core/IOException|null[0]
+ constructor <init>(kotlin/String?) // androidx.datastore.core/IOException.<init>|<init>(kotlin.String?){}[0]
+ constructor <init>(kotlin/String?, kotlin/Throwable?) // androidx.datastore.core/IOException.<init>|<init>(kotlin.String?;kotlin.Throwable?){}[0]
+}
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/resources/unique_targets.txt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/resources/unique_targets.txt
new file mode 100644
index 0000000..83f3a1f
--- /dev/null
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/resources/unique_targets.txt
@@ -0,0 +1,13 @@
+// KLib ABI Dump
+// Targets: [iosX64, linuxX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx.multiplatform-library-template:library>
+final fun my.lib/commonFun() // my.lib/commonFun|commonFun(){}[0]
+// Targets: [iosX64]
+final fun my.lib/myIosFun() // my.lib/myIosFun|myIosFun(){}[0]
+// Targets: [linuxX64]
+final fun my.lib/myLinuxFun() // my.lib/myLinuxFun|myLinuxFun(){}[0]
diff --git a/biometric/biometric-ktx/build.gradle b/biometric/biometric-ktx/build.gradle
index 7ce4886..0a615c2 100644
--- a/biometric/biometric-ktx/build.gradle
+++ b/biometric/biometric-ktx/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.LibraryType
import androidx.build.Publish
plugins {
@@ -37,7 +39,7 @@
androidx {
name = "Biometric Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Kotlin extensions for the Biometric Library."
metalavaK2UastEnabled = true
diff --git a/biometric/biometric/lint-baseline.xml b/biometric/biometric/lint-baseline.xml
index 2d8e9d0..41ad640 100644
--- a/biometric/biometric/lint-baseline.xml
+++ b/biometric/biometric/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha10" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha10)" variant="all" version="8.3.0-alpha10">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="WrongConstant"
@@ -28,4 +28,67 @@
file="src/main/java/androidx/biometric/BiometricManager.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/biometric/BiometricFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/biometric/BiometricFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/biometric/BiometricFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/biometric/BiometricFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/biometric/BiometricFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/biometric/BiometricFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/biometric/FingerprintDialogFragment.java"/>
+ </issue>
+
</issues>
diff --git a/bluetooth/integration-tests/testapp/build.gradle b/bluetooth/integration-tests/testapp/build.gradle
index 62ef054..588c52e 100644
--- a/bluetooth/integration-tests/testapp/build.gradle
+++ b/bluetooth/integration-tests/testapp/build.gradle
@@ -27,7 +27,7 @@
id("AndroidXPlugin")
id("com.android.application")
id("kotlin-android")
- id("kotlin-kapt")
+ id("com.google.devtools.ksp")
id("com.google.dagger.hilt.android")
}
@@ -61,7 +61,5 @@
implementation(libs.material)
implementation(libs.hiltAndroid)
- kapt(libs.hiltCompiler)
-
- kaptAndroidTest(libs.hiltCompiler)
+ ksp(libs.hiltCompiler)
}
diff --git a/browser/browser/lint-baseline.xml b/browser/browser/lint-baseline.xml
new file mode 100644
index 0000000..f5c4e77
--- /dev/null
+++ b/browser/browser/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/browser/customtabs/CustomTabsIntent.java"/>
+ </issue>
+
+</issues>
diff --git a/buildSrc-tests/build.gradle b/buildSrc-tests/build.gradle
index 5b3af52..4b77b51 100644
--- a/buildSrc-tests/build.gradle
+++ b/buildSrc-tests/build.gradle
@@ -57,7 +57,7 @@
implementation(project(":benchmark:benchmark-gradle-plugin"))
implementation(project(":inspection:inspection-gradle-plugin"))
implementation(project(":stableaidl:stableaidl-gradle-plugin"))
- implementation(findGradleKotlinDsl())
+ implementation(project.ext.findGradleKotlinDsl())
testImplementation(libs.junit)
testImplementation(libs.truth)
testImplementation(project(":internal-testutils-gradle-plugin"))
diff --git a/buildSrc-tests/lint-baseline.xml b/buildSrc-tests/lint-baseline.xml
index 46f896e..02be011e 100644
--- a/buildSrc-tests/lint-baseline.xml
+++ b/buildSrc-tests/lint-baseline.xml
@@ -300,155 +300,11 @@
<issue
id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" rootProject.findProperty(ENABLE_ARG) != "false""
- errorLine2=" ~~~~~~~~~~~~">
+ message="Use providers.gradleProperty instead of property"
+ errorLine1=" (this.rootProject.property("ext") as ExtraPropertiesExtension).set("
+ errorLine2=" ~~~~~~~~">
<location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/dependencyTracker/AffectedModuleDetector.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" rootProject.findProperty(ENABLE_ARG) != "false""
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/dependencyTracker/AffectedModuleDetector.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" val baseCommitOverride: String? = rootProject.findProperty(BASE_COMMIT_ARG) as String?"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/dependencyTracker/AffectedModuleDetector.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" val baseCommitOverride: String? = rootProject.findProperty(BASE_COMMIT_ARG) as String?"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/dependencyTracker/AffectedModuleDetector.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" override val compileSdk: Int by lazy { project.findProperty(COMPILE_SDK).toString().toInt() }"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*3}/androidx/build/AndroidXConfig.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" project.findProperty(COMPILE_SDK_EXTENSION) as Int?"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*3}/androidx/build/AndroidXConfig.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" project.findProperty(TARGET_SDK_VERSION).toString().toInt()"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*3}/androidx/build/AndroidXConfig.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" project.findProperty(ALTERNATIVE_PROJECT_URL) as? String"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/AndroidXGradleProperties.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" return (project.findProperty(ENABLE_DOCUMENTATION) as? String)?.toBoolean() ?: true"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/AndroidXGradleProperties.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1="fun Project.findBooleanProperty(propName: String) = (findProperty(propName) as? String)?.toBoolean()"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/AndroidXGradleProperties.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" return checkNotNull(findProperty(name)) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/AndroidXPlaygroundRootImplPlugin.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" parseTargetPlatformsFlag(project.findProperty(ENABLED_KMP_TARGET_PLATFORMS) as? String)"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*3}/androidx/build/KmpPlatforms.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" findProperty(DISABLE_COMPILER_DAEMON_FLAG)?.toString()?.toBoolean() == true"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/KonanPrebuiltsSetup.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" val value = project.findProperty(STUDIO_TYPE)?.toString()"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*3}/androidx/build/ProjectLayoutType.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" val value = project.findProperty(STUDIO_TYPE)?.toString()"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*3}/androidx/build/ProjectLayoutType.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" val group = findProperty("group") as String"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/VersionFileWriterTask.kt"/>
- </issue>
-
- <issue
- id="GradleProjectIsolation"
- message="Use providers.gradleProperty instead of findProperty"
- errorLine1=" val artifactId = findProperty("name") as String"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/VersionFileWriterTask.kt"/>
+ file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/AndroidXComposeImplPlugin.kt"/>
</issue>
<issue
diff --git a/buildSrc-tests/src/test/java/androidx/build/clang/CombineObjectFilesTaskTest.kt b/buildSrc-tests/src/test/java/androidx/build/clang/CombineObjectFilesTaskTest.kt
index cb714263..7ea9886 100644
--- a/buildSrc-tests/src/test/java/androidx/build/clang/CombineObjectFilesTaskTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/clang/CombineObjectFilesTaskTest.kt
@@ -64,7 +64,7 @@
it.relativeTo(taskOutputDir).path to it.readText()
}.toList()
assertThat(outputContents).containsExactly(
- "linux_x64/libcode.so" to KonanTarget.LINUX_X64.name,
+ "natives/linux_x64/libcode.so" to KonanTarget.LINUX_X64.name,
"x86/libcode.so" to KonanTarget.ANDROID_X86.name,
"arm64-v8a/libcode.so" to KonanTarget.ANDROID_ARM64.name
)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
index 51df347..976c1ad 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
@@ -26,7 +26,6 @@
import org.gradle.api.Project
import org.gradle.api.artifacts.type.ArtifactTypeDefinition
import org.gradle.api.attributes.Attribute
-import org.gradle.api.plugins.ExtraPropertiesExtension
import org.gradle.api.tasks.bundling.Zip
import org.gradle.kotlin.dsl.create
import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig
@@ -122,7 +121,7 @@
*/
private fun Project.configureForMultiplatform() {
// This is to allow K/N not matching the kotlinVersion
- (this.rootProject.property("ext") as ExtraPropertiesExtension).set(
+ this.rootProject.extensions.extraProperties.set(
"kotlin.native.version",
KOTLIN_NATIVE_VERSION
)
@@ -248,9 +247,8 @@
compile.inputs.property("composeReportsEnabled", enableReports)
compile.pluginClasspath.from(kotlinPluginProvider.get())
- compile.addPluginOption(ComposeCompileOptions.StrongSkippingOption, "true")
- compile.addPluginOption(ComposeCompileOptions.NonSkippingGroupOption, "true")
-
+ compile.enableFeatureFlag(ComposeFeatureFlag.StrongSkipping)
+ compile.enableFeatureFlag(ComposeFeatureFlag.OptimizeNonSkippingGroups)
if (shouldPublish) {
compile.addPluginOption(ComposeCompileOptions.SourceOption, "true")
}
@@ -294,6 +292,18 @@
}
)
+private fun KotlinCompile.enableFeatureFlag(
+ featureFlag: ComposeFeatureFlag
+) {
+ addPluginOption(ComposeCompileOptions.FeatureFlagOption, featureFlag.featureName)
+}
+
+private fun KotlinCompile.disableFeatureFlag(
+ featureFlag: ComposeFeatureFlag
+) {
+ addPluginOption(ComposeCompileOptions.FeatureFlagOption, "-${featureFlag.featureName}")
+}
+
public fun Project.zipComposeCompilerMetrics() {
if (project.enableComposeCompilerMetrics()) {
val zipComposeMetrics = project.tasks.register(zipComposeMetricsTaskName, Zip::class.java) {
@@ -340,6 +350,10 @@
SourceOption(ComposePluginId, "sourceInformation"),
MetricsOption(ComposePluginId, "metricsDestination"),
ReportsOption(ComposePluginId, "reportsDestination"),
- StrongSkippingOption(ComposePluginId, "strongSkipping"),
- NonSkippingGroupOption(ComposePluginId, "nonSkippingGroupOptimization")
+ FeatureFlagOption(ComposePluginId, "featureFlag"),
+}
+
+private enum class ComposeFeatureFlag(val featureName: String) {
+ StrongSkipping("StrongSkipping"),
+ OptimizeNonSkippingGroups("OptimizeNonSkippingGroups"),
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index 69dde1f..aeab798 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -221,7 +221,7 @@
* Returns null if there is no alternative project url.
*/
fun Project.getAlternativeProjectUrl(): String? =
- project.findProperty(ALTERNATIVE_PROJECT_URL) as? String
+ project.providers.gradleProperty(ALTERNATIVE_PROJECT_URL).getOrNull()
/**
* Check that version extra meets the specified rules (version is in format major.minor.patch-extra)
@@ -277,18 +277,9 @@
fun Project.isWriteVersionedApiFilesEnabled(): Boolean =
findBooleanProperty(WRITE_VERSIONED_API_FILES) ?: true
-/** Returns whether the project should generate documentation. */
-fun Project.isDocumentationEnabled(): Boolean {
- if (System.getenv().containsKey("ANDROIDX_PROJECTS")) {
- val projects = System.getenv()["ANDROIDX_PROJECTS"] as String
- if (projects != "ALL") return false
- }
- return (project.findProperty(ENABLE_DOCUMENTATION) as? String)?.toBoolean() ?: true
-}
-
/** Returns whether the build is for checking forward compatibility across projects */
fun Project.usingMaxDepVersions(): Boolean {
- return project.hasProperty(USE_MAX_DEP_VERSIONS)
+ return project.providers.gradleProperty(USE_MAX_DEP_VERSIONS).isPresent()
}
/**
@@ -320,7 +311,7 @@
fun Project.isCustomCompileSdkAllowed(): Boolean =
findBooleanProperty(ALLOW_CUSTOM_COMPILE_SDK) ?: true
-fun Project.findBooleanProperty(propName: String) = (findProperty(propName) as? String)?.toBoolean()
+fun Project.findBooleanProperty(propName: String) = booleanPropertyProvider(propName).get()
fun Project.booleanPropertyProvider(propName: String): Provider<Boolean> {
return project.providers.gradleProperty(propName).map { s -> s.toBoolean() }.orElse(false)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 0674f5c..8d77fac 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -507,8 +507,7 @@
if (!project.name.contains("camera-camera2-pipe")) {
kotlinCompilerArgs += "-Xjvm-default=all"
}
- if (androidXExtension.type == LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY ||
- androidXExtension.type == LibraryType.PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY) {
+ if (androidXExtension.type.targetsKotlinConsumersOnly) {
// The Kotlin Compiler adds intrinsic assertions which are only relevant
// when the code is consumed by Java users. Therefore we can turn this off
// when code is being consumed by Kotlin users.
@@ -668,9 +667,6 @@
}
}
- val reportLibraryMetrics = project.configureReportLibraryMetricsTask()
- project.addToBuildOnServer(reportLibraryMetrics)
-
project.addToProjectMap(androidXExtension)
project.afterEvaluate {
project.addToBuildOnServer("assembleAndroidMain")
@@ -858,9 +854,6 @@
project.configureVersionFileWriter(libraryAndroidComponentsExtension, androidXExtension)
project.configureJavaCompilationWarnings(androidXExtension)
- val reportLibraryMetrics = project.configureReportLibraryMetricsTask()
- project.addToBuildOnServer(reportLibraryMetrics)
-
val prebuiltLibraries = listOf("libtracing_perfetto.so", "libc++_shared.so")
val copyPublicResourcesDirTask =
project.tasks.register(
@@ -884,14 +877,6 @@
task.dependsOn("compileReleaseJavaWithJavac")
}
}
-
- reportLibraryMetrics.configure {
- it.jarFiles.from(
- project.tasks.named("bundleReleaseAar").map {
- zip -> zip.inputs.files
- }
- )
- }
}
configurePublicResourcesStub(variant, copyPublicResourcesDirTask)
val verifyELFRegionAlignmentTaskProvider = project.tasks.register(
@@ -1012,7 +997,8 @@
val mavenGroup = androidXExtension.mavenGroup
val isProbablyPublished =
androidXExtension.type == LibraryType.PUBLISHED_LIBRARY ||
- androidXExtension.type == LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY ||
+ androidXExtension.type ==
+ LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS ||
androidXExtension.type == LibraryType.UNSET
if (mavenGroup != null && isProbablyPublished && androidXExtension.shouldPublish()) {
validateProjectMavenGroup(mavenGroup.group)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXPlaygroundRootImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXPlaygroundRootImplPlugin.kt
index 4503c7b..4bb9027 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXPlaygroundRootImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXPlaygroundRootImplPlugin.kt
@@ -16,6 +16,7 @@
package androidx.build
+import androidx.build.gradle.extraPropertyOrNull
import androidx.build.gradle.isRoot
import groovy.xml.DOMBuilder
import java.net.URI
@@ -195,7 +196,7 @@
}
private fun Project.requireProperty(name: String): String {
- return checkNotNull(findProperty(name)) {
+ return checkNotNull(extraPropertyOrNull(name)) {
"missing $name property. It must be defined in the gradle.properties file"
}
.toString()
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/KonanPrebuiltsSetup.kt b/buildSrc/private/src/main/kotlin/androidx/build/KonanPrebuiltsSetup.kt
index 5759c76d..3d041e1 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/KonanPrebuiltsSetup.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/KonanPrebuiltsSetup.kt
@@ -16,6 +16,7 @@
package androidx.build
+import androidx.build.gradle.extraPropertyOrNull
import java.io.File
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
@@ -83,7 +84,7 @@
private fun Project.overrideKotlinNativeDependenciesUrlToLocalDirectory() {
val compilerDaemonDisabled =
- findProperty(DISABLE_COMPILER_DAEMON_FLAG)?.toString()?.toBoolean() == true
+ extraPropertyOrNull(DISABLE_COMPILER_DAEMON_FLAG)?.toString()?.toBoolean() == true
val konanPrebuiltsFolder = getKonanPrebuiltsFolder()
val rootBaseDir = if (compilerDaemonDisabled) projectDir else rootProject.projectDir
// use relative path so it doesn't affect gradle remote cache.
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/ReportLibraryMetricsTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/ReportLibraryMetricsTask.kt
deleted file mode 100644
index 880fa5d..0000000
--- a/buildSrc/private/src/main/kotlin/androidx/build/ReportLibraryMetricsTask.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.build
-
-import com.google.gson.Gson
-import com.jakewharton.dex.DexParser.Companion.toDexParser
-import java.io.File
-import org.gradle.api.DefaultTask
-import org.gradle.api.Project
-import org.gradle.api.file.ConfigurableFileCollection
-import org.gradle.api.provider.Property
-import org.gradle.api.tasks.CacheableTask
-import org.gradle.api.tasks.Classpath
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.TaskProvider
-
-private const val BYTECODE_SIZE = "bytecode_size"
-private const val METHOD_COUNT = "method_count"
-private const val JSON_FILE_EXTENSION = ".json"
-private const val JAR_FILE_EXTENSION = ".jar"
-private const val LINT_JAR = "lint$JAR_FILE_EXTENSION"
-
-@CacheableTask
-abstract class ReportLibraryMetricsTask : DefaultTask() {
-
- init {
- group = "LibraryMetrics"
- description = "Task for reporting build time library metrics. Currently gathers .aar sizes."
- }
-
- /** The variants we are interested in gathering metrics for. */
- @get:[InputFiles Classpath]
- abstract val jarFiles: ConfigurableFileCollection
-
- @get:OutputFile abstract val outputFile: Property<File>
-
- @TaskAction
- fun reportLibraryMetrics() {
- val file = outputFile.get()
- file.parentFile.mkdirs()
- val jarFiles = getJarFiles()
- val map = mutableMapOf<String, Any>()
- val bytecodeSize = getBytecodeSize(jarFiles)
- if (bytecodeSize > 0L) {
- map[BYTECODE_SIZE] = bytecodeSize
- }
-
- val methodCount = getMethodCount(jarFiles)
- if (methodCount > 0) {
- map[METHOD_COUNT] = methodCount
- }
-
- file.writeText(Gson().toJson(map))
- }
-
- private fun getJarFiles(): List<File> {
- return jarFiles.files.filter { file ->
- file.name.endsWith(JAR_FILE_EXTENSION) &&
- // AARs bundle a `lint.jar` that contains lint checks published by the library -
- // this isn't runtime code and is not part of the actual library, so ignore it.
- file.name != LINT_JAR
- }
- }
-
- private fun getBytecodeSize(jarFiles: List<File>): Long {
- return jarFiles.sumOf { it.length() }
- }
-
- private fun getMethodCount(jarFiles: List<File>): Int {
- return when {
- jarFiles.isEmpty() -> 0
- jarFiles.all { it.isFile } -> jarFiles.toDexParser().listMethods().size
- else ->
- throw IllegalStateException("One or more of the items in $jarFiles is not a file.")
- }
- }
-}
-
-fun Project.configureReportLibraryMetricsTask(): TaskProvider<ReportLibraryMetricsTask> {
- val task = tasks.register("reportLibraryMetrics", ReportLibraryMetricsTask::class.java)
- task.configure {
- val outputDir = project.rootProject.getLibraryMetricsDirectory()
- it.outputFile.set(
- task.map { File(outputDir, "${project.group}_${project.name}$JSON_FILE_EXTENSION") }
- )
- }
- return task
-}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/VersionFileWriterTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/VersionFileWriterTask.kt
index 115050d..874ebf9 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/VersionFileWriterTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/VersionFileWriterTask.kt
@@ -105,8 +105,8 @@
androidXExtension: AndroidXExtension
) {
writeVersionFile.configure {
- val group = findProperty("group") as String
- val artifactId = findProperty("name") as String
+ val group = project.getGroup() as String
+ val artifactId = project.getName() as String
val version =
if (androidXExtension.shouldPublish()) {
version().toString()
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/clang/CombineObjectFilesTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/clang/CombineObjectFilesTask.kt
index 02841a1..b90f919 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/clang/CombineObjectFilesTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/clang/CombineObjectFilesTask.kt
@@ -92,7 +92,6 @@
Family.LINUX to "linux",
Family.MINGW to "windows",
Family.OSX to "osx",
- Family.IOS to "ios"
)
private val architectureSuffixes = mapOf(
@@ -100,7 +99,6 @@
Architecture.ARM64 to "arm64",
Architecture.X64 to "x64",
Architecture.X86 to "x86"
-
)
private fun targetFileFor(
@@ -131,7 +129,7 @@
val architectureSuffix = architectureSuffixes[konanTarget.architecture] ?: error(
"Unsupported architecture ${konanTarget.architecture} for $konanTarget"
)
- return "${familyPrefix}_$architectureSuffix"
+ return "natives/${familyPrefix}_$architectureSuffix"
}
}
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/clang/NativeTargetCompilation.kt b/buildSrc/private/src/main/kotlin/androidx/build/clang/NativeTargetCompilation.kt
index 6d4c312..41d3c85 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/clang/NativeTargetCompilation.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/clang/NativeTargetCompilation.kt
@@ -109,8 +109,8 @@
val relativeHeaderPaths = when (konanTarget.family) {
Family.MINGW -> {
listOf(
- "/windows-x86/include",
- "/windows-x86/include/win32"
+ "windows-x86/include",
+ "windows-x86/include/win32"
)
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
index ac7e956..becd6c4 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
@@ -34,6 +34,7 @@
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
@@ -115,9 +116,20 @@
@get:Input abstract val nullabilityAnnotations: ListProperty<String>
- @get:[InputFiles PathSensitive(PathSensitivity.NONE)]
+ // Version metadata for apiSince, only marked as @InputFiles if includeVersionMetadata is true
+ @get:Internal
abstract val versionMetadataFiles: ConfigurableFileCollection
+ @InputFiles
+ @PathSensitive(PathSensitivity.NONE)
+ fun getOptionalVersionMetadataFiles(): ConfigurableFileCollection {
+ return if (includeVersionMetadata) {
+ versionMetadataFiles
+ } else {
+ objects.fileCollection()
+ }
+ }
+
// Maps to the system variable LIBRARY_METADATA_FILE containing artifactID and other metadata
@get:[InputFile PathSensitive(PathSensitivity.NONE)]
abstract val libraryMetadataFile: RegularFileProperty
@@ -131,7 +143,7 @@
/**
* Option for whether to include apiSince metadata in the docs. Defaults to including metadata.
- * Run with `--no-version-metadata -x generateApi` to avoid running `generateApi` before `docs`.
+ * Run with `--no-version-metadata` to avoid running `generateApi` before `docs`.
*/
@get:Input
@set:Option(
@@ -283,8 +295,8 @@
* an exact match of the version metadata attributes to be selected as version metadata.
*/
private fun getVersionMetadataFiles(): List<File> {
- if (!includeVersionMetadata) return emptyList()
- val (json, nonJson) = versionMetadataFiles.files.partition { it.extension == "json" }
+ val (json, nonJson) = getOptionalVersionMetadataFiles().files
+ .partition { it.extension == "json" }
if (nonJson.isNotEmpty()) {
logger.error(
"The following were resolved as version metadata files but are not JSON files. " +
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index 6307291..4dfd2de 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -110,9 +110,8 @@
val instance = AffectedModuleDetectorWrapper()
rootProject.extensions.add(ROOT_PROP_NAME, instance)
- val enabled =
- rootProject.hasProperty(ENABLE_ARG) &&
- rootProject.findProperty(ENABLE_ARG) != "false"
+ val enabledProvider = rootProject.providers.gradleProperty(ENABLE_ARG)
+ val enabled = enabledProvider.isPresent() && enabledProvider.get() != "false"
val distDir = rootProject.getDistributionDirectory()
val outputFile = distDir.resolve(LOG_FILE_NAME)
@@ -134,10 +133,9 @@
instance.wrapped = provider
return
}
- val baseCommitOverride: String? = rootProject.findProperty(BASE_COMMIT_ARG) as String?
- if (baseCommitOverride != null) {
- logger.info("using base commit override $baseCommitOverride")
- }
+ val baseCommitOverride: Provider<String> =
+ rootProject.providers.gradleProperty(BASE_COMMIT_ARG)
+
gradle.taskGraph.whenReady {
logger.lifecycle("projects evaluated")
val projectGraph = ProjectGraph(rootProject)
@@ -257,7 +255,7 @@
var cobuiltTestPaths: Set<Set<String>>?
var alwaysBuildIfExists: Set<String>?
var ignoredPaths: Set<String>?
- var baseCommitOverride: String?
+ var baseCommitOverride: Provider<String>?
var gitChangedFilesProvider: Provider<List<String>>
}
@@ -266,10 +264,6 @@
if (parameters.acceptAll) {
AcceptAll(null)
} else {
- if (parameters.baseCommitOverride != null) {
- logger.info("using base commit override ${parameters.baseCommitOverride}")
- }
-
AffectedModuleDetectorImpl(
projectGraph = parameters.projectGraph,
dependencyTracker = parameters.dependencyTracker,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
index c73c126..b96c9d6 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
@@ -27,6 +27,7 @@
import androidx.build.getDistributionDirectory
import androidx.build.getKeystore
import androidx.build.getLibraryByName
+import androidx.build.getSupportRootFolder
import androidx.build.metalava.versionMetadataUsage
import androidx.build.multiplatformUsage
import androidx.build.versionCatalog
@@ -521,7 +522,7 @@
var taskStartTime: LocalDateTime? = null
task.argsJsonFile.set(
File(
- project.rootProject.getDistributionDirectory(),
+ project.getDistributionDirectory(),
"dackkaArgs-${project.name}.json"
)
)
@@ -542,7 +543,7 @@
dackkaClasspath.from(project.files(dackkaConfiguration))
destinationDir.set(generatedDocsDir)
frameworkSamplesDir.set(
- project.rootProject.layout.projectDirectory.dir("samples")
+ File(project.getSupportRootFolder(), "samples")
)
samplesDeprecatedDir.set(unzippedDeprecatedSamplesSources)
samplesJvmDir.set(unzippedJvmSamplesSources)
@@ -550,7 +551,7 @@
jvmSourcesDir.set(unzippedJvmSourcesDirectory)
multiplatformSourcesDir.set(unzippedMultiplatformSourcesDirectory)
projectListsDirectory.set(
- project.rootProject.layout.projectDirectory.dir("docs-public/package-lists")
+ File(project.getSupportRootFolder(), "docs-public/package-lists")
)
dependenciesClasspath.from(
dependencyClasspath +
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitClient.kt b/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitClient.kt
index fca8225..b2c1cf6 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitClient.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitClient.kt
@@ -38,12 +38,12 @@
* @param baseCommitOverride optional value to use to override last merge commit
*/
fun Project.getChangedFilesProvider(
- baseCommitOverride: String?,
+ baseCommitOverride: Provider<String>,
): Provider<List<String>> {
val changeInfoPath = System.getenv("CHANGE_INFO")
val manifestPath = System.getenv("MANIFEST")
return if (changeInfoPath != null && manifestPath != null) {
- if (baseCommitOverride != null) throw GradleException(
+ if (baseCommitOverride.isPresent()) throw GradleException(
"Overriding base commit is not supported when using CHANGE_INFO and MANIFEST"
)
getChangedFilesFromChangeInfoProvider(manifestPath, changeInfoPath)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
index abfbe4d9..d311b35 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
@@ -17,7 +17,6 @@
package androidx.build.metalava
import androidx.build.AndroidXExtension
-import androidx.build.LibraryType
import androidx.build.addFilterableTasks
import androidx.build.addToBuildOnServer
import androidx.build.addToCheckTask
@@ -74,9 +73,7 @@
// implemented by excluding APIs with this annotation from the restricted API file.
val generateRestrictToLibraryGroupAPIs = !extension.mavenGroup!!.requireSameVersion
val kotlinSourceLevel: Provider<KotlinVersion> = extension.kotlinApiVersion
- val targetsJavaConsumers = (extension.type != LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY &&
- extension.type != LibraryType.PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY
- )
+ val targetsJavaConsumers = !extension.type.targetsKotlinConsumersOnly
val generateApi =
project.tasks.register("generateApi", GenerateApiTask::class.java) { task ->
task.group = "API"
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
index e80fc19..1023659 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
@@ -31,7 +31,6 @@
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.internal.tasks.userinput.UserInputHandler
-import org.gradle.api.plugins.ExtraPropertiesExtension
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
import org.gradle.internal.service.ServiceRegistry
@@ -339,7 +338,7 @@
abstract class PlaygroundStudioTask : RootStudioTask() {
@get:Internal
val supportRootFolder =
- (project.rootProject.property("ext") as ExtraPropertiesExtension).let {
+ (project.rootProject.extensions.extraProperties).let {
it.get("supportRootFolder") as File
}
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/AndroidXConfig.kt b/buildSrc/public/src/main/kotlin/androidx/build/AndroidXConfig.kt
index 4556a22..f592083 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/AndroidXConfig.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/AndroidXConfig.kt
@@ -18,6 +18,7 @@
package androidx.build
+import androidx.build.gradle.extraPropertyOrNull
import java.io.File
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
@@ -26,13 +27,17 @@
abstract class AndroidConfigImpl(private val project: Project) : AndroidConfig {
override val buildToolsVersion: String = "35.0.0-rc1"
- override val compileSdk: Int by lazy { project.findProperty(COMPILE_SDK).toString().toInt() }
+ override val compileSdk: Int by lazy {
+ val sdkString = project.extraPropertyOrNull(COMPILE_SDK)?.toString()
+ check(sdkString != null) { "$COMPILE_SDK is unset" }
+ sdkString.toInt()
+ }
- override val minSdk: Int = 19
+ override val minSdk: Int = 21
override val ndkVersion: String = "25.2.9519653"
override val targetSdk: Int by lazy {
- project.findProperty(TARGET_SDK_VERSION).toString().toInt()
+ project.providers.gradleProperty(TARGET_SDK_VERSION).get().toInt()
}
companion object {
@@ -95,7 +100,7 @@
}
fun Project.getPrebuiltsRoot(): File {
- return File(project.rootProject.property("prebuiltsRoot").toString())
+ return File(project.extraPropertyOrNull("prebuiltsRoot").toString())
}
/** @return the project's Android SDK stub JAR as a File. */
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt b/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt
index f34133e..330676e 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt
@@ -21,7 +21,6 @@
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.Project
-import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Usage
import org.gradle.api.file.FileTreeElement
@@ -29,12 +28,8 @@
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskProvider
import org.gradle.jvm.tasks.Jar
-import org.gradle.kotlin.dsl.findByType
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.named
-import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
-import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
-import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
/** Allow java and Android libraries to bundle other projects inside the project jar/aar. */
object BundleInsideHelper {
@@ -90,101 +85,6 @@
}
/**
- * Creates a configuration for the users to use that will be used bundle these dependency jars
- * inside of this project's jar.
- *
- * ```
- * dependencies {
- * bundleInside(project(":foo"))
- * debugBundleInside(project(path: ":bar", configuration: "someDebugConfiguration"))
- * releaseBundleInside(project(path: ":bar", configuration: "someReleaseConfiguration"))
- * }
- * ```
- *
- * @param from specifies from which package the rename should happen
- * @param to specifies to which package to put the renamed classes
- * @param dropResourcesWithSuffix used to drop Java resources if they match this suffix, null
- * means no filtering
- * @receiver the project that should bundle jars specified by these configurations
- */
- @JvmStatic
- fun Project.forInsideJar(from: String, to: String, dropResourcesWithSuffix: String?) {
- val bundle = createBundleConfiguration()
- val repackage =
- configureRepackageTaskForType(
- relocations = listOf(Relocation(from, to)),
- configuration = bundle,
- dropResourcesWithSuffix = dropResourcesWithSuffix
- )
- dependencies.add("compileOnly", files(repackage.flatMap { it.archiveFile }))
- dependencies.add("testImplementation", files(repackage.flatMap { it.archiveFile }))
-
- val jarTask = tasks.named("jar")
- jarTask.configure {
- it as Jar
- it.from(repackage.map { files(zipTree(it.archiveFile.get().asFile)) })
- }
- addArchivesToConfiguration("apiElements", jarTask)
- addArchivesToConfiguration("runtimeElements", jarTask)
- }
-
- private fun Project.addArchivesToConfiguration(
- configName: String,
- jarTask: TaskProvider<Task>
- ) {
- configurations.getByName(configName) {
- it.outgoing.artifacts.clear()
- it.outgoing.artifact(
- jarTask.flatMap { jarTask ->
- jarTask as Jar
- jarTask.archiveFile
- }
- )
- }
- }
-
- /**
- * KMP Version of [Project.forInsideJar]. See those docs for details.
- *
- * @param dropResourcesWithSuffix used to drop Java resources if they match this suffix,
- * * null means no filtering
- *
- * TODO(b/237104605): bundleInside is a global configuration. Should figure out how to make it
- * work properly with kmp and source sets so it can reside inside a sourceSet dependency.
- */
- @JvmStatic
- fun Project.forInsideJarKmp(from: String, to: String, dropResourcesWithSuffix: String?) {
- val kmpExtension =
- extensions.findByType<KotlinMultiplatformExtension>() ?: error("kmp only")
- val bundle = createBundleConfiguration()
- val repackage =
- configureRepackageTaskForType(
- relocations = listOf(Relocation(from, to)),
- configuration = bundle,
- dropResourcesWithSuffix = dropResourcesWithSuffix
- )
-
- // To account for KMP structure we need to find the jvm specific target
- // and add the repackaged archive files to only their compilations.
- val jvmTarget =
- kmpExtension.targets.firstOrNull { it.platformType == KotlinPlatformType.jvm }
- as? KotlinJvmTarget ?: error("cannot find jvm target")
- jvmTarget.compilations["main"].defaultSourceSet {
- dependencies { compileOnly(files(repackage.flatMap { it.archiveFile })) }
- }
- jvmTarget.compilations["test"].defaultSourceSet {
- dependencies { implementation(files(repackage.flatMap { it.archiveFile })) }
- }
- val jarTask = tasks.named(jvmTarget.artifactsTaskName)
- jarTask.configure {
- it as Jar
- it.from(repackage.map { files(zipTree(it.archiveFile.get().asFile)) })
- }
- addArchivesToConfiguration("jvmApiElements", jarTask)
- addArchivesToConfiguration("jvmRuntimeElements", jarTask)
- }
-
- /**
* Creates a configuration for users to use that will bundle the dependency jars
* inside of this lint check's jar. This is required because lintPublish does not currently
* support dependencies, so instead we need to bundle any dependencies with the lint jar
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
index ab190af..74e1548 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
@@ -16,6 +16,7 @@
package androidx.build
+import androidx.build.gradle.extraPropertyOrNull
import java.util.Locale
import org.gradle.api.Project
import org.gradle.kotlin.dsl.create
@@ -116,7 +117,9 @@
/** Extension used to store parsed KMP configuration information. */
private open class KmpPlatformsExtension(project: Project) {
val enabledKmpPlatforms =
- parseTargetPlatformsFlag(project.findProperty(ENABLED_KMP_TARGET_PLATFORMS) as? String)
+ parseTargetPlatformsFlag(
+ project.extraPropertyOrNull(ENABLED_KMP_TARGET_PLATFORMS) as? String
+ )
}
fun Project.enableJs(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.JS)
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/LibraryType.kt b/buildSrc/public/src/main/kotlin/androidx/build/LibraryType.kt
index 2ed87dc..ebdf2b8 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/LibraryType.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/LibraryType.kt
@@ -56,25 +56,25 @@
* b/281843422 UNSET: a library that has not yet been migrated to using LibraryType. Should never be
* used. APP: an app, such as an example app or integration testsapp. Should never be used; apps
* should not apply the AndroidX plugin or have an androidx block in their build.gradle files.
- *
- * TODO: potential future LibraryTypes: KOTLIN_ONLY_LIBRARY: like PUBLISHED_LIBRARY, but not
- * intended for use from java. ktx and compose. INTERNAL_TEST DEMO IDE_PLUGIN
*/
sealed class LibraryType(
val publish: Publish = Publish.NONE,
val sourceJars: Boolean = false,
val checkApi: RunApiTasks = RunApiTasks.No("Unknown Library Type"),
val compilationTarget: CompilationTarget = CompilationTarget.DEVICE,
- val allowCallingVisibleForTestsApis: Boolean = false
+ val allowCallingVisibleForTestsApis: Boolean = false,
+ val targetsKotlinConsumersOnly: Boolean = false
) {
val name: String
get() = javaClass.simpleName
companion object {
val PUBLISHED_LIBRARY = PublishedLibrary()
- val PUBLISHED_KOTLIN_ONLY_LIBRARY = PublishedKotlinOnlyLibrary()
+ val PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS =
+ PublishedLibrary(targetsKotlinConsumersOnly = true)
val PUBLISHED_TEST_LIBRARY = PublishedTestLibrary()
- val PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY = PublishedKotlinOnlyTestLibrary()
+ val PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY =
+ PublishedTestLibrary(targetsKotlinConsumersOnly = true)
val INTERNAL_TEST_LIBRARY = InternalTestLibrary()
val INTERNAL_HOST_TEST_LIBRARY = InternalHostTestLibrary()
val SAMPLES = Samples()
@@ -95,7 +95,8 @@
private val allTypes =
mapOf(
"PUBLISHED_LIBRARY" to PUBLISHED_LIBRARY,
- "PUBLISHED_KOTLIN_ONLY_LIBRARY" to PUBLISHED_KOTLIN_ONLY_LIBRARY,
+ "PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS"
+ to PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS,
"PUBLISHED_TEST_LIBRARY" to PUBLISHED_TEST_LIBRARY,
"PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY" to PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY,
"INTERNAL_TEST_LIBRARY" to INTERNAL_TEST_LIBRARY,
@@ -121,20 +122,16 @@
}
}
- open class PublishedLibrary(allowCallingVisibleForTestsApis: Boolean = false) :
+ open class PublishedLibrary(
+ allowCallingVisibleForTestsApis: Boolean = false,
+ targetsKotlinConsumersOnly: Boolean = false
+ ) :
LibraryType(
publish = Publish.SNAPSHOT_AND_RELEASE,
sourceJars = true,
checkApi = RunApiTasks.Yes(),
- allowCallingVisibleForTestsApis = allowCallingVisibleForTestsApis
- )
-
- open class PublishedKotlinOnlyLibrary(allowCallingVisibleForTestsApis: Boolean = false) :
- LibraryType(
- publish = Publish.SNAPSHOT_AND_RELEASE,
- sourceJars = true,
- checkApi = RunApiTasks.Yes(),
- allowCallingVisibleForTestsApis = allowCallingVisibleForTestsApis
+ allowCallingVisibleForTestsApis = allowCallingVisibleForTestsApis,
+ targetsKotlinConsumersOnly = targetsKotlinConsumersOnly
)
open class InternalLibrary(
@@ -147,10 +144,10 @@
allowCallingVisibleForTestsApis = allowCallingVisibleForTestsApis
)
- class PublishedTestLibrary() : PublishedLibrary(allowCallingVisibleForTestsApis = true)
-
- class PublishedKotlinOnlyTestLibrary() : PublishedKotlinOnlyLibrary(
- allowCallingVisibleForTestsApis = true)
+ class PublishedTestLibrary(targetsKotlinConsumersOnly: Boolean = false) : PublishedLibrary(
+ allowCallingVisibleForTestsApis = true,
+ targetsKotlinConsumersOnly = targetsKotlinConsumersOnly
+ )
class InternalTestLibrary() : InternalLibrary(allowCallingVisibleForTestsApis = true)
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/ProjectLayoutType.kt b/buildSrc/public/src/main/kotlin/androidx/build/ProjectLayoutType.kt
index ec0129f..aedc32f 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/ProjectLayoutType.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/ProjectLayoutType.kt
@@ -16,6 +16,7 @@
package androidx.build
+import androidx.build.gradle.extraPropertyOrNull
import org.gradle.api.Project
enum class ProjectLayoutType {
@@ -26,7 +27,7 @@
/** Returns the project layout type for the project (PLAYGROUND or ANDROIDX) */
@JvmStatic
fun from(project: Project): ProjectLayoutType {
- val value = project.findProperty(STUDIO_TYPE)?.toString()
+ val value = project.extraPropertyOrNull(STUDIO_TYPE)
return when (value) {
"playground" -> ProjectLayoutType.PLAYGROUND
null,
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt b/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt
index 9b9c338..2fa6bde 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt
@@ -21,7 +21,6 @@
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.file.FileTree
-import org.gradle.api.plugins.ExtraPropertiesExtension
/** Writes the appropriate SDK path to local.properties file in specified location. */
fun Project.writeSdkPathToLocalPropertiesFile() {
@@ -50,7 +49,7 @@
/** Returns the root project's platform-specific SDK path as a file. */
fun Project.getSdkPath(): File {
if (
- rootProject.plugins.hasPlugin("AndroidXPlaygroundRootPlugin") ||
+ ProjectLayoutType.from(project) == ProjectLayoutType.PLAYGROUND ||
System.getenv("COMPOSE_DESKTOP_GITHUB_BUILD") != null
) {
// This is not full checkout, use local settings instead.
@@ -99,7 +98,7 @@
/** Sets the path to the canonical root project directory, e.g. {@code frameworks/support}. */
fun Project.setSupportRootFolder(rootDir: File?) {
- val extension = project.property("ext") as ExtraPropertiesExtension
+ val extension = project.extensions.extraProperties
return extension.set("supportRootFolder", rootDir)
}
@@ -110,7 +109,7 @@
* because it is generalized to also work for the "ui" project and playground projects.
*/
fun Project.getSupportRootFolder(): File {
- val extension = project.property("ext") as ExtraPropertiesExtension
+ val extension = project.extensions.extraProperties
return extension.get("supportRootFolder") as File
}
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/gradle/Extensions.kt b/buildSrc/public/src/main/kotlin/androidx/build/gradle/Extensions.kt
index 7d2ebd4..92bbc57 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/gradle/Extensions.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/gradle/Extensions.kt
@@ -20,3 +20,18 @@
val Project.isRoot
get() = this == rootProject
+
+/**
+ * Implements project.extensions.extraProperties.getOrNull(key)
+ * TODO(https://github.com/gradle/gradle/issues/28857) use simpler replacement when available
+ *
+ * Note that providers.gradleProperty() might return null in cases where this function can
+ * find a value: https://github.com/gradle/gradle/issues/23572
+ */
+fun Project.extraPropertyOrNull(key: String): Any? {
+ val container = project.extensions.extraProperties
+ var result: Any? = null
+ if (container.has(key))
+ result = container.get(key)
+ return result
+}
diff --git a/buildSrc/settingsScripts/project-dependency-graph.groovy b/buildSrc/settingsScripts/project-dependency-graph.groovy
index 1590a62..5fccfd5 100644
--- a/buildSrc/settingsScripts/project-dependency-graph.groovy
+++ b/buildSrc/settingsScripts/project-dependency-graph.groovy
@@ -295,7 +295,7 @@
private static Pattern composePlugin = Pattern.compile("id\\(\"AndroidXComposePlugin\"\\)")
private static Pattern iconGenerator = Pattern.compile("IconGenerationTask\\.register")
private static Pattern publishedLibrary = Pattern.compile(
- "(type = LibraryType\\.(PUBLISHED_LIBRARY|GRADLE_PLUGIN|ANNOTATION_PROCESSOR|PUBLISHED_KOTLIN_ONLY_LIBRARY)|" +
+ "(type = LibraryType\\.(PUBLISHED_LIBRARY|GRADLE_PLUGIN|ANNOTATION_PROCESSOR|PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS)|" +
"publish = Publish\\.SNAPSHOT_AND_RELEASE)"
)
}
diff --git a/buildSrc/shared-dependencies.gradle b/buildSrc/shared-dependencies.gradle
index 85b9b6d..651d48d 100644
--- a/buildSrc/shared-dependencies.gradle
+++ b/buildSrc/shared-dependencies.gradle
@@ -44,7 +44,6 @@
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.protobufGradlePlugin) // needed to compile inspection plugin
implementation(libs.kotlinPoet) // needed to compile material-icon-generator
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/EmptyFragment.kt b/buildSrc/stableAidlImports/android/graphics/Bitmap.aidl
similarity index 71%
copy from privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/EmptyFragment.kt
copy to buildSrc/stableAidlImports/android/graphics/Bitmap.aidl
index 3a27fde..5c5744a 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/EmptyFragment.kt
+++ b/buildSrc/stableAidlImports/android/graphics/Bitmap.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 The Android Open Source Project
+ * Copyright (C) 2024 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,9 +14,6 @@
* limitations under the License.
*/
-package androidx.privacysandbox.ui.integration.testapp
+package android.graphics;
-class EmptyFragment : BaseFragment() {
- override fun handleDrawerStateChange(isDrawerOpen: Boolean) {
- }
-}
+@JavaOnlyStableParcelable parcelable Bitmap;
diff --git a/camera/camera-camera2-pipe-integration/build.gradle b/camera/camera-camera2-pipe-integration/build.gradle
index d9da277..74db51a 100644
--- a/camera/camera-camera2-pipe-integration/build.gradle
+++ b/camera/camera-camera2-pipe-integration/build.gradle
@@ -27,7 +27,7 @@
id("AndroidXPlugin")
id("com.android.library")
id("kotlin-android")
- id("kotlin-kapt")
+ id("com.google.devtools.ksp")
}
dependencies {
@@ -46,7 +46,7 @@
implementation(project(":camera:camera-camera2-pipe"))
implementation(project(":concurrent:concurrent-futures-ktx"))
- kapt(libs.daggerCompiler)
+ ksp(libs.daggerCompiler)
testImplementation(libs.testCore)
testImplementation(libs.testRunner)
@@ -99,11 +99,9 @@
test.maxParallelForks(2)
}
-kapt {
- javacOptions {
- option("-Adagger.fastInit=enabled")
- option("-Adagger.fullBindingGraphValidation=ERROR")
- }
+ksp {
+ arg("dagger.fastInit", "enabled")
+ arg("dagger.fullBindingGraphValidation", "ERROR")
}
androidx {
diff --git a/camera/camera-camera2-pipe-integration/lint-baseline.xml b/camera/camera-camera2-pipe-integration/lint-baseline.xml
index 474fd2e..f556e20 100644
--- a/camera/camera-camera2-pipe-integration/lint-baseline.xml
+++ b/camera/camera-camera2-pipe-integration/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="CameraXQuirksClassDetector"
@@ -55,4 +55,1390 @@
file="src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AeFpsRange.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AeFpsRangeLegacyQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AfRegionFlipHorizontallyQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AspectRatioLegacyApi21Quirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AutoFlashAEModeDisabler.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CamcorderProfileResolutionQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/Camera2CameraControlCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/Camera2ImplConfig.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2Interop.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2Interop.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/config/CameraAppConfig.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraCompatibilityFilter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/config/CameraConfig.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlStateAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryProvider.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraFovInfo.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraInteropStateCallbackRepository.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO: b/200306659 - Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraMetadataIntegration.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CameraMetadataSafeGetter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraNoResponseWhenEnablingFlashQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/CameraPipeConfig.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraProperties.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraSelectionOptimizer.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureFailureAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CapturePipelineTorchCorrection.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/interop/CaptureRequestOptions.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/interop/CaptureRequestOptions.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionOnClosedNotCalledQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionStuckQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCameraDeviceOnCameraGraphCloseQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnDisconnectQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ConfigureSurfaceToSecondarySessionFailQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/CoroutineAdapters.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CrashWhenTakingPhotoWithAutoFlashAEModeQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/DeviceInfoLogger.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirks.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirksLoader.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DisableAbortCapturesOnStopWithSessionProcessorQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/DynamicRangeProfilesCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/DynamicRangeProfilesCompatBaseImpl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeResolver.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/EncoderProfilesProviderAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/EvCompCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/EvCompCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/EvCompControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/EvCompControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/EvCompValue.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExcludedSupportedSizesQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/ExposureStateAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/ExposureStateAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedOutputSizeQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ExtraSupportedSurfaceCombinationsContainer.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashAvailabilityBufferUnderflowQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/FlashAvailabilityChecker.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/FlashControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashTooSlowQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/GuaranteedConfigurationsUtil.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailWithAutoFlashQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFlashNotFireQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ImageCapturePixelHDRPlus.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCapturePixelHDRPlusQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWashedOutImageQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWithFlashUnderexposureQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/InactiveSurfaceCloser.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/JpegHalCorruptImageQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MaxPreviewSize.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MeteringRegionCorrection.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Nexus4AndroidLTargetAspectRatioQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/OutputSizesCorrector.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewOrientationIncorrectQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/PreviewPixelHDRnet.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewPixelHDRnetQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/RepeatingStreamConstraintForVideoRecordingQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/RequestProcessorAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrector.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/SessionConfigAdapter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/SessionProcessorManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/Sizes.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/State3AControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/StillCaptureFlashStopRepeatingQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/StillCaptureFlow.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatBaseImpl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseUtil.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/SupportedRepeatingSurfaceSize.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/243963130): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/SurfaceOrderQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/Tags.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TargetAspectRatio.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TextureViewIsClosedQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/TorchControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO: b/200306659 - Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchFlashRequiredFor3aUpdateQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchIsClosedAfterImageCapturingQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseSurfaceManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseThreads.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO: b/200306659 - Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseFlashModeTorchFor3aUpdate.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlash.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/UseTorchAsFlashQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/YuvImageOnePixelShiftQuirk.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZoomValue.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ZslDisablerQuirk.kt"/>
+ </issue>
+
</issues>
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseUtil.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseUtil.kt
index fb81b10..a50b113 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseUtil.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseUtil.kt
@@ -47,6 +47,7 @@
import androidx.camera.core.impl.UseCaseConfig
import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
import androidx.camera.core.streamsharing.StreamSharingConfig
+import androidx.core.util.Preconditions.checkState
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
object StreamUseCaseUtil {
@@ -135,16 +136,22 @@
// MeteringRepeating is attached after the StreamUseCase population logic and
// therefore won't have the StreamUseCase option. It should always have
// SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW
+ checkState(
+ sessionConfig.surfaces.isNotEmpty(),
+ "MeteringRepeating should contain a surface"
+ )
streamUseCaseMap[sessionConfig.surfaces[0]] =
SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW.toLong()
} else if (sessionConfig.implementationOptions.containsOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION
)
) {
- streamUseCaseMap[sessionConfig.surfaces[0]] =
- sessionConfig.implementationOptions.retrieveOption(
- STREAM_USE_CASE_STREAM_SPEC_OPTION
- )!!
+ if (sessionConfig.surfaces.isNotEmpty()) {
+ streamUseCaseMap[sessionConfig.surfaces[0]] =
+ sessionConfig.implementationOptions.retrieveOption(
+ STREAM_USE_CASE_STREAM_SPEC_OPTION
+ )!!
+ }
}
position++
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseTest.kt
index 67937d5..de62d38 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseTest.kt
@@ -48,6 +48,7 @@
import androidx.camera.testing.impl.fakes.FakeUseCaseConfig
import androidx.camera.testing.impl.fakes.FakeUseCaseConfigFactory
import androidx.concurrent.futures.ResolvableFuture
+import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.ListenableFuture
import junit.framework.TestCase
import org.junit.After
@@ -159,6 +160,47 @@
}
@Test
+ fun populateSurfaceToStreamUseCaseMapping_previewAndNoSurfaceVideoCapture() {
+ val streamUseCaseMap: MutableMap<DeferrableSurface, Long> = mutableMapOf()
+ val previewOptionsBundle = MutableOptionsBundle.create()
+ previewOptionsBundle.insertOption(
+ StreamUseCaseUtil.STREAM_USE_CASE_STREAM_SPEC_OPTION,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW.toLong()
+ )
+ val previewSessionConfig = SessionConfig.Builder()
+ .addSurface(mMockSurface1)
+ .addImplementationOptions(Camera2ImplConfig(previewOptionsBundle))
+ .build()
+ val videoCaptureOptionsBundle = MutableOptionsBundle.create()
+ videoCaptureOptionsBundle.insertOption(
+ StreamUseCaseUtil.STREAM_USE_CASE_STREAM_SPEC_OPTION,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD.toLong()
+ )
+ // VideoCapture doesn't contain a surface
+ val videoCaptureSessionConfig = SessionConfig.Builder()
+ .addImplementationOptions(Camera2ImplConfig(videoCaptureOptionsBundle))
+ .build()
+ val previewConfig = getFakeUseCaseConfigWithOptions(
+ camera2InteropOverride = true, isZslDisabled = false, isZslCaptureMode = false,
+ captureType = CaptureType.PREVIEW, imageFormat = ImageFormat.PRIVATE
+ )
+ val videoCaptureConfig = getFakeUseCaseConfigWithOptions(
+ camera2InteropOverride = true, isZslDisabled = false, isZslCaptureMode = false,
+ captureType = CaptureType.VIDEO_CAPTURE, imageFormat = ImageFormat.PRIVATE
+ )
+ val sessionConfigs =
+ mutableListOf(previewSessionConfig, videoCaptureSessionConfig)
+ val useCaseConfigs = mutableListOf(previewConfig, videoCaptureConfig)
+ StreamUseCaseUtil.populateSurfaceToStreamUseCaseMapping(
+ sessionConfigs, useCaseConfigs,
+ streamUseCaseMap
+ )
+ assertThat(streamUseCaseMap.size).isEqualTo(1)
+ assertThat(streamUseCaseMap[mMockSurface1])
+ .isEqualTo(CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW.toLong())
+ }
+
+ @Test
fun getStreamSpecImplementationOptions() {
val result: Camera2ImplConfig =
StreamUseCaseUtil.getStreamSpecImplementationOptions(
diff --git a/camera/camera-camera2-pipe-testing/lint-baseline.xml b/camera/camera-camera2-pipe-testing/lint-baseline.xml
new file mode 100644
index 0000000..fe9d32d
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/lint-baseline.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraDevices.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequence.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReader.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeMetadata.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeSurfaces.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeThreads.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/testing/FakeTimeSource.kt"/>
+ </issue>
+
+</issues>
diff --git a/camera/camera-camera2-pipe/build.gradle b/camera/camera-camera2-pipe/build.gradle
index 2afb6a4..5949205 100644
--- a/camera/camera-camera2-pipe/build.gradle
+++ b/camera/camera-camera2-pipe/build.gradle
@@ -27,7 +27,7 @@
id("AndroidXPlugin")
id("com.android.library")
id("kotlin-android")
- id("kotlin-kapt")
+ id("com.google.devtools.ksp")
}
dependencies {
@@ -38,7 +38,7 @@
implementation(libs.atomicFu)
implementation(libs.dagger)
- kapt(libs.daggerCompiler)
+ ksp(libs.daggerCompiler)
testImplementation(libs.testCore)
testImplementation(libs.testRunner)
@@ -52,7 +52,7 @@
testImplementation(project(":camera:camera-camera2-pipe-testing"))
testImplementation(project(":internal-testutils-truth"))
- kaptTest(libs.daggerCompiler)
+ kspTest(libs.daggerCompiler)
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testRunner)
@@ -68,11 +68,9 @@
namespace "androidx.camera.camera2.pipe"
}
-kapt {
- javacOptions {
- option("-Adagger.fastInit=enabled")
- option("-Adagger.fullBindingGraphValidation=ERROR")
- }
+ksp {
+ arg("dagger.fastInit", "enabled")
+ arg("dagger.fullBindingGraphValidation", "ERROR")
}
androidx {
diff --git a/camera/camera-camera2-pipe/lint-baseline.xml b/camera/camera-camera2-pipe/lint-baseline.xml
new file mode 100644
index 0000000..ffb906c
--- /dev/null
+++ b/camera/camera-camera2-pipe/lint-baseline.xml
@@ -0,0 +1,1066 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/media/AndroidImage.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/media/AndroidImageReaders.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/AndroidThreads.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2Backend.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraMetadata.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraStatusMonitor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequence.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2DeviceCache.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2DeviceCloser.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2ErrorProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataCache.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataProvider.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Camera2Quirks.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CameraControls.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CameraDevices.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/CameraDevicesImpl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CameraError.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/config/CameraGraphComponent.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphImpl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphSessionImpl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CameraMetadata.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/config/CameraPipeComponent.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/CameraPipeKeys.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CameraSurfaceManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CaptureSequence.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/CaptureSequence.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Configuration.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/Controller3A.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Debug.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Debug.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/Exceptions.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/ExtensionRequestFailure.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/ExtensionRequestFailure.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraPipeComponent.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraPipeComponent.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/media/Finalizer.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/Frame.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/Frame.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/FrameCaptureQueue.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/FrameDistributor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/FrameImpl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/FrameMetadata.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/FrameMetadata.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/FrameMetadata.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/FrameState.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/FrameState.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/FrameState.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/Frames.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/GraphLifecycleManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/GraphListener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/GraphRequestProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/GraphState3A.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/media/ImageReaderWrapper.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/ImageSourceMap.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/Listener3A.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Log.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/Metadata.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Mutexes.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Mutexes.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Mutexes.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Mutexes.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/OnSessionFinalized.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/internal/OutputDistributor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Permissions.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/RequestProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/Requests.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/Result3AStateListener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/media/SharedOutputImage.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/StreamFormat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/StreamGraph.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/StreamGraphImpl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/Streams.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/graph/SurfaceGraph.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/config/ThreadConfigModule.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Threading.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Threads.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Timestamps.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Timestamps.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Timestamps.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/Token.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/pipe/core/WakeLock.kt"/>
+ </issue>
+
+</issues>
diff --git a/camera/camera-camera2/lint-baseline.xml b/camera/camera-camera2/lint-baseline.xml
index 6c8f9a0..757f312 100644
--- a/camera/camera-camera2/lint-baseline.xml
+++ b/camera/camera-camera2/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="NewApi"
@@ -46,4 +46,1435 @@
file="src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/AeFpsRange.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/AeFpsRangeLegacyQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/AfRegionFlipHorizontallyQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/ApiCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/AspectRatioLegacyApi21Quirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/AutoFlashAEModeDisabler.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/AutoFlashUnderExposedQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CamcorderProfileHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/CamcorderProfileResolutionQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CameraCaptureFailure.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CameraCaptureResult.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/interop/Camera2CameraControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CameraFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/interop/Camera2CameraInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CaptureCallbacks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CaptureCallbacks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CaptureOptionUnpacker.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CapturePipeline.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/Camera2Config.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/impl/Camera2ImplConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/interop/Camera2Interop.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/interop/Camera2Interop.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2PhysicalCameraInfoImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2RequestProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2SessionOptionUnpacker.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraAccessExceptionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CameraBurstCaptureCallback.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraCaptureSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraCaptureSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraCaptureSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraCaptureSessionCompatBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CameraCaptureSessionStateCallbacks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CameraCaptureSessionStateCallbacks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/CameraCharacteristicsProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraDeviceCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraDeviceCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraDeviceCompatBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CameraDeviceId.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CameraDeviceStateCallbacks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CameraIdUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/CameraManagerCompatBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraNoResponseWhenEnablingFlashQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraQuirks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CameraSelectionOptimizer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CameraStateMachine.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CameraUnavailableExceptionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CaptureCallbackAdapter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CaptureCallbackContainer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CaptureCallbackConverter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/CaptureNoResponseQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/interop/CaptureRequestOptions.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/interop/CaptureRequestOptions.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CaptureSession.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CaptureSessionInterface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/CaptureSessionOnClosedNotCalledQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CaptureSessionRepository.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/CaptureSessionStuckQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ConfigureSurfaceToSecondarySessionFailQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/CrashWhenTakingPhotoWithAutoFlashAEModeQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/CropRegionZoomImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/DeviceQuirks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/DeviceQuirksLoader.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/DisplayInfoManager.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/DisplaySizeCorrector.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/DynamicRangeResolver.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangesCompatBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/ExcludedSupportedSizesContainer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ExcludedSupportedSizesQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/ExposureControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/ExposureStateImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ExtraCroppingQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ExtraSupportedOutputSizeQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/FlashAvailabilityBufferUnderflowQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/FlashAvailabilityChecker.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/FlashTooSlowQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/ForceCloseCaptureSession.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/ForceCloseDeferrableSurface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/FovUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/GuaranteedConfigurationsUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ImageCaptureFailWithAutoFlashQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ImageCaptureFlashNotFireQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/ImageCaptureOptionUnpacker.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/ImageCapturePixelHDRPlus.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ImageCapturePixelHDRPlusQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ImageCaptureWashedOutImageQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ImageCaptureWithFlashUnderexposureQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/IncorrectCaptureStateQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/params/InputConfigurationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/InvalidVideoProfilesQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/JpegCaptureDownsizingQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/JpegHalCorruptImageQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/LensFacingUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/MaxPreviewSize.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/MeteringRegionCorrection.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/MeteringRepeatingSession.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/Nexus4AndroidLTargetAspectRatioQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // Needed for LegacyCameraDevice reflection"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/OutputSizesCorrector.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/OverrideAeModeForStillCapture.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/Preview3AThreadCrashQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/PreviewOrientationIncorrectQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/PreviewPixelHDRnet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/PreviewPixelHDRnetQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/RepeatingStreamConstraintForVideoRecordingQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/RequestMonitor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/ResolutionCorrector.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/params/SessionConfigurationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/params/SessionConfigurationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/SessionResetPolicy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/SmallDisplaySizeQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/StillCaptureFlashStopRepeatingQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/StillCaptureFlow.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/SupportedRepeatingSurfaceSize.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/SynchronizedCaptureSession.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionStateCallbacks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionStateCallbacks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/TargetAspectRatio.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/TemplateTypeUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/TextureViewIsClosedQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/TorchControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/TorchFlashRequiredFor3aUpdateQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/TorchIsClosedAfterImageCapturingQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/TorchStateReset.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/UseFlashModeTorchFor3aUpdate.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/workaround/UseTorchAsFlash.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/UseTorchAsFlashQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/VideoStabilizationUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/YuvImageOnePixelShiftQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/ZoomControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/ZoomStateImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/compat/quirk/ZslDisablerQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/camera2/internal/ZslUtil.java"/>
+ </issue>
+
</issues>
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
index 933806c..d1c1258 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
@@ -155,14 +155,18 @@
// MeteringRepeating is attached after the StreamUseCase population logic and
// therefore won't have the StreamUseCase option. It should always have
// SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW
+ Preconditions.checkState(!sessionConfig.getSurfaces().isEmpty(),
+ "MeteringRepeating should contain a surface");
streamUseCaseMap.put(sessionConfig.getSurfaces().get(0),
Long.valueOf(CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW));
} else if (sessionConfig.getImplementationOptions().containsOption(
STREAM_USE_CASE_STREAM_SPEC_OPTION)) {
- streamUseCaseMap.put(sessionConfig.getSurfaces().get(0),
- sessionConfig.getImplementationOptions().retrieveOption(
- STREAM_USE_CASE_STREAM_SPEC_OPTION));
+ if (!sessionConfig.getSurfaces().isEmpty()) {
+ streamUseCaseMap.put(sessionConfig.getSurfaces().get(0),
+ sessionConfig.getImplementationOptions().retrieveOption(
+ STREAM_USE_CASE_STREAM_SPEC_OPTION));
+ }
}
position++;
}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CapturePipelineTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CapturePipelineTest.kt
index 303f4dd..c37c298 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CapturePipelineTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CapturePipelineTest.kt
@@ -1285,15 +1285,15 @@
timeout: Long = TimeUnit.SECONDS.toMillis(5),
verifyResults: (captureRequests: List<CaptureConfig>) -> Boolean = { true }
) {
+ val resultPair = Pair(CountDownLatch(1), verifyResults)
synchronized(lock) {
allResults.forEach {
if (verifyResults(it)) {
return
}
}
+ waitingList.add(resultPair)
}
- val resultPair = Pair(CountDownLatch(1), verifyResults)
- waitingList.add(resultPair)
assertTrue(resultPair.first.await(timeout, TimeUnit.MILLISECONDS))
waitingList.remove(resultPair)
}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java
index 5a4072b..a661164 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/StreamUseCaseTest.java
@@ -25,6 +25,8 @@
import static androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY;
import static androidx.camera.core.ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
@@ -177,6 +179,42 @@
}
@Test
+ public void populateSurfaceToStreamUseCaseMapping_previewAndNoSurfaceVideoCapture() {
+ Map<DeferrableSurface, Long> streamUseCaseMap = new HashMap<>();
+ MutableOptionsBundle previewOptionsBundle = MutableOptionsBundle.create();
+ previewOptionsBundle.insertOption(STREAM_USE_CASE_STREAM_SPEC_OPTION,
+ Long.valueOf(CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW));
+ SessionConfig previewSessionConfig =
+ new SessionConfig.Builder()
+ .addSurface(mMockSurface1)
+ .addImplementationOptions(
+ new Camera2ImplConfig(previewOptionsBundle)).build();
+ UseCaseConfig<?> previewConfig = getFakeUseCaseConfigWithOptions(true, false, false,
+ UseCaseConfigFactory.CaptureType.PREVIEW, ImageFormat.PRIVATE);
+ MutableOptionsBundle videoOptionsBundle = MutableOptionsBundle.create();
+ videoOptionsBundle.insertOption(STREAM_USE_CASE_STREAM_SPEC_OPTION,
+ Long.valueOf(CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD));
+ // VideoCapture doesn't contain a surface
+ SessionConfig videoCaptureSessionConfig =
+ new SessionConfig.Builder()
+ .addImplementationOptions(
+ new Camera2ImplConfig(videoOptionsBundle)).build();
+ UseCaseConfig<?> videoCaptureConfig = getFakeUseCaseConfigWithOptions(true, false, false,
+ UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE, ImageFormat.PRIVATE);
+ ArrayList<SessionConfig> sessionConfigs = new ArrayList<>();
+ sessionConfigs.add(previewSessionConfig);
+ sessionConfigs.add(videoCaptureSessionConfig);
+ ArrayList<UseCaseConfig<?>> useCaseConfigs = new ArrayList<>();
+ useCaseConfigs.add(previewConfig);
+ useCaseConfigs.add(videoCaptureConfig);
+ StreamUseCaseUtil.populateSurfaceToStreamUseCaseMapping(sessionConfigs, useCaseConfigs,
+ streamUseCaseMap);
+ assertThat(streamUseCaseMap.size()).isEqualTo(1);
+ assertThat(streamUseCaseMap.get(mMockSurface1)).isEqualTo(Long.valueOf(
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW));
+ }
+
+ @Test
public void getStreamSpecImplementationOptions() {
Camera2ImplConfig result =
StreamUseCaseUtil.getStreamSpecImplementationOptions(
diff --git a/camera/camera-core/lint-baseline.xml b/camera/camera-core/lint-baseline.xml
index f1db923..c7745c1 100644
--- a/camera/camera-core/lint-baseline.xml
+++ b/camera/camera-core/lint-baseline.xml
@@ -1,5 +1,2453 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/Absent.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/AndroidImageProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/AndroidImageReaderProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/AspectRatio.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/resolutionselector/AspectRatioStrategy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/AspectRatioUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/AspectRatioUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/futures/AsyncFunction.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/AttachedSurfaceInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/executor/AudioExecutor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/Bitmap2JpegBytes.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/BitmapEffect.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/ByteBufferOutputStream.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/ByteOrderedDataInputStream.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/ByteOrderedDataOutputStream.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/Camera.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraCaptureCallback.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraCaptureCallbacks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraCaptureFailure.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraCaptureMetaData.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraCaptureResult.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/CameraCaptureResultImageInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraCaptureResults.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraClosedException.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraConfigProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraConfigs.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraControlInternal.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraEffect.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraExecutor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraFilter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraFilters.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraInfoInternal.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraInfoUnavailableException.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraInternal.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraMode.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/CameraOrientationUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraProviderExecutionState.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraProviderInitRetryPolicy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraRepository.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/CameraRequest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraSelector.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraState.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraStateRegistry.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraThreadConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraUnavailableException.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CameraValidator.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraX.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraXConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/executor/CameraXExecutors.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CameraXThreads.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CaptureBundle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/CaptureBundles.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CaptureConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/workaround/CaptureFailedRetryEnabler.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/CaptureFailedRetryQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/CaptureNode.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/CaptureStage.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/futures/ChainingListenableFuture.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/CloseGuardHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/CompareSizesByArea.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ConcurrentCamera.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/Config.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ConfigProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ConstantObservable.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/ContextUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ConvergenceUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/DeferrableSurface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/DeferrableSurfaces.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/DeviceProperties.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirksLoader.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/executor/DirectExecutor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/DisplayOrientedMeteringPointFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/DynamicRange.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/DynamicRangeUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/DynamicRanges.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/EncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/EncoderProfilesProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/compat/EncoderProfilesProxyCompatBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/Exif.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/ExifAttribute.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/ExifData.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/ExifOutputStream.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/workaround/ExifRotationAvailability.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/ExifTag.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ExperimentalGetImage.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ExposureState.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ExtendableBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ExtendedCameraConfigProviderStore.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/FocusMeteringAction.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/FocusMeteringResult.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ForwardingCameraControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ForwardingCameraInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ForwardingImageProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/futures/FutureCallback.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/futures/FutureChain.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/futures/Futures.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/executor/HandlerScheduledExecutorService.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/executor/HighPriorityExecutor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/Identifier.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/Image2Bitmap.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/Image2JpegBytes.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageAnalysis.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ImageAnalysisConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageCapture.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ImageCaptureConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/ImageCaptureControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageCaptureException.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageCaptureExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageCaptureExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageCaptureExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageCaptureExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageCaptureExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageCaptureLatencyEstimate.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/ImageCaptureRotationOptionQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ImageFormatConstants.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ImageInfoProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ImageInputConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ImageOutputConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageProcessingUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/ImageProcessorRequest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ImageProxyBundle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageProxyDownsampler.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageReaderFormatRecommender.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ImageReaderProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageReaderProxyProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImageReaderProxys.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/utils/ImageUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/futures/ImmediateFuture.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ImmediateSurface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ImmutableImageInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/ImmutableZoomState.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/IncorrectJpegMetadataQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/InitializationException.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/InternalImageProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParser.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/IoConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/executor/IoExecutor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/JpegBytes2CroppedBitmap.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/JpegBytes2Disk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/JpegBytes2Image.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/JpegImage2Result.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/workaround/JpegMetadataCorrector.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/LargeJpegImageQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/LensFacingCameraFilter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/futures/ListFuture.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/LiveDataObservable.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/Logger.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/LongRational.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/LowMemoryQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/MainThreadAsyncHandler.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/executor/MainThreadExecutor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/MediaActionSoundCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/MediaActionSoundCompatBaseImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/MetadataHolderService.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/MetadataImageReader.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/MeteringPoint.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/MeteringPointFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/MirrorMode.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/MultiValueSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/MutableConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/MutableOptionsBundle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/MutableStateObservable.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/MutableTagBundle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/NoMetadataImageReader.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/Observable.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/OnePixelShiftQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/OpenGlRenderer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/Optional.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/OptionsBundle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/OutputSurface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/OutputSurfaceConfiguration.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/Packet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/Present.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/Preview.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/PreviewCapabilities.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/capability/PreviewCapabilitiesImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/PreviewConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/ProcessingRequest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/Quirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/Quirks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/ReadableConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/RequestProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/RequestWithCallback.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/resolutionselector/ResolutionFilter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ResolutionInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/resolutionselector/ResolutionSelector.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/ResolutionSelectorUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/resolutionselector/ResolutionStrategy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/ResolutionsMerger.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/RestrictedCameraControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/RestrictedCameraInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/RetryPolicy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/RetryPolicyInternal.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/RgbaImageProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/SafeCloseImageReaderProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/ScreenFlashWrapper.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/executor/SequentialExecutor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/SessionConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/SessionProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/SessionProcessorSurface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/SessionProcessorUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/SettableImageProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/SingleCloseImageProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/SingleImageProxyBundle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/utils/SizeUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/SoftwareJpegEncodingPreferredQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/stabilization/StabilizationMode.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/StateObservable.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/StreamSharing.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/StreamSharingBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/StreamSharingConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/StreamSpec.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/SurfaceCombination.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/SurfaceConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/SurfaceEdge.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/quirk/SurfaceOrderQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/SurfaceOrientedMeteringPointFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/SurfaceProcessorInternal.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = 21)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/SurfaceProcessorNode.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/processing/SurfaceProcessorWithExecutor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/SurfaceRequest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/SurfaceSizeDefinition.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/compat/workaround/SurfaceSorter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/TagBundle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/TakePictureManager.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/imagecapture/TakePictureRequest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/TargetConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/ThreadConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/Threads.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/Timebase.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/TimeoutRetryPolicy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/TorchState.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/utils/TransformUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/UseCase.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/UseCaseAttachState.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/UseCaseConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/impl/UseCaseConfigFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/utils/UseCaseConfigUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/UseCaseEventConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/UseCaseGroup.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/utils/VideoUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ViewPort.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/ViewPorts.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/VirtualCamera.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/VirtualCameraAdapter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/VirtualCameraCaptureResult.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/streamsharing/VirtualCameraInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/ZoomState.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/core/internal/utils/ZslRingBuffer.java"/>
+ </issue>
<issue
id="UnknownNullness"
diff --git a/camera/camera-core/src/main/cpp/CMakeLists.txt b/camera/camera-core/src/main/cpp/CMakeLists.txt
index 8293c0f..0e8862c 100644
--- a/camera/camera-core/src/main/cpp/CMakeLists.txt
+++ b/camera/camera-core/src/main/cpp/CMakeLists.txt
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations under
# the License.
#
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.22.1)
project(camera_core_jni)
diff --git a/camera/camera-effects-still-portrait/lint-baseline.xml b/camera/camera-effects-still-portrait/lint-baseline.xml
new file mode 100644
index 0000000..5dfb62c
--- /dev/null
+++ b/camera/camera-effects-still-portrait/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/stillportrait/StillPortrait.java"/>
+ </issue>
+
+</issues>
diff --git a/camera/camera-effects/lint-baseline.xml b/camera/camera-effects/lint-baseline.xml
new file mode 100644
index 0000000..70c3cc5
--- /dev/null
+++ b/camera/camera-effects/lint-baseline.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/Frame.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/opengl/GlContext.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/opengl/GlProgram.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/opengl/GlProgramCopy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/opengl/GlProgramOverlay.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/opengl/GlRenderer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/OverlayEffect.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/internal/SurfaceProcessorImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/internal/TextureFrame.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/internal/TextureFrameBuffer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/effects/opengl/Utils.java"/>
+ </issue>
+
+</issues>
diff --git a/camera/camera-extensions/lint-baseline.xml b/camera/camera-extensions/lint-baseline.xml
index 92ee546..74a63c6 100644
--- a/camera/camera-extensions/lint-baseline.xml
+++ b/camera/camera-extensions/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.2.0-alpha14" type="baseline" client="cli" dependencies="false" name="AGP (8.2.0-alpha14)" variant="all" version="8.2.0-alpha14">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -10,4 +10,427 @@
file="src/main/java/androidx/camera/extensions/internal/compat/workaround/OnEnableDisableSessionDurationCheck.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/AdvancedVendorExtender.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/compat/workaround/AvailableKeysRetriever.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/Camera2CameraCaptureResult.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfigConverter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2SessionConfigBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/CameraExtensionsControls.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/CameraExtensionsInfos.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/CaptureResultImageMatcher.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/ClientVersion.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/compat/quirk/CrashWhenOnDisableTooSoon.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/compat/quirk/DeviceQuirks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/compat/quirk/DeviceQuirksLoader.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/ExtensionCameraFilter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/compat/quirk/ExtensionDisabledQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/compat/workaround/ExtensionDisabledValidator.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/ExtensionMode.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/ExtensionVersion.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/ExtensionsConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/ExtensionsInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/ExtensionsManager.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/ExtensionsUseCaseConfigFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/ExtensionsUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/compat/quirk/GetAvailableKeysNeedsOnInit.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/ImageAnalysisConfigProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/ImageCaptureConfigProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/ImageProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/ImageReaderOutputConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/ImageReference.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/MultiResolutionImageReaderOutputConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/compat/workaround/OnEnableDisableSessionDurationCheck.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/PreviewConfigProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/PreviewProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/RequestBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/RequestOptionConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/SessionProcessorBase.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/StillCaptureProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // T"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/SurfaceOutputConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/VendorExtender.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/VendorExtenderFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/VendorProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/Version.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/internal/sessionprocessor/YuvToJpegConverter.java"/>
+ </issue>
+
</issues>
diff --git a/camera/camera-lifecycle/lint-baseline.xml b/camera/camera-lifecycle/lint-baseline.xml
new file mode 100644
index 0000000..9470205
--- /dev/null
+++ b/camera/camera-lifecycle/lint-baseline.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/lifecycle/LifecycleCamera.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/lifecycle/LifecycleCameraProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/lifecycle/ProcessCameraProviderExt.kt"/>
+ </issue>
+
+</issues>
diff --git a/camera/camera-mlkit-vision/lint-baseline.xml b/camera/camera-mlkit-vision/lint-baseline.xml
index f856ce3..d7dcf61 100644
--- a/camera/camera-mlkit-vision/lint-baseline.xml
+++ b/camera/camera-mlkit-vision/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="UnsafeOptInUsageError"
@@ -10,4 +10,13 @@
file="src/main/java/androidx/camera/mlkit/vision/MlKitAnalyzer.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/mlkit/vision/MlKitAnalyzer.java"/>
+ </issue>
+
</issues>
diff --git a/camera/camera-testing/lint-baseline.xml b/camera/camera-testing/lint-baseline.xml
index 58c53b0..9528dcb 100644
--- a/camera/camera-testing/lint-baseline.xml
+++ b/camera/camera-testing/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -28,4 +28,814 @@
file="src/test/java/androidx/camera/testing/mocks/MockObserverTest.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/AndroidUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/mocks/helpers/ArgumentCaptor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/mocks/helpers/ArgumentMatcher.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/AudioUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/mocks/helpers/CallTimes.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/mocks/helpers/CallTimesAtLeast.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/activity/Camera2TestActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/activity/Camera2TestActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/activity/Camera2TestActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/Camera2Util.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/Camera2Util.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CameraAvailabilityUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CameraUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CameraUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CameraUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CameraUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CameraUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CameraUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CameraUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/activity/CameraXTestActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/activity/CameraXTestActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CameraXUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CaptureSimulation.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/Configs.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CoreAppTestUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/CoroutineAdapters.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/DeferrableSurfacesUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/EncoderProfilesUtil.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/ExifUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/fakes/FakeAppConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/fakes/FakeCamera.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeCameraCaptureResult.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeCameraCaptureResult.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeCameraConfig.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeCameraCoordinator.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeCameraDeviceSurfaceManager.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeCameraFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeCameraFilter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeCaptureStage.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeDeferrableSurface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeEncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeEncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeImageEffect.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeImageInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeImageProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeImageReaderProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeJpegPlaneProxy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeLifecycleOwner.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeMultiValueSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeSessionConfigOptionUnpacker.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeSessionProcessor.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeSurfaceEffect.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeSurfaceProcessor.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeSurfaceProcessorInternal.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeUseCase.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeUseCaseConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeUseCaseConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeUseCaseConfigFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/FakeVideoEncoderInfo.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/FileUtil.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/activity/ForegroundTestActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/GLUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/GarbageCollectionUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/fakes/GrayscaleImageEffect.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/HandlerUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/IgnoreAudioProblematicDeviceRule.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/IgnoreProblematicDeviceRule.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/ImageProxyUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/LabTestRule.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/compat/LooperCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/mocks/MockConsumer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/mocks/MockConsumerExtensions.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/mocks/MockObserver.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlash.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/ObservableExtensions.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/RequiresDevice.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/RequiresDeviceFilter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/SurfaceFormatUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/SurfaceTextureProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/SurfaceTextureProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/TestImageUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt"/>
+ </issue>
+
</issues>
diff --git a/camera/camera-testing/src/main/cpp/CMakeLists.txt b/camera/camera-testing/src/main/cpp/CMakeLists.txt
index af959ea..1023b50 100644
--- a/camera/camera-testing/src/main/cpp/CMakeLists.txt
+++ b/camera/camera-testing/src/main/cpp/CMakeLists.txt
@@ -14,7 +14,7 @@
# the License.
#
-cmake_minimum_required(VERSION 3.4.1)
+cmake_minimum_required(VERSION 3.22.1)
project(camera_testing_jni)
diff --git a/camera/camera-testlib-extensions/lint-baseline.xml b/camera/camera-testlib-extensions/lint-baseline.xml
new file mode 100644
index 0000000..c8b6ef8
--- /dev/null
+++ b/camera/camera-testlib-extensions/lint-baseline.xml
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/CameraCharacteristicAvailability.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/ExtensionsTestlibControl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/GLImage2SurfaceRenderer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/InitializerImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/InitializerImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/InitializerImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/advanced/LongCaptureAdvancedExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/advanced/LongCaptureAdvancedExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/NoOpCaptureProcessorImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpls.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/extensions/impl/SettableCaptureStage.java"/>
+ </issue>
+
+</issues>
diff --git a/camera/camera-video/lint-baseline.xml b/camera/camera-video/lint-baseline.xml
index 3d8b587..409bb11 100644
--- a/camera/camera-video/lint-baseline.xml
+++ b/camera/camera-video/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -37,4 +37,1147 @@
file="src/androidTest/java/androidx/camera/video/internal/encoder/VideoEncoderTest.kt"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/AudioConfigUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/AudioEncoderConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/AudioEncoderConfigAudioProfileResolver.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/AudioEncoderConfigDefaultResolver.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/AudioEncoderIgnoresInputTimestampQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/AudioEncoderInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/AudioEncoderInfoImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/AudioMimeInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/AudioSettings.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/AudioSettingsAudioProfileResolver.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/AudioSettingsDefaultResolver.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/AudioSource.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/AudioSourceAccessException.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/AudioSpec.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/AudioStats.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/AudioStream.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/AudioStreamFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/AudioStreamImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/AudioTimestampFramePositionIncorrectQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/AudioUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/BackupHdrProfileEncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/BufferCopiedEncodedData.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/BufferProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/BufferedAudioStream.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/BufferedAudioStream.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/CameraUseInconsistentTimebaseQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/CapabilitiesByQuality.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/CodecStuckOnFlushQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/utils/CodecUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/workaround/CorrectNegativeLatLongForMediaMuxer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/DeactivateEncoderSurfaceBeforeStopEncoderQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/DebugUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/DeviceQuirksLoader.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/DynamicRangeMatchedEncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/EncodeException.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/EncodedData.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/EncodedDataImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/Encoder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/EncoderCallback.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/EncoderConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/EncoderInfoImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/EncoderNotUsePersistentInputSurfaceQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/utils/EncoderProfilesUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/ExcludeStretchedVideoQualityQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/ExtraSupportedQualityQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/ExtraSupportedResolutionQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/FallbackStrategy.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/FileDescriptorOutputOptions.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/FileDescriptorOutputOptions.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/FileOutputOptions.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/FileOutputOptions.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/ImageCaptureFailedWhenVideoCaptureIsBoundQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/InputBuffer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/InputBufferImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/InvalidConfigException.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/MediaCodecInfoReportIncorrectInfoQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/MediaFormatMustNotUseFrameRateToFindEncoderQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/MediaSpec.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/MediaStoreOutputOptions.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/MediaStoreVideoCannotWrite.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/MimeInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/NegativeLatLongSavesIncorrectlyQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/OutputConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/OutputOptions.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/OutputResults.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/utils/OutputUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/PendingRecording.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/PreviewDelayWhenVideoCaptureIsBoundQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/PreviewStretchWhenVideoCaptureIsBoundQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/Quality.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/Quality.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/workaround/QualityAddedEncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/QualityExploredEncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/QualityRatioToResolutionsTable.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/workaround/QualityResolutionModifiedEncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/QualitySelector.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/workaround/QualityValidatedEncoderProfilesProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/Recorder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/Recorder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/Recorder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/RecorderVideoCapabilities.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/Recording.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/RecordingStats.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/ReportedVideoQualityNotSupportedQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/SharedByteBuffer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/SignalEosOutputBufferNotComeQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/audio/SilentAudioStream.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/SizeCannotEncodeVideoQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/StopCodecAfterSurfaceRemovalCrashMediaServerQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/StreamInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/StretchedVideoResolutionQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/SwappedVideoEncoderInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/SystemTimeProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/TemporalNoiseQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/TimeProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoCapabilities.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoCapture.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoCapture.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/impl/VideoCaptureConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/VideoConfigUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/VideoEncoderConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolver.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigVideoProfileResolver.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/VideoEncoderCrashQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/VideoEncoderDataSpace.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/VideoEncoderInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/encoder/VideoEncoderInfoImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/workaround/VideoEncoderInfoWrapper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoEncoderSession.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/VideoEncoderSuspendDoesNotIncludeSuspendTimeQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/config/VideoMimeInfo.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoOutput.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/compat/quirk/VideoQualityQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoRecordEvent.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoRecordEvent.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoRecordEvent.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoRecordEvent.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoRecordEvent.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoRecordEvent.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/VideoSpec.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/workaround/VideoTimebaseConverter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/video/internal/VideoValidatedEncoderProfilesProxy.java"/>
+ </issue>
+
</issues>
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
index 18fbada..cbbe2a7 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
@@ -61,6 +61,8 @@
import androidx.camera.testing.impl.mocks.helpers.CallTimesAtLeast
import androidx.camera.video.Recorder.VIDEO_CAPABILITIES_SOURCE_CAMCORDER_PROFILE
import androidx.camera.video.Recorder.VIDEO_CAPABILITIES_SOURCE_CODEC_CAPABILITIES
+import androidx.camera.video.Recorder.sRetrySetupVideoDelayMs
+import androidx.camera.video.Recorder.sRetrySetupVideoMaxCount
import androidx.camera.video.VideoOutput.SourceState.ACTIVE_NON_STREAMING
import androidx.camera.video.VideoOutput.SourceState.ACTIVE_STREAMING
import androidx.camera.video.VideoOutput.SourceState.INACTIVE
@@ -962,16 +964,10 @@
fun canRecoverFromErrorState(): Unit = runBlocking {
// Arrange.
// Create a video encoder factory that will fail on first 2 create encoder requests.
- var createEncoderRequestCount = 0
val recorder = createRecorder(
- videoEncoderFactory = { executor, config ->
- if (createEncoderRequestCount < 2) {
- createEncoderRequestCount++
- throw InvalidConfigException("Create video encoder fail on purpose.")
- } else {
- Recorder.DEFAULT_ENCODER_FACTORY.createEncoder(executor, config)
- }
- })
+ videoEncoderFactory = createVideoEncoderFactory(failCreationTimes = 2),
+ retrySetupVideoMaxCount = 0, // Don't retry
+ )
// Recorder initialization should fail by 1st encoder creation fail.
// Wait STREAM_ID_ERROR which indicates Recorder enter the error state.
withTimeoutOrNull(3000) {
@@ -992,6 +988,25 @@
}
@Test
+ fun canRetrySetupVideo(): Unit = runBlocking {
+ // Arrange.
+ // Create a video encoder factory that will fail on first 2 create encoder requests.
+ val recorder = createRecorder(
+ videoEncoderFactory = createVideoEncoderFactory(failCreationTimes = 2),
+ retrySetupVideoMaxCount = 3,
+ retrySetupVideoDelayMs = 10, // make test quicker
+ )
+
+ // Act and verify.
+ val recording = createRecordingProcess(recorder = recorder)
+ recording.startAndVerify()
+ recording.stopAndVerify { finalize ->
+ // Assert.
+ assertThat(finalize.error).isEqualTo(ERROR_NONE)
+ }
+ }
+
+ @Test
@SdkSuppress(minSdkVersion = 31)
fun audioRecordIsAttributed() = runBlocking {
// Arrange.
@@ -1094,6 +1109,8 @@
videoEncoderFactory: EncoderFactory? = null,
audioEncoderFactory: EncoderFactory? = null,
targetBitrate: Int? = null,
+ retrySetupVideoMaxCount: Int? = null,
+ retrySetupVideoDelayMs: Long? = null,
): Recorder {
val recorder = Recorder.Builder().apply {
qualitySelector?.let { setQualitySelector(it) }
@@ -1102,7 +1119,10 @@
videoEncoderFactory?.let { setVideoEncoderFactory(it) }
audioEncoderFactory?.let { setAudioEncoderFactory(it) }
targetBitrate?.let { setTargetVideoEncodingBitRate(targetBitrate) }
- }.build()
+ }.build().apply {
+ retrySetupVideoMaxCount?.let { sRetrySetupVideoMaxCount = it }
+ retrySetupVideoDelayMs?.let { sRetrySetupVideoDelayMs = it }
+ }
if (sendSurfaceRequest) {
recorder.sendSurfaceRequest()
}
@@ -1295,6 +1315,19 @@
}
}
+ @Suppress("SameParameterValue")
+ private fun createVideoEncoderFactory(failCreationTimes: Int = 0): EncoderFactory {
+ var createEncoderRequestCount = 0
+ return EncoderFactory { executor, config ->
+ if (createEncoderRequestCount < failCreationTimes) {
+ createEncoderRequestCount++
+ throw InvalidConfigException("Create video encoder fail on purpose.")
+ } else {
+ Recorder.DEFAULT_ENCODER_FACTORY.createEncoder(executor, config)
+ }
+ }
+ }
+
// It fails on devices with certain chipset if the codec is stopped when the camera is still
// producing frames to the provided surface. This method first stop the camera from
// producing frames then stops the recording safely on the problematic devices.
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java b/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
index f05dfd2..f9d1af9 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
@@ -342,11 +342,18 @@
// The audio data is expected to be less than 1 kB, the value of the cache size is used to limit
// the memory used within an acceptable range.
private static final int AUDIO_CACHE_SIZE = 60;
+ private static final int RETRY_SETUP_VIDEO_MAX_COUNT = 3;
+ private static final long RETRY_SETUP_VIDEO_DELAY_MS = 1000L;
@VisibleForTesting
static final EncoderFactory DEFAULT_ENCODER_FACTORY = EncoderImpl::new;
private static final Executor AUDIO_EXECUTOR =
CameraXExecutors.newSequentialExecutor(CameraXExecutors.ioExecutor());
+ @VisibleForTesting
+ static int sRetrySetupVideoMaxCount = RETRY_SETUP_VIDEO_MAX_COUNT;
+ @VisibleForTesting
+ static long sRetrySetupVideoDelayMs = RETRY_SETUP_VIDEO_DELAY_MS;
+
private final MutableStateObservable<StreamInfo> mStreamInfo;
// Used only by getExecutor()
private final Executor mUserProvidedExecutor;
@@ -486,6 +493,8 @@
VideoEncoderSession mVideoEncoderSessionToRelease = null;
double mAudioAmplitude = 0;
private boolean mShouldSendResumeEvent = false;
+ @Nullable
+ private SetupVideoTask mSetupVideoTask = null;
//--------------------------------------------------------------------------------------------//
Recorder(@Nullable Executor executor, @NonNull MediaSpec mediaSpec,
@@ -798,7 +807,8 @@
"surface request is required to retry "
+ "initialization.");
}
- configureInternal(mLatestSurfaceRequest, mVideoSourceTimebase);
+ configureInternal(mLatestSurfaceRequest, mVideoSourceTimebase,
+ false);
});
} else {
setState(State.PENDING_RECORDING);
@@ -1024,7 +1034,7 @@
if (mLatestSurfaceRequest != null && !mLatestSurfaceRequest.isServiced()) {
mLatestSurfaceRequest.willNotProvideSurface();
}
- configureInternal(mLatestSurfaceRequest = request, mVideoSourceTimebase = timebase);
+ configureInternal(mLatestSurfaceRequest = request, mVideoSourceTimebase = timebase, true);
}
@ExecutedBy("mSequentialExecutor")
@@ -1140,7 +1150,7 @@
@ExecutedBy("mSequentialExecutor")
private void configureInternal(@NonNull SurfaceRequest surfaceRequest,
- @NonNull Timebase videoSourceTimebase) {
+ @NonNull Timebase videoSourceTimebase, boolean enableRetrySetupVideo) {
if (surfaceRequest.isServiced()) {
Logger.w(TAG, "Ignore the SurfaceRequest since it is already served.");
return;
@@ -1164,50 +1174,120 @@
+ "produce EncoderProfiles for advertised quality.");
}
}
- setupVideo(surfaceRequest, videoSourceTimebase);
+ if (mSetupVideoTask != null) {
+ mSetupVideoTask.cancel();
+ }
+ mSetupVideoTask = new SetupVideoTask(surfaceRequest, videoSourceTimebase,
+ enableRetrySetupVideo ? sRetrySetupVideoMaxCount : 0);
+ mSetupVideoTask.start();
}
- @SuppressWarnings("ObjectToString")
- @ExecutedBy("mSequentialExecutor")
- private void setupVideo(@NonNull SurfaceRequest request, @NonNull Timebase timebase) {
- safeToCloseVideoEncoder().addListener(() -> {
- if (request.isServiced()
- || (mVideoEncoderSession.isConfiguredSurfaceRequest(request)
- && !isPersistentRecordingInProgress())) {
- // Ignore the surface request if it's already serviced. Or the video encoder
- // session is already configured, unless there's a persistent recording is running.
- Logger.w(TAG, "Ignore the SurfaceRequest " + request + " isServiced: "
- + request.isServiced() + " VideoEncoderSession: " + mVideoEncoderSession
- + " has been configured with a persistent in-progress recording.");
+ /** A class for setting up video encoder with a retry mechanism. */
+ private class SetupVideoTask {
+ private final SurfaceRequest mSurfaceRequest;
+ private final Timebase mTimebase;
+ private final int mMaxRetryCount;
+
+ private boolean mIsComplete = false;
+ private int mRetryCount = 0;
+ @Nullable
+ private ScheduledFuture<?> mRetryFuture = null;
+
+ SetupVideoTask(@NonNull SurfaceRequest surfaceRequest, @NonNull Timebase timebase,
+ int maxRetryCount) {
+ mSurfaceRequest = surfaceRequest;
+ mTimebase = timebase;
+ mMaxRetryCount = maxRetryCount;
+ }
+
+ @ExecutedBy("mSequentialExecutor")
+ void start() {
+ if (mIsComplete) {
+ Logger.w(TAG, "Task has been completed before start");
return;
}
- VideoEncoderSession videoEncoderSession =
- new VideoEncoderSession(mVideoEncoderFactory, mSequentialExecutor, mExecutor);
- MediaSpec mediaSpec = getObservableData(mMediaSpec);
- ListenableFuture<Encoder> configureFuture =
- videoEncoderSession.configure(request, timebase, mediaSpec,
- mResolvedEncoderProfiles);
- mVideoEncoderSession = videoEncoderSession;
- Futures.addCallback(configureFuture, new FutureCallback<Encoder>() {
- @Override
- public void onSuccess(@Nullable Encoder result) {
- Logger.d(TAG, "VideoEncoder is created. " + result);
- if (result == null) {
- return;
- }
- Preconditions.checkState(mVideoEncoderSession == videoEncoderSession);
- Preconditions.checkState(mVideoEncoder == null);
- onVideoEncoderReady(videoEncoderSession);
- onConfigured();
- }
+ setupVideo(mSurfaceRequest, mTimebase);
+ }
- @Override
- public void onFailure(@NonNull Throwable t) {
- Logger.d(TAG, "VideoEncoder Setup error: " + t);
- onEncoderSetupError(t);
+ @ExecutedBy("mSequentialExecutor")
+ void cancel() {
+ if (mIsComplete) {
+ return;
+ }
+ mIsComplete = true;
+ if (mRetryFuture != null) {
+ mRetryFuture.cancel(false);
+ mRetryFuture = null;
+ }
+ }
+
+ @SuppressWarnings("ObjectToString")
+ @ExecutedBy("mSequentialExecutor")
+ private void setupVideo(@NonNull SurfaceRequest request, @NonNull Timebase timebase) {
+ safeToCloseVideoEncoder().addListener(() -> {
+ if (request.isServiced() || mIsComplete
+ || (mVideoEncoderSession.isConfiguredSurfaceRequest(request)
+ && !isPersistentRecordingInProgress())) {
+ // Ignore the surface request if it's already serviced. Or the video encoder
+ // session is already configured, unless there's a persistent recording is
+ // running. Or the task has been completed.
+ Logger.w(TAG, "Ignore the SurfaceRequest " + request + " isServiced: "
+ + request.isServiced() + " is setup video complete: " + mIsComplete
+ + " VideoEncoderSession: " + mVideoEncoderSession
+ + " has been configured with a persistent in-progress recording.");
+ mIsComplete = true;
+ return;
}
+ VideoEncoderSession videoEncoderSession =
+ new VideoEncoderSession(mVideoEncoderFactory, mSequentialExecutor,
+ mExecutor);
+ MediaSpec mediaSpec = getObservableData(mMediaSpec);
+ ListenableFuture<Encoder> configureFuture =
+ videoEncoderSession.configure(request, timebase, mediaSpec,
+ mResolvedEncoderProfiles);
+ mVideoEncoderSession = videoEncoderSession;
+ Futures.addCallback(configureFuture, new FutureCallback<Encoder>() {
+ @Override
+ public void onSuccess(@Nullable Encoder result) {
+ Logger.d(TAG, "VideoEncoder is created. " + result);
+ if (mIsComplete) {
+ if (result != null) {
+ result.release();
+ }
+ return;
+ }
+ mIsComplete = true;
+ if (result == null) {
+ return;
+ }
+ Preconditions.checkState(mVideoEncoderSession == videoEncoderSession);
+ Preconditions.checkState(mVideoEncoder == null);
+ onVideoEncoderReady(videoEncoderSession);
+ onConfigured();
+ }
+
+ @Override
+ public void onFailure(@NonNull Throwable t) {
+ Logger.w(TAG, "VideoEncoder Setup error: " + t, t);
+ if (mIsComplete) {
+ return;
+ }
+ if (mRetryCount < mMaxRetryCount) {
+ mRetryCount++;
+ mRetryFuture = scheduleTask(() -> {
+ if (!mIsComplete) {
+ Logger.d(TAG, "Retry setupVideo #" + mRetryCount);
+ setupVideo(mSurfaceRequest, mTimebase);
+ }
+ }, mSequentialExecutor, sRetrySetupVideoDelayMs, TimeUnit.MILLISECONDS);
+ } else {
+ mIsComplete = true;
+ onEncoderSetupError(t);
+ }
+ }
+ }, mSequentialExecutor);
}, mSequentialExecutor);
- }, mSequentialExecutor);
+ }
}
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@@ -2099,20 +2179,19 @@
// devices that require it and to act as a flag that we need to signal the source
// stopped.
Encoder finalVideoEncoder = mVideoEncoder;
- mSourceNonStreamingTimeout = CameraXExecutors.mainThreadExecutor().schedule(
- () -> mSequentialExecutor.execute(() -> {
- Logger.d(TAG, "The source didn't become non-streaming "
- + "before timeout. Waited " + SOURCE_NON_STREAMING_TIMEOUT_MS
- + "ms");
- if (DeviceQuirks.get(
- DeactivateEncoderSurfaceBeforeStopEncoderQuirk.class)
- != null) {
- // Even in the case of timeout, we tell the encoder the source has
- // stopped because devices with this quirk require that the codec
- // produce a new surface.
- notifyEncoderSourceStopped(finalVideoEncoder);
- }
- }), SOURCE_NON_STREAMING_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ mSourceNonStreamingTimeout = scheduleTask(() -> {
+ Logger.d(TAG, "The source didn't become non-streaming "
+ + "before timeout. Waited " + SOURCE_NON_STREAMING_TIMEOUT_MS
+ + "ms");
+ if (DeviceQuirks.get(
+ DeactivateEncoderSurfaceBeforeStopEncoderQuirk.class)
+ != null) {
+ // Even in the case of timeout, we tell the encoder the source has
+ // stopped because devices with this quirk require that the codec
+ // produce a new surface.
+ notifyEncoderSourceStopped(finalVideoEncoder);
+ }
+ }, mSequentialExecutor, SOURCE_NON_STREAMING_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} else {
// Source is already non-streaming. Signal source is stopped right away.
notifyEncoderSourceStopped(mVideoEncoder);
@@ -2225,12 +2304,16 @@
// If the latest surface request hasn't been serviced, use it to re-configure the Recorder.
if (shouldConfigure && mLatestSurfaceRequest != null
&& !mLatestSurfaceRequest.isServiced()) {
- configureInternal(mLatestSurfaceRequest, mVideoSourceTimebase);
+ configureInternal(mLatestSurfaceRequest, mVideoSourceTimebase, false);
}
}
@ExecutedBy("mSequentialExecutor")
private void resetVideo() {
+ if (mSetupVideoTask != null) {
+ mSetupVideoTask.cancel();
+ mSetupVideoTask = null;
+ }
if (mVideoEncoder != null) {
Logger.d(TAG, "Releasing video encoder.");
tryReleaseVideoEncoder();
@@ -2451,7 +2534,7 @@
// Perform required actions from state changes inline on sequential executor but unlocked.
if (needsConfigure) {
- configureInternal(mLatestSurfaceRequest, mVideoSourceTimebase);
+ configureInternal(mLatestSurfaceRequest, mVideoSourceTimebase, false);
} else if (needsReset) {
reset();
} else if (recordingToStart != null) {
@@ -2780,6 +2863,13 @@
mAudioState = audioState;
}
+ @NonNull
+ private static ScheduledFuture<?> scheduleTask(@NonNull Runnable task,
+ @NonNull Executor executor, long delay, TimeUnit timeUnit) {
+ return CameraXExecutors.mainThreadExecutor().schedule(() -> executor.execute(task), delay,
+ timeUnit);
+ }
+
private static int supportedMuxerFormatOrDefaultFrom(
@Nullable VideoValidatedEncoderProfilesProxy profilesProxy, int defaultMuxerFormat) {
if (profilesProxy != null) {
diff --git a/camera/camera-view/lint-baseline.xml b/camera/camera-view/lint-baseline.xml
index 6b45b0f..6acab2a 100644
--- a/camera/camera-view/lint-baseline.xml
+++ b/camera/camera-view/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -46,4 +46,256 @@
file="src/androidTest/java/androidx/camera/view/TextureViewImplementationTest.kt"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/video/AudioConfig.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/CameraController.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/CameraController.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/transform/CoordinateTransform.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/internal/compat/quirk/DeviceQuirks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/internal/compat/quirk/DeviceQuirksLoader.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/transform/FileTransformFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/FlashModeConverter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/ForwardingLiveData.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/transform/ImageProxyTransformFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/LifecycleCameraController.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/transform/OutputTransform.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/PendingValue.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/PreviewStreamStateObserver.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/PreviewTransformation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/PreviewView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/PreviewView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/PreviewView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/PreviewViewImplementation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/PreviewViewMeteringPointFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/ProcessCameraProviderWrapperImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/RotationProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/ScreenFlashView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/SurfaceViewImplementation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/SurfaceViewImplementation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/internal/compat/quirk/SurfaceViewNotCroppedByParentQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/internal/compat/quirk/SurfaceViewStretchedQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/view/TextureViewImplementation.java"/>
+ </issue>
+
</issues>
diff --git a/camera/camera-viewfinder-compose/api/current.txt b/camera/camera-viewfinder-compose/api/current.txt
index ad010a8..518164d 100644
--- a/camera/camera-viewfinder-compose/api/current.txt
+++ b/camera/camera-viewfinder-compose/api/current.txt
@@ -1,8 +1,28 @@
// Signature format: 4.0
package androidx.camera.viewfinder.compose {
+ @androidx.compose.runtime.Stable public interface CoordinateTransformer {
+ method public float[] getTransformMatrix();
+ method public long transform(long);
+ property public float[] transformMatrix;
+ }
+
+ public final class CoordinateTransformerKt {
+ method public static androidx.camera.viewfinder.compose.MutableCoordinateTransformer MutableCoordinateTransformer(optional float[] matrix);
+ }
+
+ public final class IdentityCoordinateTransformer implements androidx.camera.viewfinder.compose.CoordinateTransformer {
+ property public float[] transformMatrix;
+ field public static final androidx.camera.viewfinder.compose.IdentityCoordinateTransformer INSTANCE;
+ }
+
+ public interface MutableCoordinateTransformer extends androidx.camera.viewfinder.compose.CoordinateTransformer {
+ method public void setTransformMatrix(float[]);
+ property public float[] transformMatrix;
+ }
+
public final class ViewfinderKt {
- method @androidx.compose.runtime.Composable public static void Viewfinder(androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest surfaceRequest, androidx.camera.viewfinder.surface.ImplementationMode implementationMode, androidx.camera.viewfinder.surface.TransformationInfo transformationInfo, optional androidx.compose.ui.Modifier modifier);
+ method @androidx.compose.runtime.Composable public static void Viewfinder(androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest surfaceRequest, androidx.camera.viewfinder.surface.ImplementationMode implementationMode, androidx.camera.viewfinder.surface.TransformationInfo transformationInfo, optional androidx.compose.ui.Modifier modifier, optional androidx.camera.viewfinder.compose.MutableCoordinateTransformer? coordinateTransformer);
}
}
diff --git a/camera/camera-viewfinder-compose/api/restricted_current.txt b/camera/camera-viewfinder-compose/api/restricted_current.txt
index ad010a8..518164d 100644
--- a/camera/camera-viewfinder-compose/api/restricted_current.txt
+++ b/camera/camera-viewfinder-compose/api/restricted_current.txt
@@ -1,8 +1,28 @@
// Signature format: 4.0
package androidx.camera.viewfinder.compose {
+ @androidx.compose.runtime.Stable public interface CoordinateTransformer {
+ method public float[] getTransformMatrix();
+ method public long transform(long);
+ property public float[] transformMatrix;
+ }
+
+ public final class CoordinateTransformerKt {
+ method public static androidx.camera.viewfinder.compose.MutableCoordinateTransformer MutableCoordinateTransformer(optional float[] matrix);
+ }
+
+ public final class IdentityCoordinateTransformer implements androidx.camera.viewfinder.compose.CoordinateTransformer {
+ property public float[] transformMatrix;
+ field public static final androidx.camera.viewfinder.compose.IdentityCoordinateTransformer INSTANCE;
+ }
+
+ public interface MutableCoordinateTransformer extends androidx.camera.viewfinder.compose.CoordinateTransformer {
+ method public void setTransformMatrix(float[]);
+ property public float[] transformMatrix;
+ }
+
public final class ViewfinderKt {
- method @androidx.compose.runtime.Composable public static void Viewfinder(androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest surfaceRequest, androidx.camera.viewfinder.surface.ImplementationMode implementationMode, androidx.camera.viewfinder.surface.TransformationInfo transformationInfo, optional androidx.compose.ui.Modifier modifier);
+ method @androidx.compose.runtime.Composable public static void Viewfinder(androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest surfaceRequest, androidx.camera.viewfinder.surface.ImplementationMode implementationMode, androidx.camera.viewfinder.surface.TransformationInfo transformationInfo, optional androidx.compose.ui.Modifier modifier, optional androidx.camera.viewfinder.compose.MutableCoordinateTransformer? coordinateTransformer);
}
}
diff --git a/camera/camera-viewfinder-compose/build.gradle b/camera/camera-viewfinder-compose/build.gradle
index 91f4586..18fc9d7 100644
--- a/camera/camera-viewfinder-compose/build.gradle
+++ b/camera/camera-viewfinder-compose/build.gradle
@@ -51,7 +51,7 @@
androidx {
name = "androidx.camera:camera-viewfinder-compose"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2023"
description = "Composable ViewFinder implementation for CameraX"
metalavaK2UastEnabled = true
diff --git a/camera/camera-viewfinder-compose/src/androidTest/kotlin/androidx/camera/viewfinder/compose/CoordinateTransformerTest.kt b/camera/camera-viewfinder-compose/src/androidTest/kotlin/androidx/camera/viewfinder/compose/CoordinateTransformerTest.kt
new file mode 100644
index 0000000..68a9878
--- /dev/null
+++ b/camera/camera-viewfinder-compose/src/androidTest/kotlin/androidx/camera/viewfinder/compose/CoordinateTransformerTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 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.viewfinder.compose
+
+import androidx.compose.ui.geometry.Offset
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class CoordinateTransformerTest {
+
+ @Test
+ fun transform_withoutChangingMatrix_shouldReturnSameOffset() {
+ val transformer = MutableCoordinateTransformer()
+
+ val offset = Offset(10f, 10f)
+
+ with(transformer) {
+ val cameraSpaceOffset = offset.transform()
+ assertThat(cameraSpaceOffset).isEqualTo(offset)
+ }
+ }
+
+ @Test
+ fun transform_withMatrix() {
+ val transformer = MutableCoordinateTransformer()
+
+ val offset = Offset(10f, 10f)
+ transformer.transformMatrix.scale(2.0f, 2.0f)
+
+ with(transformer) {
+ val cameraSpaceOffset = offset.transform()
+ assertThat(cameraSpaceOffset).isEqualTo(Offset(20f, 20f))
+ }
+ }
+}
diff --git a/camera/camera-viewfinder-compose/src/androidTest/kotlin/androidx/camera/viewfinder/compose/ViewfinderTest.kt b/camera/camera-viewfinder-compose/src/androidTest/kotlin/androidx/camera/viewfinder/compose/ViewfinderTest.kt
index c799604..4e41191 100644
--- a/camera/camera-viewfinder-compose/src/androidTest/kotlin/androidx/camera/viewfinder/compose/ViewfinderTest.kt
+++ b/camera/camera-viewfinder-compose/src/androidTest/kotlin/androidx/camera/viewfinder/compose/ViewfinderTest.kt
@@ -26,6 +26,8 @@
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Matrix
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
@@ -57,6 +59,80 @@
assertCanRetrieveSurface(implementationMode = ImplementationMode.COMPATIBLE)
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+ @Test
+ fun coordinatesTransformationSameSizeNoRotation(): Unit = runBlocking {
+
+ val coordinateTransformer = MutableCoordinateTransformer()
+
+ val surfaceRequest = ViewfinderSurfaceRequest.Builder(TEST_RESOLUTION).build()
+ rule.setContent {
+ with(LocalDensity.current) {
+ Viewfinder(
+ modifier = Modifier.size(540.toDp(), 960.toDp()),
+ surfaceRequest = surfaceRequest,
+ transformationInfo = TEST_TRANSFORMATION_INFO,
+ implementationMode = ImplementationMode.PERFORMANCE,
+ coordinateTransformer = coordinateTransformer
+ )
+ }
+ }
+
+ val expectedMatrix = Matrix(
+ values = floatArrayOf(
+ 1f, 0f, 0f, 0f,
+ 0f, 1f, 0f, 0f,
+ 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f
+ )
+ )
+
+ assertThat(coordinateTransformer.transformMatrix.values)
+ .isEqualTo(expectedMatrix.values)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+ @Test
+ fun coordinatesTransformationSameSizeWithHalfCrop(): Unit = runBlocking {
+ // Viewfinder size: 1080x1920
+ // Surface size: 1080x1920
+ // Crop rect size: 540x960
+
+ val coordinateTransformer = MutableCoordinateTransformer()
+
+ val surfaceRequest = ViewfinderSurfaceRequest.Builder(TEST_RESOLUTION).build()
+ rule.setContent {
+ with(LocalDensity.current) {
+ Viewfinder(
+ modifier = Modifier.size(540.toDp(), 960.toDp()),
+ surfaceRequest = surfaceRequest,
+ transformationInfo = TransformationInfo(
+ sourceRotation = 0,
+ cropRectLeft = 0,
+ cropRectRight = 270,
+ cropRectTop = 0,
+ cropRectBottom = 480,
+ shouldMirror = false
+ ),
+ implementationMode = ImplementationMode.PERFORMANCE,
+ coordinateTransformer = coordinateTransformer
+ )
+ }
+ }
+
+ val expectedMatrix = Matrix(
+ values = floatArrayOf(
+ 0.5f, 0f, 0f, 0f,
+ 0f, 0.5f, 0f, 0f,
+ 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f
+ )
+ )
+
+ assertThat(coordinateTransformer.transformMatrix.values)
+ .isEqualTo(expectedMatrix.values)
+ }
+
@RequiresApi(Build.VERSION_CODES.M) // Needed for Surface.lockHardwareCanvas()
private suspend fun assertCanRetrieveSurface(implementationMode: ImplementationMode) {
val surfaceDeferred = CompletableDeferred<Surface>()
@@ -87,7 +163,7 @@
companion object {
val TEST_VIEWFINDER_SIZE = DpSize(360.dp, 640.dp)
- val TEST_RESOLUTION = Size(1080, 1920)
+ val TEST_RESOLUTION = Size(540, 960)
val TEST_TRANSFORMATION_INFO = TransformationInfo(
sourceRotation = 0,
cropRectLeft = 0,
diff --git a/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/CoordinateTransformer.kt b/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/CoordinateTransformer.kt
new file mode 100644
index 0000000..93d589d
--- /dev/null
+++ b/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/CoordinateTransformer.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 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.viewfinder.compose
+
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Matrix
+
+/**
+ * Coordinate transformer that's used to convert coordinates from one space to another.
+ *
+ * [transformMatrix] must be set by whoever manipulates the coordinate space, otherwise an identity
+ * will be used for the coordinate transformations. When used in a [Viewfinder], the viewfinder
+ * will set this transform matrix.
+ *
+ */
+@Stable
+interface CoordinateTransformer {
+ /**
+ * Matrix that's used for coordinate transformations.
+ */
+ val transformMatrix: Matrix
+
+ /**
+ * Returns the [Offset] in the transformed space.
+ */
+ fun Offset.transform() = transformMatrix.map(this)
+}
+
+/**
+ * CoordinateTransformer where the transformMatrix is mutable.
+ */
+interface MutableCoordinateTransformer : CoordinateTransformer {
+ override var transformMatrix: Matrix
+}
+
+/**
+ * Creates a [MutableCoordinateTransformer] with the given matrix as the transformMatrix.
+ */
+fun MutableCoordinateTransformer(matrix: Matrix = Matrix()): MutableCoordinateTransformer =
+ MutableCoordinateTransformerImpl(matrix)
+
+private class MutableCoordinateTransformerImpl(
+ initialMatrix: Matrix
+) : MutableCoordinateTransformer {
+ override var transformMatrix: Matrix by mutableStateOf(initialMatrix)
+}
+
+/**
+ * [CoordinateTransformer] where the transformMatrix is the identity matrix.
+ */
+object IdentityCoordinateTransformer : CoordinateTransformer {
+ override val transformMatrix = Matrix()
+
+ override fun Offset.transform() = this
+}
diff --git a/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/Viewfinder.kt b/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/Viewfinder.kt
index 266c716a..0d055c2 100644
--- a/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/Viewfinder.kt
+++ b/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/Viewfinder.kt
@@ -17,6 +17,7 @@
package androidx.camera.viewfinder.compose
import android.annotation.SuppressLint
+import android.graphics.RectF
import android.util.Size
import android.view.Surface
import androidx.camera.viewfinder.compose.internal.SurfaceTransformationUtil
@@ -55,6 +56,10 @@
* @param implementationMode Determines the underlying implementation of the [Surface].
* @param transformationInfo Specifies the required transformations for the media being displayed.
* @param modifier Modifier to be applied to the [Viewfinder]
+ * @param coordinateTransformer Coordinate transformer that can be used to convert Compose space
+ * coordinates such as touch coordinates to surface space coordinates.
+ * When the Viewfinder is displaying content from the camera, this transformer can be used to
+ * translate touch events into camera sensor coordinates for focus and metering actions.
*
* TODO(b/322420487): Add a sample with `@sample`
*/
@@ -63,7 +68,8 @@
surfaceRequest: ViewfinderSurfaceRequest,
implementationMode: ImplementationMode,
transformationInfo: TransformationInfo,
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
+ coordinateTransformer: MutableCoordinateTransformer? = null,
) {
val resolution = surfaceRequest.resolution
@@ -85,6 +91,7 @@
// TODO(b/322420176): Properly handle onSurfaceChanged()
},
+ coordinateTransformer,
)
}
}
@@ -107,6 +114,7 @@
transformationInfo: TransformationInfo,
implementationMode: ImplementationMode,
onInit: AndroidExternalSurfaceScope.() -> Unit,
+ coordinateTransformer: MutableCoordinateTransformer?,
) {
// For TextureView, correct the orientation to match the target rotation.
val correctionMatrix = Matrix()
@@ -134,13 +142,25 @@
val heightOffset = 0.coerceAtLeast((placeable.height - constraints.maxHeight) / 2)
layout(placeable.width, placeable.height) {
placeable.placeWithLayer(widthOffset, heightOffset) {
- val surfaceRectInViewfinder =
- SurfaceTransformationUtil.getTransformedSurfaceRect(
- resolution,
+ val surfaceToViewFinderMatrix =
+ SurfaceTransformationUtil.getTransformedSurfaceMatrix(
transformationInfo,
Size(constraints.maxWidth, constraints.maxHeight)
)
+ coordinateTransformer?.transformMatrix = Matrix().apply {
+ setFrom(surfaceToViewFinderMatrix)
+ invert()
+ }
+
+ val surfaceRectInViewfinder = RectF(
+ 0f,
+ 0f,
+ resolution.width.toFloat(),
+ resolution.height.toFloat()
+ )
+ surfaceToViewFinderMatrix.mapRect(surfaceRectInViewfinder)
+
transformOrigin = TransformOrigin(0f, 0f)
scaleX = surfaceRectInViewfinder.width() / resolution.width
scaleY = surfaceRectInViewfinder.height() / resolution.height
diff --git a/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/internal/SurfaceTransformationUtil.kt b/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/internal/SurfaceTransformationUtil.kt
index f2a118d..41134e2 100644
--- a/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/internal/SurfaceTransformationUtil.kt
+++ b/camera/camera-viewfinder-compose/src/main/java/androidx/camera/viewfinder/compose/internal/SurfaceTransformationUtil.kt
@@ -174,18 +174,16 @@
return matrix
}
- fun getTransformedSurfaceRect(
- resolution: Size,
+ fun getTransformedSurfaceMatrix(
transformationInfo: TransformationInfo,
viewfinderSize: Size,
- ): RectF {
+ ): Matrix {
val surfaceToViewFinder: Matrix =
getSurfaceToViewFinderMatrix(
viewfinderSize,
transformationInfo,
)
- val rect = RectF(0f, 0f, resolution.width.toFloat(), resolution.height.toFloat())
- surfaceToViewFinder.mapRect(rect)
- return rect
+
+ return surfaceToViewFinder
}
}
diff --git a/camera/camera-viewfinder-core/lint-baseline.xml b/camera/camera-viewfinder-core/lint-baseline.xml
new file mode 100644
index 0000000..b9f8498
--- /dev/null
+++ b/camera/camera-viewfinder-core/lint-baseline.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/surface/ViewfinderSurfaceRequestExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/core/ZoomGestureDetector.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/impl/package-info.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/impl/package-info.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/surface/package-info.java"/>
+ </issue>
+
+</issues>
diff --git a/camera/camera-viewfinder/lint-baseline.xml b/camera/camera-viewfinder/lint-baseline.xml
index 318cf78..deb32be 100644
--- a/camera/camera-viewfinder/lint-baseline.xml
+++ b/camera/camera-viewfinder/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -46,4 +46,211 @@
file="src/androidTest/java/androidx/camera/viewfinder/TextureViewImplementationTest.kt"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/utils/CameraOrientationUtil.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/utils/CameraThreads.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/CameraViewfinder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/CameraViewfinder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/CameraViewfinder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/CameraViewfinderExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/quirk/DeviceQuirks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/quirk/DeviceQuirksLoader.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/utils/Logger.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/quirk/Quirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/quirk/Quirks.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/transform/Rotation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/SurfaceViewImplementation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/SurfaceViewImplementation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/quirk/SurfaceViewNotCroppedByParentQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/quirk/SurfaceViewStretchedQuirk.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/TextureViewImplementation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/utils/Threads.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/internal/utils/TransformUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/ViewfinderImplementation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/ViewfinderSurfaceRequest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/ViewfinderSurfaceRequestExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/camera/viewfinder/ViewfinderTransformation.java"/>
+ </issue>
+
</issues>
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/ui/widget/Button.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/ui/widget/Button.kt
index ad474a8..cf4ffa0 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/ui/widget/Button.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/ui/widget/Button.kt
@@ -46,7 +46,7 @@
content: @Composable () -> Unit
) {
CompositionLocalProvider(
- LocalRippleConfiguration provides RippleConfiguration(isEnabled = enabled)
+ LocalRippleConfiguration provides if (enabled) RippleConfiguration() else null
) {
FloatingActionButton(
onClick = if (enabled) onClick else { {} },
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/CaptureOptionSubmissionTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/CaptureOptionSubmissionTest.kt
index 5c53be8..c4f2220 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/CaptureOptionSubmissionTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/CaptureOptionSubmissionTest.kt
@@ -200,9 +200,6 @@
listOf(
VideoCapture.Builder(Recorder.Builder().build())
.setTargetFrameRate(targetFpsRange),
- // TODO: b/333365764 - Remove extra Preview use case added to avoid capture
- // session failure due to MediaCodec error
- Preview.Builder()
)
)
diff --git a/camera/integration-tests/coretestapp/src/main/cpp/CMakeLists.txt b/camera/integration-tests/coretestapp/src/main/cpp/CMakeLists.txt
index 1f9ba23..680736f 100644
--- a/camera/integration-tests/coretestapp/src/main/cpp/CMakeLists.txt
+++ b/camera/integration-tests/coretestapp/src/main/cpp/CMakeLists.txt
@@ -14,7 +14,7 @@
# the License.
#
-cmake_minimum_required(VERSION 3.4.1)
+cmake_minimum_required(VERSION 3.22.1)
project(camera_test_app_jni)
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt
index 6bd0e668..a9963d2 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt
@@ -89,7 +89,6 @@
import androidx.concurrent.futures.CallbackToFutureAdapter
import androidx.concurrent.futures.CallbackToFutureAdapter.Completer
import androidx.core.util.Preconditions
-import androidx.lifecycle.lifecycleScope
import androidx.test.espresso.idling.CountingIdlingResource
import com.google.common.util.concurrent.ListenableFuture
import java.text.Format
@@ -99,11 +98,16 @@
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicLong
+import kotlin.coroutines.cancellation.CancellationException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.async
@@ -135,6 +139,8 @@
*/
private var cameraCaptureSession: Any? = null
+ private var captureSessionClosedDeferred: CompletableDeferred<Unit> = CompletableDeferred()
+
private var currentCameraId = "0"
private lateinit var backCameraId: String
@@ -206,7 +212,8 @@
}
override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture): Boolean {
- return true
+ // Will release the surface texture after the camera is closed
+ return false
}
override fun onSurfaceTextureUpdated(surfaceTexture: SurfaceTexture) {
@@ -267,10 +274,13 @@
private var restartOnStart = false
+ private val lock = Object()
private var activityStopped = false
private val cameraTaskDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
+ private val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
+
private var imageSaveTerminationFuture: ListenableFuture<Any?> = Futures.immediateFuture(null)
/**
@@ -449,7 +459,7 @@
val extensionMode = currentExtensionMode
restartPreview = true
- lifecycleScope.launch(cameraTaskDispatcher) {
+ coroutineScope.launch(cameraTaskDispatcher) {
extensionModeEnabled = !extensionModeEnabled
if (cameraCaptureSession == null) {
@@ -460,7 +470,7 @@
val extensionEnabled = extensionModeEnabled
- lifecycleScope.launch(Dispatchers.Main) {
+ coroutineScope.launch(Dispatchers.Main) {
setExtensionToggleButtonResource()
val newToast = if (extensionEnabled) {
@@ -580,7 +590,7 @@
val mode = if (isChecked) "Preview" else "Off"
videoStabilizationModeView.text = "Video Stabilization Mode: $mode"
- lifecycleScope.launch {
+ coroutineScope.launch {
suspendCancellableCoroutine<Any> { cont ->
setRepeatingRequestWhenCaptureSessionConfigured(cont, device, session)
}
@@ -648,7 +658,9 @@
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart()")
- activityStopped = false
+ synchronized(lock) {
+ activityStopped = false
+ }
if (restartOnStart) {
restartOnStart = false
setupAndStartPreview(currentCameraId, currentExtensionMode)
@@ -658,19 +670,29 @@
override fun onStop() {
Log.d(TAG, "onStop()++")
super.onStop()
+ synchronized(lock) {
+ activityStopped = true
+ }
closeCaptureSessionAndCameraAsync()
lastSurfaceTextureTimestampNanos = 0L
restartOnStart = true
- activityStopped = true
Log.d(TAG, "onStop()--")
}
override fun onDestroy() {
Log.d(TAG, "onDestroy()++")
super.onDestroy()
- previewSurface?.release()
- textureView.surfaceTexture?.release()
- imageSaveTerminationFuture.addListener({ stillImageReader?.close() }, mainExecutor)
+ Log.d(TAG, "Waiting for capture session closed...")
+ synchronized(lock) { captureSessionClosedDeferred }.asListenableFuture().addListener({
+ previewSurface?.release()
+ textureView.surfaceTexture?.release()
+ Log.d(TAG, "Surface texture released. $previewSurface")
+ imageSaveTerminationFuture.addListener({
+ stillImageReader?.close()
+ Log.d(TAG, "stillImageReader closed. ${stillImageReader?.surface}")
+ }, mainExecutor)
+ }, cameraTaskDispatcher.asExecutor())
+
normalModeCaptureThread.quitSafely()
streamConfigurationLatency[KEY_CAMERA2_LATENCY]?.also {
@@ -717,31 +739,44 @@
}
private fun closeCaptureSessionAndCameraAsync(keepCamera: Boolean = false): Deferred<Unit> =
- lifecycleScope.async(cameraTaskDispatcher) {
+ coroutineScope.async(cameraTaskDispatcher) {
Log.d(TAG, "closeCaptureSession()++")
resetCaptureSessionConfiguredIdlingResource()
+ val oldCaptureSessionClosedDeferred: CompletableDeferred<Unit>
+
+ synchronized(lock) {
+ oldCaptureSessionClosedDeferred = captureSessionClosedDeferred
+ captureSessionClosedDeferred = CompletableDeferred()
+ }
if (cameraCaptureSession != null) {
try {
if (cameraCaptureSession is CameraCaptureSession) {
(cameraCaptureSession as CameraCaptureSession).close()
+ Log.d(TAG, "closed CameraCaptureSession")
} else {
(cameraCaptureSession as CameraExtensionSession).close()
+ Log.d(TAG, "closed CameraExtensionSession")
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
}
+ } else {
+ captureSessionClosedDeferred.complete(Unit)
}
- cameraCaptureSession = null
+ captureSessionClosedDeferred.asListenableFuture().addListener({
+ if (!oldCaptureSessionClosedDeferred.isCompleted) {
+ oldCaptureSessionClosedDeferred.complete(Unit)
+ }
+ if (!keepCamera && synchronized(lock) { activityStopped }) {
+ Log.d(TAG, "Close camera++")
+ cameraDevice?.close()
+ cameraDevice = null
+ Log.d(TAG, "Close camera--")
+ }
+ }, cameraTaskDispatcher.asExecutor())
Log.d(TAG, "closeCaptureSession()--")
-
- if (!keepCamera) {
- Log.d(TAG, "Close camera++")
- cameraDevice?.close()
- cameraDevice = null
- Log.d(TAG, "Close camera--")
- }
}
/**
@@ -810,7 +845,7 @@
* Opens the camera and capture session to start the preview with the extension mode enabled.
*/
private fun startPreview(cameraId: String, extensionMode: Int) =
- lifecycleScope.launch(cameraTaskDispatcher) {
+ coroutineScope.launch(cameraTaskDispatcher) {
Log.d(TAG, "openCameraWithExtensionMode()++ cameraId: $cameraId")
if (cameraDevice == null || cameraDevice!!.id != cameraId) {
cameraDevice = openCamera(cameraManager, cameraId)
@@ -840,14 +875,16 @@
override fun onDisconnected(device: CameraDevice) {
Log.w(TAG, "Camera $cameraId has been disconnected")
// Rerun the flow to re-open the camera and capture session
- lifecycleScope.launch(Dispatchers.Main) {
- setupAndStartPreview(currentCameraId, currentExtensionMode)
+ coroutineScope.launch(Dispatchers.Main) {
+ if (!synchronized(lock) { activityStopped }) {
+ setupAndStartPreview(currentCameraId, currentExtensionMode)
+ }
}
}
override fun onClosed(camera: CameraDevice) {
Log.d(TAG, "Camera - onClosed: $cameraId")
- lifecycleScope.launch(Dispatchers.Main) {
+ coroutineScope.launch(Dispatchers.Main) {
if (restartCamera) {
restartCamera = false
updateExtensionInfo()
@@ -916,7 +953,9 @@
object : CameraCaptureSession.StateCallback() {
override fun onClosed(session: CameraCaptureSession) {
Log.d(TAG, "CaptureSession - onClosed: $session")
- lifecycleScope.launch(Dispatchers.Main) {
+ cameraCaptureSession = null
+ captureSessionClosedDeferred.complete(Unit)
+ coroutineScope.launch(Dispatchers.Main) {
if (restartPreview) {
restartPreviewWhenCaptureSessionClosed()
}
@@ -925,8 +964,14 @@
override fun onConfigured(session: CameraCaptureSession) {
Log.d(TAG, "CaptureSession - onConfigured: $session")
+ val isActivityStopped = synchronized(lock) { activityStopped }
+ if (isActivityStopped) {
+ Log.d(TAG, "activityStopped -> force close capture session")
+ session.close()
+ return
+ }
setRepeatingRequestWhenCaptureSessionConfigured(cont, session.device, session)
- lifecycleScope.launch(Dispatchers.Main) {
+ coroutineScope.launch(Dispatchers.Main) {
enableUiControl(true)
if (!captureSessionConfiguredIdlingResource.isIdleNow) {
captureSessionConfiguredIdlingResource.decrement()
@@ -936,7 +981,7 @@
override fun onConfigureFailed(session: CameraCaptureSession) {
Log.e(TAG, "CaptureSession - onConfigureFailed: $session")
- lifecycleScope.launch(Dispatchers.Main) {
+ coroutineScope.launch(Dispatchers.Main) {
setupAndStartPreview(currentCameraId, currentExtensionMode)
}
}
@@ -958,7 +1003,9 @@
cameraTaskDispatcher.asExecutor(), object : CameraExtensionSession.StateCallback() {
override fun onClosed(session: CameraExtensionSession) {
Log.d(TAG, "Extension CaptureSession - onClosed: $session")
- lifecycleScope.launch(Dispatchers.Main) {
+ cameraCaptureSession = null
+ captureSessionClosedDeferred.complete(Unit)
+ coroutineScope.launch(Dispatchers.Main) {
if (restartPreview) {
restartPreviewWhenCaptureSessionClosed()
}
@@ -967,6 +1014,12 @@
override fun onConfigured(session: CameraExtensionSession) {
Log.d(TAG, "Extension CaptureSession - onConfigured: $session")
+ val isActivityStopped = synchronized(lock) { activityStopped }
+ if (isActivityStopped) {
+ Log.d(TAG, "activityStopped -> force close capture session")
+ session.close()
+ return
+ }
setRepeatingRequestWhenCaptureSessionConfigured(cont, session.device, session)
runOnUiThread {
enableUiControl(true)
@@ -978,7 +1031,7 @@
override fun onConfigureFailed(session: CameraExtensionSession) {
Log.e(TAG, "Extension CaptureSession - onConfigureFailed: $session")
- lifecycleScope.launch(Dispatchers.Main) {
+ coroutineScope.launch(Dispatchers.Main) {
setupAndStartPreview(currentCameraId, currentExtensionMode)
}
}
@@ -1055,7 +1108,7 @@
updatePreviewSize(currentCameraId, newExtensionMode)
- lifecycleScope.launch(cameraTaskDispatcher) {
+ coroutineScope.launch(cameraTaskDispatcher) {
cameraCaptureSession =
openCaptureSession(newExtensionMode)
}
@@ -1075,7 +1128,7 @@
/**
* Takes a picture.
*/
- private fun takePicture() = lifecycleScope.launch(cameraTaskDispatcher) {
+ private fun takePicture() = coroutineScope.launch(cameraTaskDispatcher) {
Preconditions.checkState(
cameraCaptureSession != null,
"take picture button is only enabled when session is configured successfully"
@@ -1091,7 +1144,7 @@
stillImageReader!!.setOnImageAvailableListener(
{ reader: ImageReader ->
- lifecycleScope.launch(cameraTaskDispatcher) {
+ coroutineScope.launch(cameraTaskDispatcher) {
val (imageUri, rotationDegrees) = acquireImageAndSave(reader)
imageUri?.let { sessionImageUriSet.add(it) }
@@ -1219,7 +1272,7 @@
}
if (!isRequestMode) {
- lifecycleScope.launch(Dispatchers.Main) {
+ coroutineScope.launch(Dispatchers.Main) {
Toast.makeText(this@Camera2ExtensionsActivity, msg, Toast.LENGTH_SHORT)
.show()
}
@@ -1416,3 +1469,29 @@
}
fun Double.format(scale: Int): String = String.format("%.${scale}f", this)
+
+/**
+ * Convert a job into a ListenableFuture<T>.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+private fun <T> Deferred<T>.asListenableFuture(
+ tag: Any? = "Deferred.asListenableFuture"
+): ListenableFuture<T> {
+ val resolver: CallbackToFutureAdapter.Resolver<T> =
+ CallbackToFutureAdapter.Resolver<T> { completer ->
+ this.invokeOnCompletion {
+ if (it != null) {
+ if (it is CancellationException) {
+ completer.setCancelled()
+ } else {
+ completer.setException(it)
+ }
+ } else {
+ // Ignore exceptions - This should never throw in this situation.
+ completer.set(this.getCompleted())
+ }
+ }
+ tag
+ }
+ return CallbackToFutureAdapter.getFuture(resolver)
+}
diff --git a/camera/integration-tests/viewtestapp/lint-baseline.xml b/camera/integration-tests/viewtestapp/lint-baseline.xml
index 2f1ac7f..c6b452f 100644
--- a/camera/integration-tests/viewtestapp/lint-baseline.xml
+++ b/camera/integration-tests/viewtestapp/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="RestrictedApiAndroidX"
@@ -147,33 +147,6 @@
<issue
id="RestrictedApiAndroidX"
- message="CameraXExecutors.mainThreadExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
- errorLine1=" CameraXExecutors.mainThreadExecutor().execute {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/camera/integration/view/ComposeUiFragment.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="CameraSelector.getLensFacing can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
- errorLine1=" if (cameraController.cameraSelector.lensFacing == LENS_FACING_BACK) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/camera/integration/view/EffectsFragment.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="CameraSelector.getLensFacing can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
- errorLine1=" if (cameraController.cameraSelector.lensFacing == LENS_FACING_BACK) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/camera/integration/view/EffectsFragment.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
message="CameraXExecutors.directExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
errorLine1=" directExecutor(),"
errorLine2=" ~~~~~~~~~~~~~~">
diff --git a/car/app/app-samples/navigation/mobile/lint-baseline.xml b/car/app/app-samples/navigation/mobile/lint-baseline.xml
deleted file mode 100644
index 7d1e656..0000000
--- a/car/app/app-samples/navigation/mobile/lint-baseline.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
-
- <issue
- id="ForegroundServicePermission"
- message="foregroundServiceType:location requires permission:[android.permission.FOREGROUND_SERVICE_LOCATION] AND any permission in list:[android.permission.ACCESS_COARSE_LOCATION, android.permission.ACCESS_FINE_LOCATION]"
- errorLine1=" <service"
- errorLine2=" ^">
- <location
- file="src/main/AndroidManifest.xml"/>
- </issue>
-
-</issues>
diff --git a/car/app/app/gradle.properties b/car/app/app/gradle.properties
deleted file mode 100644
index a060082..0000000
--- a/car/app/app/gradle.properties
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Copyright 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-androidx.targetSdkVersion = 33
diff --git a/car/app/app/lint-baseline.xml b/car/app/app/lint-baseline.xml
index 3e44987..e10674d 100644
--- a/car/app/app/lint-baseline.xml
+++ b/car/app/app/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="MissingPermission"
@@ -362,12 +362,12 @@
</issue>
<issue
- id="PrivateConstructorForUtilityClass"
- message="Utility class is missing private constructor"
- errorLine1="class PersonsEqualityHelper {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ id="UnspecifiedRegisterReceiverFlag"
+ message="`mBroadcastReceiver` is missing `RECEIVER_EXPORTED` or `RECEIVER_NOT_EXPORTED` flag for unprotected broadcasts registered for androidx.car.app.connection.action.CAR_CONNECTION_UPDATED"
+ errorLine1=" mContext.registerReceiver(mBroadcastReceiver, filter);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="src/main/java/androidx/car/app/messaging/model/PersonsEqualityHelper.java"/>
+ file="src/main/java/androidx/car/app/connection/CarConnectionTypeLiveData.java"/>
</issue>
<issue
diff --git a/car/app/app/src/main/java/androidx/car/app/model/ItemList.java b/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
index 8092881..fbaa3a4 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/ItemList.java
@@ -16,6 +16,8 @@
package androidx.car.app.model;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
import static java.util.Objects.requireNonNull;
import android.annotation.SuppressLint;
@@ -24,6 +26,7 @@
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
import androidx.car.app.annotations.CarProtocol;
import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.annotations.KeepFields;
@@ -252,6 +255,15 @@
return this;
}
+ /** @see #setOnItemsVisibilityChangedListener(OnItemVisibilityChangedListener) */
+ @NonNull
+ @RestrictTo(LIBRARY)
+ public Builder setOnItemsVisibilityChangedDelegate(
+ @Nullable OnItemVisibilityChangedDelegate onItemVisibilityChangedDelegate) {
+ mOnItemVisibilityChangedDelegate = onItemVisibilityChangedDelegate;
+ return this;
+ }
+
/**
* Marks the list as selectable by setting the {@link OnSelectedListener} to call when an
* item is selected by the user, or set to {@code null} to mark the list as non-selectable.
@@ -276,6 +288,15 @@
return this;
}
+ /** @see #setOnSelectedListener(OnSelectedListener) */
+ @NonNull
+ @RestrictTo(LIBRARY)
+ public Builder setOnSelectedDelegate(@Nullable OnSelectedDelegate onSelectedDelegate) {
+ mOnSelectedDelegate = onSelectedDelegate;
+ return this;
+ }
+
+
/**
* Sets the index of the item to show as selected.
*
diff --git a/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java b/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java
index 68fbc6b..518d150 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java
@@ -45,6 +45,14 @@
@RunWith(RobolectricTestRunner.class)
@DoNotInstrument
public class ItemListTest {
+ private static final Row row1 = new Row.Builder().setTitle("Row1").build();
+ private static final Row row2 = new Row.Builder().setTitle("Row2").build();
+ private static final GridItem gridItem1 = new GridItem.Builder().setTitle("title 1").setImage(BACK).build();
+ private static final GridItem gridItem2 = new GridItem.Builder().setTitle("title 2").setImage(BACK).build();
+ private final OnSelectedListener mockSelectedListener = mock(OnSelectedListener.class);
+ private final OnItemVisibilityChangedListener mockItemVisibilityChangedListener = mock(OnItemVisibilityChangedListener.class);
+ OnDoneCallback mockOnDoneCallback = mock(OnDoneCallback.class);
+
@Test
public void createEmpty() {
ItemList list = new ItemList.Builder().build();
@@ -53,19 +61,13 @@
@Test
public void createRows() {
- Row row1 = new Row.Builder().setTitle("Row1").build();
- Row row2 = new Row.Builder().setTitle("Row2").build();
ItemList list = new ItemList.Builder().addItem(row1).addItem(row2).build();
- assertThat(list.getItems()).hasSize(2);
- assertThat(list.getItems().get(0)).isEqualTo(row1);
- assertThat(list.getItems().get(1)).isEqualTo(row2);
+ assertThat(list.getItems()).containsExactly(row1, row2).inOrder();
}
@Test
public void createGridItems() {
- GridItem gridItem1 = new GridItem.Builder().setTitle("title 1").setImage(BACK).build();
- GridItem gridItem2 = new GridItem.Builder().setTitle("title 2").setImage(BACK).build();
ItemList list = new ItemList.Builder().addItem(gridItem1).addItem(gridItem2).build();
assertThat(list.getItems()).containsExactly(gridItem1, gridItem2).inOrder();
@@ -73,54 +75,45 @@
@Test
public void clearItems() {
- Row row1 = new Row.Builder().setTitle("Row1").build();
- Row row2 = new Row.Builder().setTitle("Row2").build();
ItemList list = new ItemList.Builder()
.addItem(row1)
.clearItems()
.addItem(row2)
.build();
- assertThat(list.getItems()).hasSize(1);
- assertThat(list.getItems().get(0)).isEqualTo(row2);
+ assertThat(list.getItems()).containsExactly(row2).inOrder();
}
@Test
public void setSelectedable_emptyList_throws() {
assertThrows(
IllegalStateException.class,
- () -> new ItemList.Builder().setOnSelectedListener(selectedIndex -> {
- }).build());
+ () -> new ItemList.Builder().setOnSelectedListener(mockSelectedListener).build());
}
@Test
public void setSelectedIndex_greaterThanListSize_throws() {
- Row row1 = new Row.Builder().setTitle("Row1").build();
assertThrows(
IllegalStateException.class,
() -> new ItemList.Builder()
.addItem(row1)
- .setOnSelectedListener(selectedIndex -> {
- })
+ .setOnSelectedListener(mockSelectedListener)
.setSelectedIndex(2)
.build());
}
@Test
public void setSelectable() throws RemoteException {
- OnSelectedListener mockListener = mock(OnSelectedListener.class);
ItemList itemList =
new ItemList.Builder()
- .addItem(new Row.Builder().setTitle("title").build())
- .setOnSelectedListener(mockListener)
+ .addItem(row1)
+ .setOnSelectedListener(mockSelectedListener)
.build();
- OnDoneCallback onDoneCallback = mock(OnDoneCallback.class);
-
- itemList.getOnSelectedDelegate().sendSelected(0, onDoneCallback);
- verify(mockListener).onSelected(eq(0));
- verify(onDoneCallback).onSuccess(null);
+ itemList.getOnSelectedDelegate().sendSelected(0, mockOnDoneCallback);
+ verify(mockSelectedListener).onSelected(eq(0));
+ verify(mockOnDoneCallback).onSuccess(null);
}
@Test
@@ -130,15 +123,13 @@
() -> new ItemList.Builder()
.addItem(new Row.Builder().setTitle("foo").setOnClickListener(() -> {
}).build())
- .setOnSelectedListener((index) -> {
- })
+ .setOnSelectedListener(mockSelectedListener)
.build());
// Positive test.
new ItemList.Builder()
.addItem(new Row.Builder().setTitle("foo").build())
- .setOnSelectedListener((index) -> {
- })
+ .setOnSelectedListener(mockSelectedListener)
.build();
}
@@ -149,72 +140,65 @@
() -> new ItemList.Builder()
.addItem(new Row.Builder().setToggle(new Toggle.Builder(isChecked -> {
}).build()).build())
- .setOnSelectedListener((index) -> {
- })
+ .setOnSelectedListener(mockSelectedListener)
.build());
}
@Test
public void setOnItemVisibilityChangeListener_triggerListener() {
- OnItemVisibilityChangedListener listener = mock(OnItemVisibilityChangedListener.class);
ItemList list =
new ItemList.Builder()
- .addItem(new Row.Builder().setTitle("1").build())
- .setOnItemsVisibilityChangedListener(listener)
+ .addItem(row1)
+ .setOnItemsVisibilityChangedListener(mockItemVisibilityChangedListener)
.build();
- OnDoneCallback onDoneCallback = mock(OnDoneCallback.class);
list.getOnItemVisibilityChangedDelegate().sendItemVisibilityChanged(0, 1,
- onDoneCallback);
+ mockOnDoneCallback);
ArgumentCaptor<Integer> startIndexCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> endIndexCaptor = ArgumentCaptor.forClass(Integer.class);
- verify(listener).onItemVisibilityChanged(startIndexCaptor.capture(),
+ verify(mockItemVisibilityChangedListener).onItemVisibilityChanged(startIndexCaptor.capture(),
endIndexCaptor.capture());
- verify(onDoneCallback).onSuccess(null);
+ verify(mockOnDoneCallback).onSuccess(null);
assertThat(startIndexCaptor.getValue()).isEqualTo(0);
assertThat(endIndexCaptor.getValue()).isEqualTo(1);
}
@Test
public void setOnItemVisibilityChangeListener_triggerListenerWithFailure() {
- OnItemVisibilityChangedListener listener = mock(OnItemVisibilityChangedListener.class);
ItemList list =
new ItemList.Builder()
- .addItem(new Row.Builder().setTitle("1").build())
- .setOnItemsVisibilityChangedListener(listener)
+ .addItem(row1)
+ .setOnItemsVisibilityChangedListener(mockItemVisibilityChangedListener)
.build();
String testExceptionMessage = "Test exception";
- doThrow(new RuntimeException(testExceptionMessage)).when(listener).onItemVisibilityChanged(
+ doThrow(new RuntimeException(testExceptionMessage)).when(mockItemVisibilityChangedListener).onItemVisibilityChanged(
0, 1);
- OnDoneCallback onDoneCallback = mock(OnDoneCallback.class);
try {
list.getOnItemVisibilityChangedDelegate().sendItemVisibilityChanged(0, 1,
- onDoneCallback);
+ mockOnDoneCallback);
} catch (RuntimeException e) {
assertThat(e.getMessage()).contains(testExceptionMessage);
}
ArgumentCaptor<Integer> startIndexCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> endIndexCaptor = ArgumentCaptor.forClass(Integer.class);
- verify(listener).onItemVisibilityChanged(startIndexCaptor.capture(),
+ verify(mockItemVisibilityChangedListener).onItemVisibilityChanged(startIndexCaptor.capture(),
endIndexCaptor.capture());
- verify(onDoneCallback).onFailure(any());
+ verify(mockOnDoneCallback).onFailure(any());
assertThat(startIndexCaptor.getValue()).isEqualTo(0);
assertThat(endIndexCaptor.getValue()).isEqualTo(1);
}
@Test
public void equals_itemListWithRows() {
- assertThat(createFullyPopulatedRowItemList())
- .isEqualTo(createFullyPopulatedRowItemList());
+ assertThat(createFullyPopulatedRowItemList()).isEqualTo(createFullyPopulatedRowItemList());
}
@Test
public void equals_itemListWithGridItems() {
- assertThat(createFullyPopulatedGridItemList())
- .isEqualTo(createFullyPopulatedGridItemList());
+ assertThat(createFullyPopulatedGridItemList()).isEqualTo(createFullyPopulatedGridItemList());
}
@Test
@@ -225,48 +209,46 @@
@Test
public void notEquals_differentSelectedIndex() {
- Row row = new Row.Builder().setTitle("Title").build();
- ItemList itemList =
- new ItemList.Builder().setOnSelectedListener((index) -> {
- }).addItem(row).addItem(row).build();
+ ItemList itemList = new ItemList.Builder()
+ .setOnSelectedListener(mockSelectedListener)
+ .addItem(row1)
+ .addItem(row2)
+ .build();
assertThat(itemList)
.isNotEqualTo(
new ItemList.Builder()
- .setOnSelectedListener((index) -> {
- })
+ .setOnSelectedListener(mockSelectedListener)
.setSelectedIndex(1)
- .addItem(row)
- .addItem(row)
+ .addItem(row1)
+ .addItem(row2)
.build());
}
@Test
public void notEquals_missingSelectedListener() {
- Row row = new Row.Builder().setTitle("Title").build();
- ItemList itemList =
- new ItemList.Builder().setOnSelectedListener((index) -> {
- }).addItem(row).addItem(row).build();
- assertThat(itemList).isNotEqualTo(new ItemList.Builder().addItem(row).addItem(row).build());
+ ItemList itemList = new ItemList.Builder()
+ .setOnSelectedListener(mockSelectedListener)
+ .addItem(row1)
+ .addItem(row2)
+ .build();
+ assertThat(itemList).isNotEqualTo(new ItemList.Builder().addItem(row1).addItem(row2).build());
}
@Test
public void notEquals_missingVisibilityChangedListener() {
- Row row = new Row.Builder().setTitle("Title").build();
ItemList itemList =
new ItemList.Builder()
- .setOnItemsVisibilityChangedListener((start, end) -> {
- })
- .addItem(row)
- .addItem(row)
+ .setOnItemsVisibilityChangedListener(mockItemVisibilityChangedListener)
+ .addItem(row1)
+ .addItem(row2)
.build();
- assertThat(itemList).isNotEqualTo(new ItemList.Builder().addItem(row).addItem(row).build());
+ assertThat(itemList).isNotEqualTo(new ItemList.Builder().addItem(row1).addItem(row2).build());
}
@Test
public void notEquals_differentRows() {
- Row row = new Row.Builder().setTitle("Title").build();
- ItemList itemList = new ItemList.Builder().addItem(row).addItem(row).build();
- assertThat(itemList).isNotEqualTo(new ItemList.Builder().addItem(row).build());
+ ItemList itemList = new ItemList.Builder().addItem(row1).addItem(row1).build();
+ assertThat(itemList).isNotEqualTo(new ItemList.Builder().addItem(row1).build());
}
@Test
@@ -277,6 +259,20 @@
}
@Test
+ public void equals_delegateVsCallback() {
+ ItemList itemListWithListeners = new ItemList.Builder().addItem(row1)
+ .setOnItemsVisibilityChangedListener(mockItemVisibilityChangedListener)
+ .setOnSelectedListener(mockSelectedListener)
+ .build();
+ ItemList itemListWithDelegates = new ItemList.Builder().addItem(row1)
+ .setOnItemsVisibilityChangedDelegate(OnItemVisibilityChangedDelegateImpl.create(mockItemVisibilityChangedListener))
+ .setOnSelectedDelegate(OnSelectedDelegateImpl.create(mockSelectedListener))
+ .build();
+
+ assertThat(itemListWithListeners).isEqualTo(itemListWithDelegates);
+ }
+
+ @Test
public void toBuilder_createsEquivalentInstance_rows() {
ItemList itemList = createFullyPopulatedRowItemList();
@@ -294,51 +290,41 @@
public void toBuilder_fieldsCanBeOverwritten() {
Row row = new Row.Builder().setTitle("Title").build();
ItemList itemList = new ItemList.Builder()
- .setOnSelectedListener((index) -> {
- })
+ .setOnSelectedListener(mockSelectedListener)
.setNoItemsMessage("no items")
.setSelectedIndex(0)
- .setOnItemsVisibilityChangedListener((start, end) -> {
- })
+ .setOnItemsVisibilityChangedListener(mockItemVisibilityChangedListener)
.addItem(row)
.build();
// Verify fields can be overwritten (no crash)
itemList.toBuilder()
- .setOnSelectedListener((index) -> {
- })
+ .setOnSelectedListener(mockSelectedListener)
.setNoItemsMessage("no items")
.setSelectedIndex(0)
- .setOnItemsVisibilityChangedListener((start, end) -> {
- })
+ .setOnItemsVisibilityChangedListener(mockItemVisibilityChangedListener)
.clearItems()
.addItem(row)
.build();
}
- private static ItemList createFullyPopulatedRowItemList() {
- Row row = new Row.Builder().setTitle("Title").build();
+ private ItemList createFullyPopulatedRowItemList() {
return new ItemList.Builder()
- .setOnSelectedListener((index) -> {
- })
+ .setOnSelectedListener(mockSelectedListener)
.setNoItemsMessage("no items")
.setSelectedIndex(0)
- .setOnItemsVisibilityChangedListener((start, end) -> {
- })
- .addItem(row)
+ .setOnItemsVisibilityChangedListener(mockItemVisibilityChangedListener)
+ .addItem(row1)
.build();
}
- private static ItemList createFullyPopulatedGridItemList() {
- GridItem gridItem = new GridItem.Builder().setImage(BACK).setTitle("Title").build();
+ private ItemList createFullyPopulatedGridItemList() {
return new ItemList.Builder()
- .setOnSelectedListener((index) -> {
- })
+ .setOnSelectedListener(mockSelectedListener)
.setNoItemsMessage("no items")
.setSelectedIndex(0)
- .setOnItemsVisibilityChangedListener((start, end) -> {
- })
- .addItem(gridItem)
+ .setOnItemsVisibilityChangedListener(mockItemVisibilityChangedListener)
+ .addItem(gridItem1)
.build();
}
}
diff --git a/cardview/cardview/lint-baseline.xml b/cardview/cardview/lint-baseline.xml
new file mode 100644
index 0000000..a71b389
--- /dev/null
+++ b/cardview/cardview/lint-baseline.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/cardview/widget/CardView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/cardview/widget/CardViewApi21Impl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/cardview/widget/RoundRectDrawable.java"/>
+ </issue>
+
+</issues>
diff --git a/collection/collection-ktx/build.gradle b/collection/collection-ktx/build.gradle
index 1cdeaee..7bdd853 100644
--- a/collection/collection-ktx/build.gradle
+++ b/collection/collection-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -34,7 +34,7 @@
androidx {
name = "Collections Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'collection' artifact"
metalavaK2UastEnabled = true
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LruCache.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LruCache.kt
index 689e5c3..2632e31 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LruCache.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LruCache.kt
@@ -202,6 +202,8 @@
*
* @param evicted `true` if the entry is being removed to make space, `false`
* if the removal was caused by a [put] or [remove].
+ * @param key key of the entry that was evicted or removed.
+ * @param oldValue the original value of the entry that was evicted removed.
* @param newValue the new value for [key], if it exists. If non-null, this removal was caused
* by a [put]. Otherwise it was caused by an eviction or a [remove].
*/
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index d13c916..1258389 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -122,7 +122,7 @@
androidx {
name = "Compose Animation Core"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Animation engine and animation primitives that are the building blocks of the Compose animation library"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt
index 0f8a79b..5d7d570 100644
--- a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt
+++ b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt
@@ -167,4 +167,20 @@
previousFraction = newFraction
}
}
+
+ @Test
+ fun pathEasing_Overshoots() {
+ val path = Path().apply {
+ cubicTo(0.34f, 1.56f, 0.64f, 1.0f, 1.0f, 1.0f)
+ }
+ assertThat(PathEasing(path).transform(0.6f)).isGreaterThan(1.0f)
+ }
+
+ @Test
+ fun pathEasing_Undershoots() {
+ val path = Path().apply {
+ cubicTo(0.68f, -0.6f, 0.32f, 1.6f, 1.0f, 1.0f)
+ }
+ assertThat(PathEasing(path).transform(0.1f)).isLessThan(0.0f)
+ }
}
diff --git a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt
index f2101f2..d5992f9 100644
--- a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt
+++ b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt
@@ -51,6 +51,7 @@
import androidx.test.filters.SdkSuppress
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.android.awaitFrame
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -2390,4 +2391,81 @@
}
}
}
+
+ @Test
+ fun isRunningDuringAnimateTo() {
+ val seekableTransitionState = SeekableTransitionState(AnimStates.From)
+ lateinit var transition: Transition<AnimStates>
+ var animatedValue by mutableIntStateOf(-1)
+
+ rule.mainClock.autoAdvance = false
+
+ rule.setContent {
+ LaunchedEffect(seekableTransitionState) {
+ seekableTransitionState.animateTo(AnimStates.To)
+ }
+ transition = rememberTransition(seekableTransitionState, label = "Test")
+ animatedValue = transition.animateInt(
+ label = "Value",
+ transitionSpec = { tween(easing = LinearEasing) }
+ ) { state ->
+ when (state) {
+ AnimStates.From -> 0
+ else -> 1000
+ }
+ }.value
+ }
+ rule.runOnIdle {
+ assertEquals(0, animatedValue)
+ assertFalse(transition.isRunning)
+ }
+ rule.mainClock.advanceTimeByFrame() // wait for composition after animateTo()
+ rule.mainClock.advanceTimeByFrame() // one frame to set the start time
+ rule.runOnIdle {
+ assertTrue(animatedValue > 0)
+ assertTrue(transition.isRunning)
+ }
+ rule.mainClock.advanceTimeBy(5000)
+ rule.runOnIdle {
+ assertEquals(1000, animatedValue)
+ assertFalse(transition.isRunning)
+ }
+ }
+
+ @Test
+ fun isRunningFalseAfterSnapTo() {
+ val seekableTransitionState = SeekableTransitionState(AnimStates.From)
+ lateinit var transition: Transition<AnimStates>
+ var animatedValue by mutableIntStateOf(-1)
+
+ rule.mainClock.autoAdvance = false
+
+ rule.setContent {
+ LaunchedEffect(seekableTransitionState) {
+ awaitFrame() // Not sure why this is needed. Animated val doesn't change without it.
+ seekableTransitionState.snapTo(AnimStates.To)
+ }
+ transition = rememberTransition(seekableTransitionState, label = "Test")
+ animatedValue = transition.animateInt(
+ label = "Value",
+ transitionSpec = { tween(easing = LinearEasing) }
+ ) { state ->
+ when (state) {
+ AnimStates.From -> 0
+ else -> 1000
+ }
+ }.value
+ }
+ rule.runOnIdle {
+ assertEquals(0, animatedValue)
+ assertFalse(transition.isRunning)
+ }
+ rule.mainClock.advanceTimeByFrame() // wait for composition after animateTo()
+ rule.mainClock.advanceTimeByFrame() // one frame to snap
+ rule.mainClock.advanceTimeByFrame() // one frame for LaunchedEffect's awaitFrame()
+ rule.runOnIdle {
+ assertEquals(1000, animatedValue)
+ assertFalse(transition.isRunning)
+ }
+ }
}
diff --git a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/AnimatableTest.kt b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/AnimatableTest.kt
index 051d544..39555aa 100644
--- a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/AnimatableTest.kt
+++ b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/AnimatableTest.kt
@@ -21,6 +21,7 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.IntSize
import com.google.common.truth.Truth.assertThat
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertFalse
@@ -309,6 +310,48 @@
}
@Test
+ fun testIntSize_alwaysWithinValidBounds() {
+ val animatable = Animatable(
+ initialValue = IntSize(10, 10),
+ typeConverter = IntSize.VectorConverter,
+ visibilityThreshold = IntSize.VisibilityThreshold
+ )
+
+ val values = mutableListOf<IntSize>()
+
+ runBlocking {
+ val clock = SuspendAnimationTest.TestFrameClock()
+
+ // Add frames to evaluate at
+ clock.frame(0L)
+ clock.frame(25L * 1_000_000L)
+ clock.frame(75L * 1_000_000L)
+ clock.frame(100L * 1_000_000L)
+
+ withContext(clock) {
+ // Animate linearly from -100 to 100
+ animatable.animateTo(
+ IntSize(100, 100),
+ keyframes {
+ durationMillis = 100
+ IntSize(-100, -100) at 0 using LinearEasing
+ }
+ ) {
+ values.add(value)
+ }
+ }
+ }
+
+ // The internal animation is expected to be: -100, -50, 50, 100. But for IntSize, we don't
+ // support negative values, so it's clamped to Zero
+ assertEquals(4, values.size)
+ assertEquals(IntSize.Zero, values[0])
+ assertEquals(IntSize.Zero, values[1])
+ assertEquals(IntSize(50, 50), values[2])
+ assertEquals(IntSize(100, 100), values[3])
+ }
+
+ @Test
fun animationResult_toString() {
val animatable = AnimationResult(
endReason = AnimationEndReason.Finished,
diff --git a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.kt b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.kt
index eb5c46e..beea6dbd 100644
--- a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.kt
+++ b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.kt
@@ -28,8 +28,8 @@
@RunWith(JUnit4::class)
class EasingTest {
- val ZeroEpsilon = -(1.0f.ulp)
- val OneEpsilon = 1.0f + 1.0f.ulp
+ private val ZeroEpsilon = -(1.0f.ulp)
+ private val OneEpsilon = 1.0f + 1.0f.ulp
@Test
fun cubicBezierStartsAt0() {
@@ -40,7 +40,19 @@
@Test
fun cubicBezierEndsAt1() {
val easing = FastOutLinearInEasing
- assertThat(easing.transform(1f) == 1f)
+ assertThat(easing.transform(1f)).isEqualTo(1.0f)
+ }
+
+ @Test
+ fun cubicBezierDoesntExceed1() {
+ val easing = CubicBezierEasing(0f, 0f, 0.15f, 1f)
+ assertThat(easing.transform(0.999999f) <= 1.0f).isTrue()
+ }
+
+ @Test
+ fun cubicBezierDoesExceed1() {
+ val easing = CubicBezierEasing(0.34f, 1.56f, 0.64f, 1.0f)
+ assertThat(easing.transform(0.6f)).isGreaterThan(1.0f)
}
@Test
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
index 51dbbbf..f69cb99 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
@@ -18,8 +18,10 @@
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
+import androidx.compose.ui.graphics.computeCubicVerticalBounds
import androidx.compose.ui.graphics.evaluateCubic
import androidx.compose.ui.graphics.findFirstCubicRoot
+import androidx.compose.ui.util.fastCoerceIn
/**
* Easing is a way to adjust an animation’s fraction. Easing allows transitioning
@@ -106,10 +108,17 @@
private val c: Float,
private val d: Float
) : Easing {
+ private val min: Float
+ private val max: Float
+
init {
requirePrecondition(!a.isNaN() && !b.isNaN() && !c.isNaN() && !d.isNaN()) {
"Parameters to CubicBezierEasing cannot be NaN. Actual parameters are: $a, $b, $c, $d."
}
+ val roots = FloatArray(5)
+ val extrema = computeCubicVerticalBounds(0.0f, b, d, 1.0f, roots, 0)
+ min = extrema.first
+ max = extrema.second
}
/**
@@ -131,20 +140,24 @@
// No root, the cubic curve has no solution
if (t.isNaN()) {
- throw IllegalArgumentException(
- "The cubic curve with parameters ($a, $b, $c, $d) has no solution at $fraction"
- )
+ throwNoSolution(fraction)
}
// Don't clamp the values since the curve might be used to over- or under-shoot
// The test above that checks if fraction is in ]0..1[ will ensure we start and
// end at 0 and 1 respectively
- evaluateCubic(b, d, t)
+ evaluateCubic(b, d, t).fastCoerceIn(min, max)
} else {
fraction
}
}
+ private fun throwNoSolution(fraction: Float) {
+ throw IllegalArgumentException(
+ "The cubic curve with parameters ($a, $b, $c, $d) has no solution at $fraction"
+ )
+ }
+
override fun equals(other: Any?): Boolean {
return other is CubicBezierEasing && a == other.a && b == other.b && c == other.c &&
d == other.d
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
index 43446776..9aac1bb 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
@@ -24,7 +24,6 @@
import androidx.compose.ui.graphics.computeHorizontalBounds
import androidx.compose.ui.graphics.evaluateY
import androidx.compose.ui.graphics.findFirstRoot
-import androidx.compose.ui.util.fastCoerceIn
/**
* An easing function for an arbitrary [Path].
@@ -66,39 +65,7 @@
}
if (!::intervals.isInitialized) {
- val roots = FloatArray(5)
-
- // Using an interval tree is a bit heavy handed but since we are dealing with
- // easing curves, we don't expect many segments, and therefore few allocations.
- // The interval tree allows us to quickly query for the correct segment inside
- // the transform() function.
- val segmentIntervals = IntervalTree<PathSegment>().apply {
- // A path easing curve is defined in the domain 0..1, use an error
- // appropriate for this domain (the default is 0.25). Conic segments
- // should be unlikely in path easing curves, but just in case...
- val iterator = path.iterator(
- PathIterator.ConicEvaluation.AsQuadratics,
- 2e-4f
- )
- while (iterator.hasNext()) {
- val segment = iterator.next()
- requirePrecondition(segment.type != PathSegment.Type.Close) {
- "The path cannot contain a close() command."
- }
- if (segment.type != PathSegment.Type.Move &&
- segment.type != PathSegment.Type.Done
- ) {
- val bounds = computeHorizontalBounds(segment, roots)
- addInterval(bounds.first, bounds.second, segment)
- }
- }
- }
-
- requirePrecondition(0.0f in segmentIntervals && 1.0f in segmentIntervals) {
- "The easing path must start at 0.0f and end at 1.0f."
- }
-
- intervals = segmentIntervals
+ initializeEasing()
}
val result = intervals.findFirstOverlap(fraction)
@@ -111,6 +78,42 @@
"The easing path is invalid. Make sure it does not contain NaN/Infinity values."
}
- return evaluateY(segment, t).fastCoerceIn(0.0f, 1.0f)
+ return evaluateY(segment, t)
+ }
+
+ private fun initializeEasing() {
+ val roots = FloatArray(5)
+
+ // Using an interval tree is a bit heavy handed but since we are dealing with
+ // easing curves, we don't expect many segments, and therefore few allocations.
+ // The interval tree allows us to quickly query for the correct segment inside
+ // the transform() function.
+ val segmentIntervals = IntervalTree<PathSegment>().apply {
+ // A path easing curve is defined in the domain 0..1, use an error
+ // appropriate for this domain (the default is 0.25). Conic segments
+ // should be unlikely in path easing curves, but just in case...
+ val iterator = path.iterator(
+ PathIterator.ConicEvaluation.AsQuadratics,
+ 2e-4f
+ )
+ while (iterator.hasNext()) {
+ val segment = iterator.next()
+ requirePrecondition(segment.type != PathSegment.Type.Close) {
+ "The path cannot contain a close() command."
+ }
+ if (segment.type != PathSegment.Type.Move &&
+ segment.type != PathSegment.Type.Done
+ ) {
+ val bounds = computeHorizontalBounds(segment, roots)
+ addInterval(bounds.first, bounds.second, segment)
+ }
+ }
+ }
+
+ requirePrecondition(0.0f in segmentIntervals && 1.0f in segmentIntervals) {
+ "The easing path must start at 0.0f and end at 1.0f."
+ }
+
+ intervals = segmentIntervals
}
}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index 111f3e9..ba2fc7d 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -206,6 +206,12 @@
it.onTotalDurationChanged()
}
+private val SeekableStateObserver: SnapshotStateObserver by lazy(LazyThreadSafetyMode.NONE) {
+ SnapshotStateObserver { it() }.apply {
+ start()
+ }
+}
+
/**
* A [TransitionState] that can manipulate the progress of the [Transition] by seeking
* with [seekTo] or animating with [animateTo].
@@ -234,8 +240,6 @@
*/
private var transition: Transition<S>? = null
- private val observer = SnapshotStateObserver { it() }
-
// Used for seekToFraction calculations to avoid allocation
internal var totalDurationNanos = 0L
@@ -471,6 +475,7 @@
// the correct animation values
waitForCompositionAfterTargetStateChange()
}
+ transition.onTransitionEnd()
}
}
@@ -685,6 +690,7 @@
currentState = targetState
waitForComposition()
fraction = 0f
+ transition.onTransitionEnd()
}
}
}
@@ -695,21 +701,16 @@
"An instance of SeekableTransitionState has been used in different Transitions. " +
"Previous instance: ${this.transition}, new instance: $transition"
}
- if (this.transition == null) {
- observer.start()
- }
this.transition = transition
}
override fun transitionRemoved() {
- if (this.transition != null) {
- observer.stop()
- this.transition = null
- }
+ this.transition = null
+ SeekableStateObserver.clear(this)
}
internal fun observeTotalDuration() {
- observer.observeReads(
+ SeekableStateObserver.observeReads(
scope = this,
onValueChangedForScope = SeekableTransitionStateTotalDurationChanged,
block = recalculateTotalDurationNanos
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt
index 6455afd..d197dc0 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt
@@ -124,6 +124,8 @@
/**
* A type converter that converts a [IntSize] to a [AnimationVector2D], and vice versa.
+ *
+ * Clamps negative values to zero when converting back to [IntSize].
*/
val IntSize.Companion.VectorConverter: TwoWayConverter<IntSize, AnimationVector2D>
get() = IntSizeToVector
@@ -174,11 +176,18 @@
/**
* A type converter that converts a [IntSize] to a [AnimationVector2D], and vice versa.
+ *
+ * Clamps negative values to zero when converting back to [IntSize].
*/
private val IntSizeToVector: TwoWayConverter<IntSize, AnimationVector2D> =
TwoWayConverter(
{ AnimationVector2D(it.width.toFloat(), it.height.toFloat()) },
- { IntSize(it.v1.fastRoundToInt(), it.v2.fastRoundToInt()) }
+ {
+ IntSize(
+ width = it.v1.fastRoundToInt().coerceAtLeast(0),
+ height = it.v2.fastRoundToInt().coerceAtLeast(0)
+ )
+ }
)
/**
diff --git a/compose/animation/animation-graphics/build.gradle b/compose/animation/animation-graphics/build.gradle
index 15c9724..7a464f2 100644
--- a/compose/animation/animation-graphics/build.gradle
+++ b/compose/animation/animation-graphics/build.gradle
@@ -119,7 +119,7 @@
androidx {
name = "Compose Animation Graphics"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "Compose Animation Graphics Library for using animated-vector resources in Compose"
samples(project(":compose:animation:animation-graphics:animation-graphics-samples"))
diff --git a/compose/animation/animation-tooling-internal/build.gradle b/compose/animation/animation-tooling-internal/build.gradle
index dc22417..a5f56b2 100644
--- a/compose/animation/animation-tooling-internal/build.gradle
+++ b/compose/animation/animation-tooling-internal/build.gradle
@@ -21,9 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import androidx.build.RunApiTasks
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -37,8 +35,7 @@
androidx {
name = "Compose Animation Tooling"
description = "Compose Animation APIs for tooling support. Internal use only."
- publish = Publish.SNAPSHOT_AND_RELEASE
- runApiTasks = new RunApiTasks.Yes()
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
metalavaK2UastEnabled = true
doNotDocumentReason = "Only used externally by Android Studio"
}
diff --git a/compose/animation/animation/api/current.txt b/compose/animation/animation/api/current.txt
index 2411661..39cb35b 100644
--- a/compose/animation/animation/api/current.txt
+++ b/compose/animation/animation/api/current.txt
@@ -46,12 +46,11 @@
public final class AnimatedVisibilityKt {
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static <T> void AnimatedVisibility(androidx.compose.animation.core.Transition<T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static <T> void AnimatedVisibility(androidx.compose.animation.core.Transition<T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
- method @Deprecated @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, androidx.compose.animation.EnterTransition enter, androidx.compose.animation.ExitTransition exit, boolean initiallyVisible, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
}
@@ -93,7 +92,7 @@
method @Deprecated @androidx.compose.runtime.Composable public static <T> void Crossfade(T targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi public enum EnterExitState {
+ public enum EnterExitState {
enum_constant public static final androidx.compose.animation.EnterExitState PostExit;
enum_constant public static final androidx.compose.animation.EnterExitState PreEnter;
enum_constant public static final androidx.compose.animation.EnterExitState Visible;
diff --git a/compose/animation/animation/api/restricted_current.txt b/compose/animation/animation/api/restricted_current.txt
index 2411661..39cb35b 100644
--- a/compose/animation/animation/api/restricted_current.txt
+++ b/compose/animation/animation/api/restricted_current.txt
@@ -46,12 +46,11 @@
public final class AnimatedVisibilityKt {
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static <T> void AnimatedVisibility(androidx.compose.animation.core.Transition<T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static <T> void AnimatedVisibility(androidx.compose.animation.core.Transition<T>, kotlin.jvm.functions.Function1<? super T,java.lang.Boolean> visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.ColumnScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, androidx.compose.animation.core.MutableTransitionState<java.lang.Boolean> visibleState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(androidx.compose.foundation.layout.RowScope, boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
- method @Deprecated @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, androidx.compose.animation.EnterTransition enter, androidx.compose.animation.ExitTransition exit, boolean initiallyVisible, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void AnimatedVisibility(boolean visible, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enter, optional androidx.compose.animation.ExitTransition exit, optional String label, kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedVisibilityScope,kotlin.Unit> content);
}
@@ -93,7 +92,7 @@
method @Deprecated @androidx.compose.runtime.Composable public static <T> void Crossfade(T targetState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi public enum EnterExitState {
+ public enum EnterExitState {
enum_constant public static final androidx.compose.animation.EnterExitState PostExit;
enum_constant public static final androidx.compose.animation.EnterExitState PreEnter;
enum_constant public static final androidx.compose.animation.EnterExitState Visible;
diff --git a/compose/animation/animation/build.gradle b/compose/animation/animation/build.gradle
index cff8085..c4a6998 100644
--- a/compose/animation/animation/build.gradle
+++ b/compose/animation/animation/build.gradle
@@ -122,7 +122,7 @@
androidx {
name = "Compose Animation"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Compose animation library"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/sharedelement/ContainerTransformDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/sharedelement/ContainerTransformDemo.kt
index a0f83f8..9f41de50 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/sharedelement/ContainerTransformDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/sharedelement/ContainerTransformDemo.kt
@@ -18,6 +18,7 @@
package androidx.compose.animation.demos.sharedelement
+import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.ExperimentalSharedTransitionApi
@@ -34,6 +35,8 @@
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -55,7 +58,6 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Favorite
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -70,22 +72,14 @@
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import kotlinx.coroutines.delay
@Preview
@Composable
fun ContainerTransformDemo(model: MyModel = remember { MyModel().apply { selected = items[1] } }) {
+ BackHandler {
+ model.selected = null
+ }
SharedTransitionLayout {
- LaunchedEffect(key1 = Unit) {
- while (true) {
- delay(2500)
- if (model.selected == null) {
- model.selected = model.items[1]
- } else {
- model.selected = null
- }
- }
- }
AnimatedContent(
model.selected,
transitionSpec = {
@@ -213,14 +207,22 @@
) {
Column(
Modifier
+ .clickable(
+ interactionSource = remember {
+ MutableInteractionSource()
+ },
+ indication = null
+ ) {
+ model.selected = null
+ }
.sharedBounds(
rememberSharedContentState(
key = "container + ${selected.id}"
),
this@AnimatedVisibilityScope,
- scaleInSharedContentToBounds(contentScale = ContentScale.Crop),
- scaleOutSharedContentToBounds(contentScale = ContentScale.Crop),
- clipInOverlayDuringTransition = OverlayClip(RoundedCornerShape(20.dp))
+ scaleInSharedContentToBounds(contentScale = ContentScale.Crop) + fadeIn(),
+ scaleOutSharedContentToBounds(contentScale = ContentScale.Crop) + fadeOut(),
+ clipInOverlayDuringTransition = OverlayClip(RoundedCornerShape(20.dp)),
)
) {
Row(Modifier.fillMaxHeight(0.5f)) {
@@ -274,7 +276,11 @@
contentPadding = PaddingValues(top = 90.dp)
) {
items(6) {
- KittyItem(model.items[it])
+ Box(modifier = Modifier.clickable {
+ model.selected = model.items[it]
+ }) {
+ KittyItem(model.items[it])
+ }
}
}
}
@@ -286,8 +292,8 @@
Kitty("油条", R.drawable.yt_profile, "Tabby", 1),
Kitty("Cowboy", R.drawable.cowboy, "American Short Hair", 2),
Kitty("Pepper", R.drawable.pepper, "Tabby", 3),
- Kitty("Unknown", R.drawable.question_mark, "Unknown", 4),
- Kitty("Unknown", R.drawable.question_mark, "Unknown", 5),
+ Kitty("Unknown", R.drawable.question_mark, "Unknown Breed", 4),
+ Kitty("Unknown", R.drawable.question_mark, "Unknown Breed", 5),
Kitty("YT", R.drawable.yt_profile2, "Tabby", 6),
)
var selected: Kitty? by mutableStateOf(null)
@@ -301,7 +307,7 @@
.padding(start = 10.dp, end = 10.dp, bottom = 10.dp)
.sharedBounds(
rememberSharedContentState(key = "container + ${kitty.id}"),
- this@AnimatedVisibilityScope
+ this@AnimatedVisibilityScope,
)
.background(Color.White, RoundedCornerShape(20.dp))
) {
@@ -317,7 +323,6 @@
)
.aspectRatio(1f)
.clip(RoundedCornerShape(20.dp))
- .background(Color(0xffaaaaaa))
)
Spacer(Modifier.size(10.dp))
Text(
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/res/drawable/question_mark.xml b/compose/animation/animation/integration-tests/animation-demos/src/main/res/drawable/question_mark.xml
index 929ae8a..f87589f 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/res/drawable/question_mark.xml
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/res/drawable/question_mark.xml
@@ -17,5 +17,6 @@
<vector android:height="12dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="12dp" xmlns:android="http://schemas.android.com/apk/res/android">
+<path android:pathData="M0,0h24v24H0V0z" android:fillColor="#aaaaaa" />
<path android:fillColor="#CCCCCC" android:pathData="M11.07,12.85c0.77,-1.39 2.25,-2.21 3.11,-3.44c0.91,-1.29 0.4,-3.7 -2.18,-3.7c-1.69,0 -2.52,1.28 -2.87,2.34L6.54,6.96C7.25,4.83 9.18,3 11.99,3c2.35,0 3.96,1.07 4.78,2.41c0.7,1.15 1.11,3.3 0.03,4.9c-1.2,1.77 -2.35,2.31 -2.97,3.45c-0.25,0.46 -0.35,0.76 -0.35,2.24h-2.89C10.58,15.22 10.46,13.95 11.07,12.85zM14,20c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2S14,18.9 14,20z"/>
</vector>
\ No newline at end of file
diff --git a/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/SharedTransitionTest.kt b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/SharedTransitionTest.kt
index 012d184..16782be 100644
--- a/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/SharedTransitionTest.kt
+++ b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/SharedTransitionTest.kt
@@ -29,15 +29,18 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
@@ -45,6 +48,7 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.testutils.assertPixels
import androidx.compose.ui.Alignment
@@ -79,6 +83,7 @@
import kotlin.math.roundToInt
import kotlin.math.sqrt
import kotlin.random.Random
+import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -2469,7 +2474,6 @@
repeat(6) {
rule.waitForIdle()
rule.mainClock.advanceTimeByFrame()
- println("LTD, scaleX: ${scaleX[selected]}, detailX: $detailScaleX")
assertEquals(scaleX[selected], scaleY[selected])
assertEquals(detailScaleX, detailScaleY)
@@ -2569,6 +2573,217 @@
isSquare = !isSquare
rule.waitForIdle()
}
+
+ sealed class Screen {
+ abstract val key: Int
+
+ object List : Screen() {
+ override val key = 1
+ }
+
+ data class Details(val item: Int) : Screen() {
+ override val key = 2
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = 26)
+ @Test
+ fun testRandomScrollingWithInterruption() {
+ @Suppress("PrimitiveInCollection")
+ val colors = listOf(
+ Color(0xffff6f69),
+ Color(0xffffcc5c),
+ Color(0xff2a9d84),
+ Color(0xff264653)
+ )
+ var state by mutableStateOf<Screen>(Screen.List)
+ val lazyListState = LazyListState()
+ rule.setContent {
+ SharedTransitionLayout(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(800.dp)
+ ) {
+ AnimatedContent(state, label = "", contentKey = { it.key },
+ transitionSpec = {
+ if (initialState == Screen.List) {
+ slideInHorizontally { -it } + fadeIn() togetherWith
+ slideOutHorizontally { it } + fadeOut()
+ } else {
+ slideInHorizontally { it } + fadeIn() togetherWith
+ slideOutHorizontally { -it } + fadeOut()
+ }
+ }) {
+ when (it) {
+ Screen.List -> {
+ LazyColumn(state = lazyListState) {
+ items(50) { item ->
+ Row(modifier = Modifier.fillMaxWidth()) {
+ Box(
+ modifier = Modifier
+ .size(100.dp)
+ .then(
+ Modifier.sharedElement(
+ rememberSharedContentState(
+ key = "item-image$item"
+ ),
+ this@AnimatedContent,
+ )
+ )
+ .background(colors[item % 4]),
+ )
+ Spacer(Modifier.size(15.dp))
+ }
+ }
+ }
+ }
+
+ is Screen.Details -> {
+ val item = it.item
+ Column(modifier = Modifier.fillMaxSize()) {
+ Box(
+ modifier = Modifier
+ .sharedElement(
+ rememberSharedContentState(key = "item-image$item"),
+ this@AnimatedContent,
+ )
+ .background(colors[item % 4])
+ .fillMaxWidth(),
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ rule.waitForIdle()
+ rule.mainClock.autoAdvance = false
+ state = Screen.Details(5)
+ repeat(3) {
+ rule.waitForIdle()
+ rule.mainClock.advanceTimeByFrame()
+ }
+ state = Screen.List
+ rule.waitForIdle()
+ rule.mainClock.advanceTimeByFrame()
+
+ repeat(3) {
+ repeat(5) {
+ rule.runOnIdle {
+ runBlocking {
+ lazyListState.scrollToItem(40 - it * 10)
+ }
+ }
+ }
+ rule.mainClock.advanceTimeByFrame()
+ repeat(10) {
+ rule.runOnIdle {
+ runBlocking {
+ lazyListState.scrollToItem(it * 5)
+ }
+ }
+ }
+ rule.mainClock.advanceTimeByFrame()
+ rule.runOnIdle {
+ runBlocking {
+ lazyListState.scrollToItem(0)
+ }
+ }
+ }
+ rule.mainClock.autoAdvance = false
+ }
+
+ @Test
+ fun vigorouslyScrollingSharedElementsInLazyList() {
+ var state by mutableStateOf<Screen>(Screen.List)
+ val lazyListState = LazyListState()
+
+ @Suppress("PrimitiveInCollection")
+ val colors = listOf(
+ Color(0xffff6f69),
+ Color(0xffffcc5c),
+ Color(0xff2a9d84),
+ Color(0xff264653)
+ )
+ rule.setContent {
+ SharedTransitionLayout(modifier = Modifier.fillMaxSize()) {
+ LazyColumn(state = lazyListState) {
+ items(50) { item ->
+ AnimatedVisibility(visible = (state as? Screen.Details)?.item != item) {
+ Row(modifier = Modifier.fillMaxWidth()) {
+ Box(
+ modifier = Modifier
+ .size(100.dp)
+ .then(
+ Modifier.sharedElement(
+ rememberSharedContentState(
+ key = "item-image$item"
+ ),
+ this@AnimatedVisibility,
+ )
+ )
+ .background(colors[item % 4]),
+ )
+ Spacer(Modifier.size(15.dp))
+ }
+ }
+ }
+ }
+
+ AnimatedVisibility(visible = state is Screen.Details) {
+ var item: Int? by remember { mutableStateOf(null) }
+ if (state is Screen.Details) {
+ item = (state as Screen.Details).item
+ }
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ ) {
+ Box(
+ modifier = Modifier
+ .sharedElement(
+ rememberSharedContentState(key = "item-image$item"),
+ this@AnimatedVisibility,
+ )
+ .fillMaxWidth()
+ .background(colors[item!! % 4])
+ )
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ rule.mainClock.autoAdvance = false
+ state = Screen.Details(5)
+ repeat(5) {
+ rule.waitForIdle()
+ rule.mainClock.advanceTimeByFrame()
+ }
+ state = Screen.List
+ repeat(3) {
+ rule.waitForIdle()
+ rule.mainClock.advanceTimeByFrame()
+ }
+
+ repeat(5) {
+ rule.runOnIdle {
+ runBlocking {
+ lazyListState.scrollToItem(it + 1)
+ }
+ }
+ rule.mainClock.advanceTimeByFrame()
+ }
+ repeat(20) {
+ rule.runOnIdle {
+ val id = Random.nextInt(0, 20)
+ runBlocking {
+ lazyListState.scrollToItem(id)
+ }
+ }
+ rule.mainClock.advanceTimeByFrame()
+ }
+ }
}
private fun assertEquals(a: IntSize, b: IntSize, delta: IntSize) {
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
index 71d31fb..16d9ae7 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalAnimationApi::class)
-
package androidx.compose.animation
import androidx.compose.animation.EnterExitState.PostExit
@@ -296,7 +294,6 @@
* @sample androidx.compose.animation.samples.AnimatedVisibilityWithBooleanVisibleParamNoReceiver
* @see AnimatedVisibility
*/
-@ExperimentalAnimationApi
enum class EnterExitState {
/**
* The initial state of a custom enter animation in [AnimatedVisibility]..
@@ -603,7 +600,6 @@
* @see AnimatedVisibilityScope
* @see Transition.AnimatedVisibility
*/
-@ExperimentalAnimationApi
@Composable
fun <T> Transition<T>.AnimatedVisibility(
visible: (T) -> Boolean,
@@ -674,7 +670,6 @@
}
}
-@ExperimentalAnimationApi
internal class AnimatedVisibilityScopeImpl internal constructor(
transition: Transition<EnterExitState>
) : AnimatedVisibilityScope {
@@ -682,47 +677,12 @@
internal val targetSize = mutableStateOf(IntSize.Zero)
}
-@ExperimentalAnimationApi
-@Composable
-@Deprecated(
- "AnimatedVisibility no longer accepts initiallyVisible as a parameter, please use " +
- "AnimatedVisibility(MutableTransitionState, Modifier, ...) API instead",
- replaceWith = ReplaceWith(
- "AnimatedVisibility(" +
- "transitionState = remember { MutableTransitionState(initiallyVisible) }\n" +
- ".apply { targetState = visible },\n" +
- "modifier = modifier,\n" +
- "enter = enter,\n" +
- "exit = exit) {\n" +
- "content() \n" +
- "}",
- "androidx.compose.animation.core.MutableTransitionState"
- )
-)
-fun AnimatedVisibility(
- visible: Boolean,
- modifier: Modifier = Modifier,
- enter: EnterTransition,
- exit: ExitTransition,
- initiallyVisible: Boolean,
- content: @Composable () -> Unit
-) = AnimatedVisibility(
- visibleState = remember { MutableTransitionState(initiallyVisible) }
- .apply { targetState = visible },
- modifier = modifier,
- enter = enter,
- exit = exit
-) {
- content()
-}
-
/**
* RowScope and ColumnScope AnimatedVisibility extensions and AnimatedVisibility without a receiver
* converge here.
* AnimatedVisibilityImpl sets up 2 things: 1) It adds a modifier to report 0 size in lookahead
* when animating out. 2) It sets up a criteria for when content should be disposed.
*/
-@OptIn(ExperimentalAnimationApi::class)
@Composable
internal fun <T> AnimatedVisibilityImpl(
transition: Transition<T>,
@@ -764,7 +724,6 @@
@OptIn(
ExperimentalTransitionApi::class,
InternalAnimationApi::class,
- ExperimentalAnimationApi::class,
)
@Composable
internal fun <T> AnimatedEnterExitImpl(
@@ -842,7 +801,6 @@
private val Transition<EnterExitState>.exitFinished
get() = currentState == PostExit && targetState == PostExit
-@OptIn(ExperimentalAnimationApi::class)
private class AnimatedEnterExitMeasurePolicy(
val scope: AnimatedVisibilityScopeImpl
) : MeasurePolicy {
@@ -891,7 +849,6 @@
}
// This converts Boolean visible to EnterExitState
-@OptIn(InternalAnimationApi::class, ExperimentalAnimationApi::class)
@Composable
private fun <T> Transition<T>.targetEnterExit(
visible: (T) -> Boolean,
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedContentNode.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedContentNode.kt
index e8913d0..9b964d2 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedContentNode.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedContentNode.kt
@@ -42,6 +42,7 @@
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.requireDensity
import androidx.compose.ui.node.requireGraphicsContext
+import androidx.compose.ui.node.requireLayoutCoordinates
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
@@ -87,23 +88,18 @@
provide(ModifierLocalSharedElementInternalState, value)
state.parentState = ModifierLocalSharedElementInternalState.current
state.layer = layer
- state.lookaheadCoords = lookaheadCoords
- state.lookaheadSize = lookaheadSize
+ state.lookaheadCoords = { requireLookaheadLayoutCoordinates() }
}
}
}
- private var lookaheadCoords: LayoutCoordinates? = state.lookaheadCoords
- set(value) {
- state.lookaheadCoords = value
- field = value
+ @OptIn(ExperimentalComposeUiApi::class)
+ private fun requireLookaheadLayoutCoordinates(): LayoutCoordinates =
+ with(state.sharedElement.scope) {
+ requireLayoutCoordinates().toLookaheadCoordinates()
}
+
private val boundsAnimation: BoundsAnimation get() = state.boundsAnimation
- private var lookaheadSize: Size? = state.lookaheadSize
- set(value) {
- state.lookaheadSize = value
- field = value
- }
private var layer: GraphicsLayer? = state.layer
set(value) {
@@ -126,13 +122,21 @@
provide(ModifierLocalSharedElementInternalState, state)
state.parentState = ModifierLocalSharedElementInternalState.current
layer = requireGraphicsContext().createGraphicsLayer()
+ state.lookaheadCoords = { requireLookaheadLayoutCoordinates() }
}
override fun onDetach() {
super.onDetach()
layer = null
state.parentState = null
- lookaheadCoords = null
+ state.lookaheadCoords = { null }
+ }
+
+ override fun onReset() {
+ super.onReset()
+ // Reset layer
+ layer?.let { requireGraphicsContext().releaseGraphicsLayer(it) }
+ layer = requireGraphicsContext().createGraphicsLayer()
}
override fun MeasureScope.measure(
@@ -141,17 +145,14 @@
): MeasureResult {
// Lookahead pass: Record lookahead size and lookahead coordinates
val placeable = measurable.measure(constraints)
- lookaheadSize = Size(placeable.width.toFloat(), placeable.height.toFloat())
+ val lookaheadSize = Size(placeable.width.toFloat(), placeable.height.toFloat())
return layout(placeable.width, placeable.height) {
val topLeft = coordinates?.let {
- lookaheadCoords = it
rootLookaheadCoords.localPositionOf(it, Offset.Zero).also { topLeft ->
if (sharedElement.currentBounds == null) {
sharedElement.currentBounds = Rect(
topLeft,
- requireNotNull(lookaheadSize) {
- "Error: Lookahead measure has not happened."
- }
+ lookaheadSize
)
}
}
@@ -160,14 +161,14 @@
// Update the lookahead result after child placement, so that child has an
// opportunity to use its placement to influence the bounds animation.
topLeft?.let {
- sharedElement.onLookaheadResult(state, lookaheadSize!!, it)
+ sharedElement.onLookaheadResult(state, lookaheadSize, it)
}
}
}
private fun MeasureScope.place(placeable: Placeable): MeasureResult {
val (w, h) = state.placeHolderSize.calculateSize(
- lookaheadSize!!.roundToIntSize(),
+ requireLookaheadLayoutCoordinates().size,
IntSize(placeable.width, placeable.height)
)
return layout(w, h) {
@@ -262,9 +263,9 @@
layer.record {
[email protected]()
- if (VisualDebugging) {
+ if (VisualDebugging && sharedElement.foundMatch) {
// TODO: also draw border of the clip path
- drawRect(Color.Red, style = Stroke(3f))
+ drawRect(Color.Green, style = Stroke(3f))
}
}
if (state.shouldRenderInPlace) {
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedElement.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedElement.kt
index 13841b8..2a9aafd 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedElement.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedElement.kt
@@ -35,6 +35,7 @@
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.unit.toSize
import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachReversed
@@ -55,9 +56,7 @@
val targetBounds: Rect?
get() {
_targetBounds = targetBoundsProvider?.run {
- Rect(calculateLookaheadOffset(), requireNotNull(lookaheadSize) {
- "Error: target has not been lookahead measured."
- })
+ Rect(calculateLookaheadOffset(), nonNullLookaheadSize)
}
return _targetBounds
}
@@ -196,17 +195,15 @@
var overlayClip: SharedTransitionScope.OverlayClip by mutableStateOf(overlayClip)
var userState: SharedTransitionScope.SharedContentState by mutableStateOf(userState)
- init {
- sharedElement.scope.onStateAdded(this)
- sharedElement.updateTargetBoundsProvider()
- }
-
internal var clipPathInOverlay: Path? = null
override fun drawInOverlay(drawScope: DrawScope) {
val layer = layer ?: return
if (shouldRenderInOverlay) {
with(drawScope) {
+ requireNotNull(sharedElement.currentBounds) {
+ "Error: current bounds not set yet."
+ }
val (x, y) = sharedElement.currentBounds?.topLeft!!
clipPathInOverlay?.let {
clipPath(it) {
@@ -219,21 +216,26 @@
}
}
- var lookaheadSize: Size? = null
- var lookaheadCoords: LayoutCoordinates? = null
+ val nonNullLookaheadSize: Size
+ get() = requireNotNull(lookaheadCoords()) {
+ "Error: lookahead coordinates is null for ${sharedElement.key}."
+ }.size.toSize()
+ var lookaheadCoords: () -> LayoutCoordinates? = { null }
override var parentState: SharedElementInternalState? = null
// This can only be accessed during placement
fun calculateLookaheadOffset(): Offset {
- val c = requireNotNull(lookaheadCoords) {
- "Error: target has not been placed in lookahead pass yet."
+ val c = requireNotNull(lookaheadCoords()) {
+ "Error: lookahead coordinates is null."
}
return sharedElement.scope.lookaheadRoot.localPositionOf(c, Offset.Zero)
}
val target: Boolean get() = boundsAnimation.target
- var layer: GraphicsLayer? = null
+ // Delegate the property to a mutable state, so that when layer is updated, the rendering
+ // gets invalidated.
+ var layer: GraphicsLayer? by mutableStateOf(null)
private val shouldRenderBasedOnTarget: Boolean
get() = sharedElement.targetBoundsProvider == this || !renderOnlyWhenVisible
@@ -246,6 +248,8 @@
get() = !sharedElement.foundMatch || (!shouldRenderInOverlay && shouldRenderBasedOnTarget)
override fun onRemembered() {
+ sharedElement.scope.onStateAdded(this)
+ sharedElement.updateTargetBoundsProvider()
}
override fun onForgotten() {
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedTransitionScope.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedTransitionScope.kt
index 799827c..74cef36 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedTransitionScope.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedTransitionScope.kt
@@ -860,44 +860,67 @@
clipInOverlayDuringTransition: OverlayClip,
) = composed {
val key = sharedContentState.key
- val sharedElement = remember(key) { sharedElementsFor(key) }
+ val sharedElementState = key(key) {
+ val sharedElement = remember { sharedElementsFor(key) }
- @Suppress("UNCHECKED_CAST")
- val boundsTransition = key(key, parentTransition) {
- if (parentTransition != null) {
- parentTransition.createChildTransition(key.toString()) { visible(it) }
- } else {
- val targetState =
- (visible as (Unit) -> Boolean).invoke(Unit)
- val transitionState = remember {
- MutableTransitionState(
- initialState = if (sharedElement.currentBounds != null) {
- // In the transition that we completely own, we could make the
- // assumption that if a new shared element is added, it'll
- // always animate from current bounds to target bounds. This ensures
- // continuity of shared element bounds.
- !targetState
- } else {
- targetState
- }
+ val boundsAnimation = key(parentTransition) {
+ val boundsTransition = if (parentTransition != null) {
+ parentTransition.createChildTransition(key.toString()) { visible(it) }
+ } else {
+ @Suppress("UNCHECKED_CAST")
+ val targetState =
+ (visible as (Unit) -> Boolean).invoke(Unit)
+ val transitionState = remember {
+ MutableTransitionState(
+ initialState = if (sharedElement.currentBounds != null) {
+ // In the transition that we completely own, we could make the
+ // assumption that if a new shared element is added, it'll
+ // always animate from current bounds to target bounds. This ensures
+ // continuity of shared element bounds.
+ !targetState
+ } else {
+ targetState
+ }
+ )
+ }.also { it.targetState = targetState }
+ rememberTransition(transitionState)
+ }
+ val animation = key(isTransitionActive) {
+ boundsTransition.createDeferredAnimation(Rect.VectorConverter)
+ }
+ remember(boundsTransition) {
+ BoundsAnimation(
+ this@SharedTransitionScope, boundsTransition, animation, boundsTransform
)
- }.also { it.targetState = targetState }
- rememberTransition(transitionState)
+ }.also { it.updateAnimation(animation, boundsTransform) }
}
- }
- val animation = key(isTransitionActive) {
- boundsTransition.createDeferredAnimation(Rect.VectorConverter)
- }
-
- val boundsAnimation = remember(boundsTransition) {
- BoundsAnimation(
- this@SharedTransitionScope, boundsTransition, animation, boundsTransform
+ rememberSharedElementState(
+ sharedElement = sharedElement,
+ boundsAnimation = boundsAnimation,
+ placeHolderSize = placeHolderSize,
+ renderOnlyWhenVisible = renderOnlyWhenVisible,
+ sharedContentState = sharedContentState,
+ clipInOverlayDuringTransition = clipInOverlayDuringTransition,
+ zIndexInOverlay = zIndexInOverlay,
+ renderInOverlayDuringTransition = renderInOverlayDuringTransition
)
- }.also {
- it.updateAnimation(animation, boundsTransform)
}
- val sharedElementState = remember(key) {
+ this then SharedBoundsNodeElement(sharedElementState)
+ }
+
+ @Composable
+ private fun rememberSharedElementState(
+ sharedElement: SharedElement,
+ boundsAnimation: BoundsAnimation,
+ placeHolderSize: PlaceHolderSize,
+ renderOnlyWhenVisible: Boolean,
+ sharedContentState: SharedContentState,
+ clipInOverlayDuringTransition: OverlayClip,
+ zIndexInOverlay: Float,
+ renderInOverlayDuringTransition: Boolean
+ ): SharedElementInternalState =
+ remember {
SharedElementInternalState(
sharedElement,
boundsAnimation,
@@ -921,9 +944,6 @@
it.userState = sharedContentState
}
- this then SharedBoundsNodeElement(sharedElementState)
- }
-
internal lateinit var root: LayoutCoordinates
internal lateinit var lookaheadRoot: LayoutCoordinates
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
index 3ba1cac..300d8ed 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
@@ -30,8 +30,10 @@
abstract class AbstractIrTransformTest(useFir: Boolean) : AbstractCodegenTest(useFir) {
override fun CompilerConfiguration.updateConfiguration() {
put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, true)
- put(ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY, true)
- put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
+ put(ComposeConfiguration.FEATURE_FLAGS, listOf(
+ FeatureFlag.StrongSkipping.featureName,
+ FeatureFlag.OptimizeNonSkippingGroups.featureName,
+ ))
}
@JvmField
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractLiveLiteralTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractLiveLiteralTransformTests.kt
index d7ed43d..3540874 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractLiveLiteralTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/AbstractLiveLiteralTransformTests.kt
@@ -63,7 +63,8 @@
pluginContext,
symbolRemapper,
ModuleMetricsImpl("temp") { stabilityInferencer.stabilityOf(it) },
- stabilityInferencer
+ stabilityInferencer,
+ FeatureFlags()
) {
override fun makeKeySet(): MutableSet<String> {
return super.makeKeySet().also { builtKeys = it }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposeBytecodeCodegenTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposeBytecodeCodegenTest.kt
index dbbe560..b37ef1e 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposeBytecodeCodegenTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposeBytecodeCodegenTest.kt
@@ -17,6 +17,7 @@
package androidx.compose.compiler.plugins.kotlin
import kotlin.test.assertFalse
+import kotlin.test.assertTrue
import org.junit.Assume.assumeFalse
import org.junit.Test
@@ -585,11 +586,18 @@
""",
validate = {
// select Example function body
- val match = Regex("public final static Example[\\s\\S]*?LOCALVARIABLE").find(it)!!
+ val func = Regex("public final static Example[\\s\\S]*?LOCALVARIABLE")
+ .findAll(it)
+ .single()
assertFalse(message = "Function body should not contain a not-null check.") {
- match.value.contains("Intrinsics.checkNotNullParameter")
+ func.value.contains("Intrinsics.checkNotNullParameter")
+ }
+ val stub = Regex("public final static synthetic Example[\\s\\S]*?LOCALVARIABLE")
+ .findAll(it)
+ .single()
+ assertTrue(message = "Function stub should contain a not-null check.") {
+ stub.value.contains("Intrinsics.checkNotNullParameter")
}
},
- dumpClasses = true
)
}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposeModuleMetricsTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposeModuleMetricsTests.kt
index afb7b31..a409059 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposeModuleMetricsTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposeModuleMetricsTests.kt
@@ -22,7 +22,10 @@
class ComposeModuleMetricsTests(useFir: Boolean) : AbstractMetricsTransformTest(useFir) {
override fun CompilerConfiguration.updateConfiguration() {
// Tests in this file are about testing the output, so we want non-skippable composables
- put(ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY, false)
+ put(
+ ComposeConfiguration.FEATURE_FLAGS,
+ listOf(FeatureFlag.StrongSkipping.disabledName)
+ )
}
@Test
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
index 2aaf3ae..17951ef 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
@@ -1782,4 +1782,190 @@
)
}
)
+
+ @Test
+ fun testDefaultParamInlineClassRefType() = checkApi(
+ """
+ @JvmInline
+ value class Data(val string: String)
+ @JvmInline
+ value class IntData(val value: Int)
+
+ @Composable fun Example(data: Data = Data(""), intData: IntData = IntData(0)) {}
+ @Composable private fun PrivateExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+ @Composable internal fun InternalExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+ @Composable @PublishedApi internal fun PublishedExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+
+ abstract class Test {
+ @Composable private fun PrivateExample(data: Data = Data("")) {}
+ @Composable fun PublicExample(data: Data = Data("")) {}
+ @Composable internal fun InternalExample(data: Data = Data("")) {}
+ @Composable @PublishedApi internal fun PublishedExample(data: Data = Data("")) {}
+ @Composable protected fun ProtectedExample(data: Data = Data("")) {}
+ }
+ """,
+ """
+ public final class Data {
+ public final getString()Ljava/lang/String;
+ public static toString-impl(Ljava/lang/String;)Ljava/lang/String;
+ public toString()Ljava/lang/String;
+ public static hashCode-impl(Ljava/lang/String;)I
+ public hashCode()I
+ public static equals-impl(Ljava/lang/String;Ljava/lang/Object;)Z
+ public equals(Ljava/lang/Object;)Z
+ private synthetic <init>(Ljava/lang/String;)V
+ public static constructor-impl(Ljava/lang/String;)Ljava/lang/String;
+ public final static synthetic box-impl(Ljava/lang/String;)LData;
+ public final synthetic unbox-impl()Ljava/lang/String;
+ public final static equals-impl0(Ljava/lang/String;Ljava/lang/String;)Z
+ private final Ljava/lang/String; string
+ }
+ public final class IntData {
+ public final getValue()I
+ public static toString-impl(I)Ljava/lang/String;
+ public toString()Ljava/lang/String;
+ public static hashCode-impl(I)I
+ public hashCode()I
+ public static equals-impl(ILjava/lang/Object;)Z
+ public equals(Ljava/lang/Object;)Z
+ private synthetic <init>(I)V
+ public static constructor-impl(I)I
+ public final static synthetic box-impl(I)LIntData;
+ public final synthetic unbox-impl()I
+ public final static equals-impl0(II)Z
+ private final I value
+ }
+ public abstract class Test {
+ public <init>()V
+ private final PrivateExample-PLmOXgA(Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ public final PublicExample-PLmOXgA(Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ public final InternalExample-PLmOXgA%test_module(Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ public final PublishedExample-PLmOXgA(Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ protected final ProtectedExample-PLmOXgA(Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ public final synthetic PublicExample-XUE1X6o(Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ public final synthetic PublishedExample-XUE1X6o(Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ protected final synthetic ProtectedExample-XUE1X6o(Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ public final static synthetic access%PrivateExample-PLmOXgA(LTest;Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ static <clinit>()V
+ public final static I %stable
+ final static INNERCLASS Test%InternalExample%1 null null
+ final static INNERCLASS Test%PrivateExample%1 null null
+ final static INNERCLASS Test%ProtectedExample%1 null null
+ final static INNERCLASS Test%PublicExample%1 null null
+ final static INNERCLASS Test%PublishedExample%1 null null
+ }
+ final class Test%PrivateExample%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>(LTest;Ljava/lang/String;II)V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ final synthetic LTest; %tmp0_rcvr
+ final synthetic Ljava/lang/String; %data
+ final synthetic I %%changed
+ final synthetic I %%default
+ OUTERCLASS Test PrivateExample-PLmOXgA (Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS Test%PrivateExample%1 null null
+ }
+ final class Test%PublicExample%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>(LTest;Ljava/lang/String;II)V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ final synthetic LTest; %tmp0_rcvr
+ final synthetic Ljava/lang/String; %data
+ final synthetic I %%changed
+ final synthetic I %%default
+ OUTERCLASS Test PublicExample-PLmOXgA (Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS Test%PublicExample%1 null null
+ }
+ final class Test%InternalExample%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>(LTest;Ljava/lang/String;II)V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ final synthetic LTest; %tmp0_rcvr
+ final synthetic Ljava/lang/String; %data
+ final synthetic I %%changed
+ final synthetic I %%default
+ OUTERCLASS Test InternalExample-PLmOXgA%test_module (Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS Test%InternalExample%1 null null
+ }
+ final class Test%PublishedExample%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>(LTest;Ljava/lang/String;II)V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ final synthetic LTest; %tmp0_rcvr
+ final synthetic Ljava/lang/String; %data
+ final synthetic I %%changed
+ final synthetic I %%default
+ OUTERCLASS Test PublishedExample-PLmOXgA (Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS Test%PublishedExample%1 null null
+ }
+ final class Test%ProtectedExample%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>(LTest;Ljava/lang/String;II)V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ final synthetic LTest; %tmp0_rcvr
+ final synthetic Ljava/lang/String; %data
+ final synthetic I %%changed
+ final synthetic I %%default
+ OUTERCLASS Test ProtectedExample-PLmOXgA (Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS Test%ProtectedExample%1 null null
+ }
+ public final class TestKt {
+ public final static Example-zy-tHAg(Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ private final static PrivateExample-zy-tHAg(Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ public final static InternalExample-zy-tHAg(Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ public final static PublishedExample-zy-tHAg(Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ public final static synthetic Example-phPabU0(Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ public final static synthetic PublishedExample-phPabU0(Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ public final static synthetic access%PrivateExample-zy-tHAg(Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS TestKt%Example%1 null null
+ final static INNERCLASS TestKt%InternalExample%1 null null
+ final static INNERCLASS TestKt%PrivateExample%1 null null
+ final static INNERCLASS TestKt%PublishedExample%1 null null
+ }
+ final class TestKt%Example%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>(Ljava/lang/String;III)V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ final synthetic Ljava/lang/String; %data
+ final synthetic I %intData
+ final synthetic I %%changed
+ final synthetic I %%default
+ OUTERCLASS TestKt Example-zy-tHAg (Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS TestKt%Example%1 null null
+ }
+ final class TestKt%PrivateExample%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>(Ljava/lang/String;III)V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ final synthetic Ljava/lang/String; %data
+ final synthetic I %intData
+ final synthetic I %%changed
+ final synthetic I %%default
+ OUTERCLASS TestKt PrivateExample-zy-tHAg (Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS TestKt%PrivateExample%1 null null
+ }
+ final class TestKt%InternalExample%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>(Ljava/lang/String;III)V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ final synthetic Ljava/lang/String; %data
+ final synthetic I %intData
+ final synthetic I %%changed
+ final synthetic I %%default
+ OUTERCLASS TestKt InternalExample-zy-tHAg (Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS TestKt%InternalExample%1 null null
+ }
+ final class TestKt%PublishedExample%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+ <init>(Ljava/lang/String;III)V
+ public final invoke(Landroidx/compose/runtime/Composer;I)V
+ public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ final synthetic Ljava/lang/String; %data
+ final synthetic I %intData
+ final synthetic I %%changed
+ final synthetic I %%default
+ OUTERCLASS TestKt PublishedExample-zy-tHAg (Ljava/lang/String;ILandroidx/compose/runtime/Composer;II)V
+ final static INNERCLASS TestKt%PublishedExample%1 null null
+ }
+ """.trimIndent()
+ )
}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
index 9457ace..b7ceccb 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
@@ -580,12 +580,27 @@
@JvmInline
value class Data(val string: String)
@JvmInline
+ value class NullableData(val string: String?)
+ @JvmInline
value class IntData(val value: Int)
""",
source = """
import androidx.compose.runtime.*
@Composable fun Example(data: Data = Data(""), intData: IntData = IntData(0)) {}
+ @Composable fun ExampleNullable(data: Data? = Data(""), intData: IntData = IntData(0)) {}
+ @Composable fun ExampleNullableData(data: NullableData = NullableData(null), intData: IntData = IntData(0)) {}
+ @Composable private fun PrivateExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+ @Composable internal fun InternalExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+ @Composable @PublishedApi internal fun PublishedExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+
+ abstract class Test {
+ @Composable private fun PrivateExample(data: Data = Data("")) {}
+ @Composable fun PublicExample(data: Data = Data("")) {}
+ @Composable internal fun InternalExample(data: Data = Data("")) {}
+ @Composable @PublishedApi internal fun PublishedExample(data: Data = Data("")) {}
+ @Composable protected fun ProtectedExample(data: Data = Data("")) {}
+ }
"""
)
}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
index 052076b..57a4b23 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
@@ -24,7 +24,10 @@
) : AbstractControlFlowTransformTests(useFir) {
override fun CompilerConfiguration.updateConfiguration() {
put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, false)
- put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
+ put(
+ ComposeConfiguration.FEATURE_FLAGS,
+ listOf(FeatureFlag.OptimizeNonSkippingGroups.featureName)
+ )
put(ComposeConfiguration.TRACE_MARKERS_ENABLED_KEY, false)
}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
index 1a6c166..a879c13 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
@@ -25,7 +25,10 @@
class LambdaMemoizationTransformTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
override fun CompilerConfiguration.updateConfiguration() {
put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, true)
- put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
+ put(
+ ComposeConfiguration.FEATURE_FLAGS,
+ listOf(FeatureFlag.OptimizeNonSkippingGroups.featureName)
+ )
languageVersionSettings = LanguageVersionSettingsImpl(
languageVersion = languageVersionSettings.languageVersion,
apiVersion = languageVersionSettings.apiVersion,
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
index 105e2eb..721f513 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
@@ -23,8 +23,13 @@
class RememberIntrinsicTransformTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
override fun CompilerConfiguration.updateConfiguration() {
put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, true)
- put(ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY, true)
- put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
+ put(
+ ComposeConfiguration.FEATURE_FLAGS,
+ listOf(
+ FeatureFlag.OptimizeNonSkippingGroups.featureName,
+ FeatureFlag.IntrinsicRemember.featureName
+ )
+ )
}
private fun comparisonPropagation(
@@ -793,9 +798,14 @@
) : AbstractIrTransformTest(useFir) {
override fun CompilerConfiguration.updateConfiguration() {
put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, true)
- put(ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY, true)
- put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
- put(ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY, true)
+ put(
+ ComposeConfiguration.FEATURE_FLAGS,
+ listOf(
+ FeatureFlag.IntrinsicRemember.featureName,
+ FeatureFlag.OptimizeNonSkippingGroups.featureName,
+ FeatureFlag.StrongSkipping.featureName
+ )
+ )
}
@Test
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt
index 2692965..d324b21 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt
@@ -440,7 +440,6 @@
val instanceClass = compiledClassesLoader.loadClass(className)
val instanceOfClass = instanceClass.getDeclaredConstructor().newInstance()
- println(instanceClass.methods.joinToString())
val testMethod = instanceClass.getMethod(
"test",
Composer::class.java,
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StabilityConfigurationParserTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StabilityConfigurationParserTests.kt
index fd9324b..9a65db7 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StabilityConfigurationParserTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StabilityConfigurationParserTests.kt
@@ -166,7 +166,10 @@
"$PATH_TO_CONFIG_FILES/config2.conf"
)
)
- put(ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY, false)
+ put(
+ ComposeConfiguration.FEATURE_FLAGS,
+ listOf(FeatureFlag.OptimizeNonSkippingGroups.featureName)
+ )
}
@Test
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
index 760ded6..2c03a8b 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
@@ -42,12 +42,14 @@
}
override fun CompilerConfiguration.updateConfiguration() {
- put(ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY, true)
put(
- ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
- intrinsicRememberEnabled
+ ComposeConfiguration.FEATURE_FLAGS,
+ listOf(
+ FeatureFlag.StrongSkipping.featureName,
+ FeatureFlag.OptimizeNonSkippingGroups.featureName,
+ FeatureFlag.IntrinsicRemember.name(intrinsicRememberEnabled)
+ )
)
- put(ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY, true)
}
@Test
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/composeValueClassDefaultParameter\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/composeValueClassDefaultParameter\133useFir = false\135.txt"
index f43c062..222f44b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/composeValueClassDefaultParameter\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/composeValueClassDefaultParameter\133useFir = false\135.txt"
@@ -5,6 +5,19 @@
import androidx.compose.runtime.*
@Composable fun Example(data: Data = Data(""), intData: IntData = IntData(0)) {}
+@Composable fun ExampleNullable(data: Data? = Data(""), intData: IntData = IntData(0)) {}
+@Composable fun ExampleNullableData(data: NullableData = NullableData(null), intData: IntData = IntData(0)) {}
+@Composable private fun PrivateExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+@Composable internal fun InternalExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+@Composable @PublishedApi internal fun PublishedExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+
+abstract class Test {
+ @Composable private fun PrivateExample(data: Data = Data("")) {}
+ @Composable fun PublicExample(data: Data = Data("")) {}
+ @Composable internal fun InternalExample(data: Data = Data("")) {}
+ @Composable @PublishedApi internal fun PublishedExample(data: Data = Data("")) {}
+ @Composable protected fun ProtectedExample(data: Data = Data("")) {}
+}
//
// Transformed IR
@@ -34,3 +47,266 @@
Example(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
+@Composable
+fun ExampleNullable(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(ExampleNullable)P(0:Data,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ ExampleNullable(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@Composable
+fun ExampleNullableData(data: NullableData?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(ExampleNullableData)P(0:NullableData,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = NullableData(null)
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ ExampleNullableData(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@Composable
+private fun PrivateExample(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PrivateExample)P(0:Data,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ PrivateExample(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@Composable
+internal fun InternalExample(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(InternalExample)P(0:Data,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ InternalExample(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@Composable
+@PublishedApi
+internal fun PublishedExample(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PublishedExample)P(0:Data,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ PublishedExample(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@StabilityInferred(parameters = 1)
+abstract class Test {
+ @Composable
+ private fun PrivateExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PrivateExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.PrivateExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ @Composable
+ fun PublicExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PublicExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.PublicExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ @Composable
+ internal fun InternalExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(InternalExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.InternalExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ @Composable
+ @PublishedApi
+ internal fun PublishedExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PublishedExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.PublishedExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ @Composable
+ protected fun ProtectedExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(ProtectedExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.ProtectedExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ static val %stable: Int = 0
+ @Composable
+ @JvmSynthetic
+ fun PublicExample(data: Data, %composer: Composer?, %changed: Int, %default: Int) {
+ return PublicExample(data, %composer, %changed, %default)
+ }
+ @Composable
+ @PublishedApi
+ @JvmSynthetic
+ internal fun PublishedExample(data: Data, %composer: Composer?, %changed: Int, %default: Int) {
+ return PublishedExample(data, %composer, %changed, %default)
+ }
+ @Composable
+ @JvmSynthetic
+ protected fun ProtectedExample(data: Data, %composer: Composer?, %changed: Int, %default: Int) {
+ return ProtectedExample(data, %composer, %changed, %default)
+ }
+}
+@Composable
+@JvmSynthetic
+fun Example(data: Data, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ return Example(data, intData, %composer, %changed, %default)
+}
+@Composable
+@PublishedApi
+@JvmSynthetic
+internal fun PublishedExample(data: Data, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ return PublishedExample(data, intData, %composer, %changed, %default)
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/composeValueClassDefaultParameter\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/composeValueClassDefaultParameter\133useFir = true\135.txt"
index f43c062..222f44b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/composeValueClassDefaultParameter\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ComposerParamTransformTests/composeValueClassDefaultParameter\133useFir = true\135.txt"
@@ -5,6 +5,19 @@
import androidx.compose.runtime.*
@Composable fun Example(data: Data = Data(""), intData: IntData = IntData(0)) {}
+@Composable fun ExampleNullable(data: Data? = Data(""), intData: IntData = IntData(0)) {}
+@Composable fun ExampleNullableData(data: NullableData = NullableData(null), intData: IntData = IntData(0)) {}
+@Composable private fun PrivateExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+@Composable internal fun InternalExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+@Composable @PublishedApi internal fun PublishedExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
+
+abstract class Test {
+ @Composable private fun PrivateExample(data: Data = Data("")) {}
+ @Composable fun PublicExample(data: Data = Data("")) {}
+ @Composable internal fun InternalExample(data: Data = Data("")) {}
+ @Composable @PublishedApi internal fun PublishedExample(data: Data = Data("")) {}
+ @Composable protected fun ProtectedExample(data: Data = Data("")) {}
+}
//
// Transformed IR
@@ -34,3 +47,266 @@
Example(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
+@Composable
+fun ExampleNullable(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(ExampleNullable)P(0:Data,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ ExampleNullable(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@Composable
+fun ExampleNullableData(data: NullableData?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(ExampleNullableData)P(0:NullableData,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = NullableData(null)
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ ExampleNullableData(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@Composable
+private fun PrivateExample(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PrivateExample)P(0:Data,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ PrivateExample(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@Composable
+internal fun InternalExample(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(InternalExample)P(0:Data,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ InternalExample(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@Composable
+@PublishedApi
+internal fun PublishedExample(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PublishedExample)P(0:Data,1:IntData):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (%default and 0b0010 != 0) {
+ intData = IntData(0)
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ PublishedExample(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+}
+@StabilityInferred(parameters = 1)
+abstract class Test {
+ @Composable
+ private fun PrivateExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PrivateExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.PrivateExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ @Composable
+ fun PublicExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PublicExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.PublicExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ @Composable
+ internal fun InternalExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(InternalExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.InternalExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ @Composable
+ @PublishedApi
+ internal fun PublishedExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(PublishedExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.PublishedExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ @Composable
+ protected fun ProtectedExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(ProtectedExample)P(0:Data):Test.kt")
+ if (%changed and 0b0001 != 0 || !%composer.skipping) {
+ if (%default and 0b0001 != 0) {
+ data = Data("")
+ }
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.ProtectedExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
+ }
+ }
+ static val %stable: Int = 0
+ @Composable
+ @JvmSynthetic
+ fun PublicExample(data: Data, %composer: Composer?, %changed: Int, %default: Int) {
+ return PublicExample(data, %composer, %changed, %default)
+ }
+ @Composable
+ @PublishedApi
+ @JvmSynthetic
+ internal fun PublishedExample(data: Data, %composer: Composer?, %changed: Int, %default: Int) {
+ return PublishedExample(data, %composer, %changed, %default)
+ }
+ @Composable
+ @JvmSynthetic
+ protected fun ProtectedExample(data: Data, %composer: Composer?, %changed: Int, %default: Int) {
+ return ProtectedExample(data, %composer, %changed, %default)
+ }
+}
+@Composable
+@JvmSynthetic
+fun Example(data: Data, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ return Example(data, intData, %composer, %changed, %default)
+}
+@Composable
+@PublishedApi
+@JvmSynthetic
+internal fun PublishedExample(data: Data, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
+ return PublishedExample(data, intData, %composer, %changed, %default)
+}
diff --git a/compose/compiler/compiler-hosted/runtime-tests/build.gradle b/compose/compiler/compiler-hosted/runtime-tests/build.gradle
index 095b280..fd5ee44 100644
--- a/compose/compiler/compiler-hosted/runtime-tests/build.gradle
+++ b/compose/compiler/compiler-hosted/runtime-tests/build.gradle
@@ -74,7 +74,7 @@
kotlinOptions {
freeCompilerArgs += [
"-P",
- "plugin:androidx.compose.compiler.plugins.kotlin:nonSkippingGroupOptimization=true"
+ "plugin:androidx.compose.compiler.plugins.kotlin:featureFlag=OptimizeNonSkippingGroups"
]
}
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
index 6d1df04..cb3eec3 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
@@ -58,17 +58,15 @@
private val generateFunctionKeyMetaClasses: Boolean = false,
private val sourceInformationEnabled: Boolean = true,
private val traceMarkersEnabled: Boolean = true,
- private val intrinsicRememberEnabled: Boolean = false,
- private val nonSkippingGroupOptimizationEnabled: Boolean = false,
private val decoysEnabled: Boolean = false,
private val metricsDestination: String? = null,
private val reportsDestination: String? = null,
private val validateIr: Boolean = false,
private val useK2: Boolean = false,
- private val strongSkippingEnabled: Boolean = false,
private val stableTypeMatchers: Set<FqNameMatcher> = emptySet(),
private val moduleMetricsFactory: ((StabilityInferencer) -> ModuleMetrics)? = null,
private val descriptorSerializerContext: ComposeDescriptorSerializerContext? = null,
+ private val featureFlags: FeatureFlags,
) : IrGenerationExtension {
var metrics: ModuleMetrics = EmptyModuleMetrics
private set
@@ -105,14 +103,14 @@
}
}
- if (pluginContext.platform.isNative() &&
- descriptorSerializerContext?.hideFromObjCDeclarationsSet != null) {
+ if (pluginContext.platform.isNative()) {
AddHiddenFromObjCLowering(
pluginContext,
symbolRemapper,
metrics,
- descriptorSerializerContext.hideFromObjCDeclarationsSet,
- stabilityInferencer
+ descriptorSerializerContext?.hideFromObjCDeclarationsSet,
+ stabilityInferencer,
+ featureFlags,
).lower(moduleFragment)
}
@@ -125,7 +123,8 @@
classStabilityInferredCollection = descriptorSerializerContext
?.classStabilityInferredCollection?.takeIf {
!pluginContext.platform.isJvm()
- }
+ },
+ featureFlags,
).lower(moduleFragment)
LiveLiteralTransformer(
@@ -135,7 +134,8 @@
pluginContext,
symbolRemapper,
metrics,
- stabilityInferencer
+ stabilityInferencer,
+ featureFlags,
).lower(moduleFragment)
ComposableFunInterfaceLowering(pluginContext).lower(moduleFragment)
@@ -144,7 +144,8 @@
pluginContext,
symbolRemapper,
metrics,
- stabilityInferencer
+ stabilityInferencer,
+ featureFlags,
)
functionKeyTransformer.lower(moduleFragment)
@@ -155,9 +156,7 @@
symbolRemapper,
metrics,
stabilityInferencer,
- strongSkippingEnabled,
- intrinsicRememberEnabled,
- nonSkippingGroupOptimizationEnabled,
+ featureFlags,
).lower(moduleFragment)
if (!useK2) {
@@ -187,6 +186,7 @@
idSignatureBuilder,
stabilityInferencer,
metrics,
+ featureFlags,
).lower(moduleFragment)
SubstituteDecoyCallsTransformer(
@@ -195,6 +195,7 @@
idSignatureBuilder,
stabilityInferencer,
metrics,
+ featureFlags,
).lower(moduleFragment)
}
@@ -207,13 +208,15 @@
stabilityInferencer,
decoysEnabled,
metrics,
+ featureFlags,
).lower(moduleFragment)
ComposableTargetAnnotationsTransformer(
pluginContext,
symbolRemapper,
metrics,
- stabilityInferencer
+ stabilityInferencer,
+ featureFlags,
).lower(moduleFragment)
// transform calls to the currentComposer to just use the local parameter from the
@@ -227,9 +230,7 @@
stabilityInferencer,
sourceInformationEnabled,
traceMarkersEnabled,
- intrinsicRememberEnabled,
- nonSkippingGroupOptimizationEnabled,
- strongSkippingEnabled
+ featureFlags,
).lower(moduleFragment)
if (decoysEnabled) {
@@ -243,7 +244,8 @@
idSignatureBuilder,
metrics,
mangler!!,
- stabilityInferencer
+ stabilityInferencer,
+ featureFlags,
).lower(moduleFragment)
}
@@ -252,7 +254,8 @@
pluginContext,
symbolRemapper,
metrics,
- stabilityInferencer
+ stabilityInferencer,
+ featureFlags,
).lower(moduleFragment)
}
@@ -263,7 +266,8 @@
metrics,
idSignatureBuilder,
stabilityInferencer,
- decoysEnabled
+ decoysEnabled,
+ featureFlags,
).lower(moduleFragment)
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
index c445253..0788ec2 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
@@ -88,6 +88,10 @@
)
val TRACE_MARKERS_ENABLED_KEY =
CompilerConfigurationKey<Boolean>("Include composition trace markers in generated code")
+ val FEATURE_FLAGS =
+ CompilerConfigurationKey<List<String>>(
+ "A list of features to enable."
+ )
}
@OptIn(ExperimentalCompilerApi::class)
@@ -137,17 +141,29 @@
required = false,
allowMultipleOccurrences = false
)
+ val FEATURE_FLAG_OPTION = CliOption(
+ "featureFlag",
+ "<feature name>",
+ "The name of the feature to enable",
+ required = false,
+ allowMultipleOccurrences = true
+ )
val INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_OPTION = CliOption(
"intrinsicRemember",
"<true|false>",
- "Include source information in generated code",
+ "Include source information in generated code. Deprecated. Use ${
+ useFeatureFlagInsteadMessage(FeatureFlag.IntrinsicRemember)
+ }",
required = false,
allowMultipleOccurrences = false
)
val NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_OPTION = CliOption(
optionName = "nonSkippingGroupOptimization",
valueDescription = "<true|false>",
- description = "Remove groups around non-skipping composable functions",
+ description = "Remove groups around non-skipping composable functions. " +
+ "Deprecated. ${
+ useFeatureFlagInsteadMessage(FeatureFlag.OptimizeNonSkippingGroups)
+ }",
required = false,
allowMultipleOccurrences = false
)
@@ -168,14 +184,17 @@
val STRONG_SKIPPING_OPTION = CliOption(
"strongSkipping",
"<true|false>",
- "Enable strong skipping mode",
+ "Enable strong skipping mode. " +
+ "Deprecated. ${useFeatureFlagInsteadMessage(FeatureFlag.StrongSkipping)}",
required = false,
allowMultipleOccurrences = false
)
val EXPERIMENTAL_STRONG_SKIPPING_OPTION = CliOption(
"experimentalStrongSkipping",
"<true|false>",
- "Deprecated - Use strongSkipping instead",
+ "Deprecated. ${
+ useFeatureFlagInsteadMessage(FeatureFlag.StrongSkipping)
+ }",
required = false,
allowMultipleOccurrences = false
)
@@ -211,6 +230,7 @@
STRONG_SKIPPING_OPTION,
STABLE_CONFIG_PATH_OPTION,
TRACE_MARKERS_OPTION,
+ FEATURE_FLAG_OPTION,
)
override fun processOption(
@@ -242,14 +262,28 @@
ComposeConfiguration.REPORTS_DESTINATION_KEY,
value
)
- INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_OPTION -> configuration.put(
- ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
- value == "true"
- )
- NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_OPTION -> configuration.put(
- ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY,
- value == "true"
- )
+ INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_OPTION -> {
+ oldOptionDeprecationWarning(
+ configuration,
+ INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_OPTION,
+ FeatureFlag.IntrinsicRemember
+ )
+ configuration.put(
+ ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
+ value == "true"
+ )
+ }
+ NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_OPTION -> {
+ oldOptionDeprecationWarning(
+ configuration,
+ NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_OPTION,
+ FeatureFlag.OptimizeNonSkippingGroups
+ )
+ configuration.put(
+ ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY,
+ value == "true"
+ )
+ }
SUPPRESS_KOTLIN_VERSION_CHECK_ENABLED_OPTION -> configuration.put(
ComposeConfiguration.SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK,
value
@@ -259,21 +293,27 @@
value == "true"
)
EXPERIMENTAL_STRONG_SKIPPING_OPTION -> {
- val msgCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
- msgCollector?.report(
- CompilerMessageSeverity.WARNING,
- "${EXPERIMENTAL_STRONG_SKIPPING_OPTION.optionName} is deprecated." +
- " Use ${STRONG_SKIPPING_OPTION.optionName} instead."
+ oldOptionDeprecationWarning(
+ configuration,
+ EXPERIMENTAL_STRONG_SKIPPING_OPTION,
+ FeatureFlag.StrongSkipping
)
configuration.put(
ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY,
value == "true"
)
}
- STRONG_SKIPPING_OPTION -> configuration.put(
- ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY,
- value == "true"
- )
+ STRONG_SKIPPING_OPTION -> {
+ oldOptionDeprecationWarning(
+ configuration,
+ EXPERIMENTAL_STRONG_SKIPPING_OPTION,
+ FeatureFlag.StrongSkipping
+ )
+ configuration.put(
+ ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY,
+ value == "true"
+ )
+ }
STABLE_CONFIG_PATH_OPTION -> configuration.appendList(
ComposeConfiguration.STABILITY_CONFIG_PATH_KEY,
value
@@ -282,10 +322,215 @@
ComposeConfiguration.TRACE_MARKERS_ENABLED_KEY,
value == "true"
)
+ FEATURE_FLAG_OPTION -> {
+ validateFeatureFlag(configuration, value)
+ configuration.appendList(
+ ComposeConfiguration.FEATURE_FLAGS,
+ value
+ )
+ }
else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
}
}
+/**
+ * A list of features that can be enabled through the "featureFlags" option.
+ *
+ * Features should be added to this list if they are intended to eventually become the default
+ * behavior of the compiler. This is intended to allow progressive roll-out of a feature to
+ * facilitate coordinating the runtime and compiler changes. New features should be disabled
+ * by default until it is validated to be ready for production after testing with the corresponding
+ * changes needed in the runtime. Using this technique does not remove the need to feature detect
+ * for the version of runtime and is only intended to disable the feature even if the feature is
+ * detected in the runtime.
+ *
+ * If a feature default is `true` the feature is reported as known by the command-line processor
+ * but will generate a warning that the option is no longer necessary as it is the default. If
+ * the feature is not in this list a warning is produced instead of an error to facilitate moving
+ * compiler versions without having to always remove features unknown to older versions of the
+ * plugin.
+ *
+ * A feature flag enum value can be used in the transformers that derive from
+ * AbstractComposeLowering by using the FeatureFlag.enabled extension property. For example
+ * testing if StrongSkipping is enabled can be checked by checking
+ *
+ * FeatureFlag.StrongSkipping.enabled
+ *
+ * The `default` field is the source of truth for the default of the property. Turning it
+ * to `true` here will make it default on even if the value was previous enabled through
+ * a deprecated explicit option.
+ *
+ * A feature can be explicitly disabled by prefixing the feature name with "-" even if
+ * the feature is enabled by default.
+ *
+ * @param featureName The name of the feature that is used with featureFlags to enable or disable
+ * the feature.
+ * @param default True if the feature is enabled by default or false if it is not.
+ */
+enum class FeatureFlag(val featureName: String, val default: Boolean) {
+ StrongSkipping("StrongSkipping", default = false),
+ IntrinsicRemember("IntrinsicRemember", default = true),
+ OptimizeNonSkippingGroups("OptimizeNonSkippingGroups", default = false);
+
+ val disabledName get() = "-$featureName"
+ fun name(enabled: Boolean) = if (enabled) featureName else disabledName
+
+ companion object {
+ fun fromString(featureName: String): Pair<FeatureFlag?, Boolean> {
+ val (featureToSearch, enabled) = when {
+ featureName.startsWith("+") -> featureName.substring(1) to true
+ featureName.startsWith("-") -> featureName.substring(1) to false
+ else -> featureName to true
+ }
+ return FeatureFlag.values().firstOrNull {
+ featureToSearch.trim().compareTo(it.featureName, ignoreCase = true) == 0
+ } to enabled
+ }
+ }
+}
+
+class FeatureFlags(featureConfiguration: List<String> = emptyList()) {
+ private val setForCompatibility = mutableSetOf<FeatureFlag>()
+ private val duplicate = mutableSetOf<FeatureFlag>()
+ private val enabledFeatures = mutableSetOf<FeatureFlag>()
+ private val disabledFeatures = mutableSetOf<FeatureFlag>()
+
+ init {
+ processConfigurationList(featureConfiguration)
+ }
+
+ private fun enableFeature(feature: FeatureFlag) {
+ if (feature in disabledFeatures) {
+ duplicate.add(feature)
+ disabledFeatures.remove(feature)
+ }
+ enabledFeatures.add(feature)
+ }
+
+ private fun disableFeature(feature: FeatureFlag) {
+ if (feature in enabledFeatures) {
+ duplicate.add(feature)
+ enabledFeatures.remove(feature)
+ }
+ disabledFeatures.add(feature)
+ }
+
+ fun setFeature(feature: FeatureFlag, value: Boolean) {
+ if (feature.default != value) {
+ setForCompatibility.add(feature)
+ if (value) enableFeature(feature) else disableFeature(feature)
+ }
+ }
+
+ fun isEnabled(feature: FeatureFlag) = feature in enabledFeatures || (feature.default &&
+ feature !in disabledFeatures)
+
+ private fun processConfigurationList(featuresNames: List<String>) {
+ for (featureName in featuresNames) {
+ val (feature, enabled) = FeatureFlag.fromString(featureName)
+ if (feature != null) {
+ if (enabled) enableFeature(feature) else disableFeature(feature)
+ }
+ }
+ }
+
+ fun validateFeatureFlags(configuration: CompilerConfiguration) {
+ val msgCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
+ if (msgCollector != null) {
+ val reported = mutableSetOf<FeatureFlag>()
+ fun report(feature: FeatureFlag, message: String) {
+ if (feature !in reported) {
+ reported.add(feature)
+ msgCollector.report(
+ CompilerMessageSeverity.WARNING,
+ message
+ )
+ }
+ }
+ val configured = enabledFeatures + disabledFeatures
+ val oldAndNewSet = setForCompatibility.intersect(configured)
+ for (feature in oldAndNewSet) {
+ report(
+ feature,
+ "Feature ${featureFlagName()}=${feature.featureName} is using featureFlags " +
+ "and is set using the deprecated option. It is recommended to only use " +
+ "featureFlag. ${currentState(feature)}"
+ )
+ }
+ for (feature in duplicate) {
+ if (feature !in reported) {
+ report(
+ feature,
+ "Feature ${featureFlagName()}=${feature.featureName} was both enabled " +
+ "and disabled. ${currentState(feature)}"
+ )
+ }
+ }
+ for (feature in disabledFeatures) {
+ if (!feature.default) {
+ report(
+ feature,
+ "The feature ${featureFlagName()}=${feature.featureName} is disabled " +
+ "by default and specifying this option explicitly is not necessary."
+ )
+ }
+ }
+ for (feature in enabledFeatures) {
+ if (feature.default) {
+ report(
+ feature,
+ "The feature ${featureFlagName()}=${feature.featureName} is enabled " +
+ "by default and specifying this option explicitly is not necessary."
+ )
+ }
+ }
+ }
+ }
+
+ private fun currentState(feature: FeatureFlag): String =
+ "With the given options set, the feature is ${
+ if (isEnabled(feature)) "enabled" else "disabled"
+ }"
+}
+
+fun featureFlagName() =
+ "plugin:${ComposeCommandLineProcessor.PLUGIN_ID}:${
+ ComposeCommandLineProcessor.FEATURE_FLAG_OPTION.optionName
+ }"
+
+fun useFeatureFlagInsteadMessage(feature: FeatureFlag) = "Use " +
+ "${featureFlagName()}=${feature.featureName} instead"
+
+fun oldOptionDeprecationWarning(
+ configuration: CompilerConfiguration,
+ oldOption: AbstractCliOption,
+ feature: FeatureFlag
+) {
+ val msgCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
+ msgCollector?.report(
+ CompilerMessageSeverity.WARNING,
+ "${oldOption.optionName} is deprecated. ${useFeatureFlagInsteadMessage(feature)}"
+ )
+}
+
+fun validateFeatureFlag(
+ configuration: CompilerConfiguration,
+ value: String
+) {
+ val (feature, enabled) = FeatureFlag.fromString(value)
+ if (feature == null || (feature.default == enabled)) {
+ val msgCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
+ if (msgCollector != null) {
+ if (feature == null) {
+ msgCollector.report(
+ CompilerMessageSeverity.WARNING,
+ "${featureFlagName()} contains an unrecognized feature name: $value."
+ )
+ }
+ }
+ }
+}
+
@Suppress("DEPRECATION") // CompilerPluginRegistrar does not expose project (or disposable) causing
// memory leaks, see: https://youtrack.jetbrains.com/issue/KT-60952
@OptIn(ExperimentalCompilerApi::class)
@@ -471,11 +716,11 @@
)
val intrinsicRememberEnabled = configuration.get(
ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
- true
+ FeatureFlag.IntrinsicRemember.default
)
val nonSkippingGroupOptimizationEnabled = configuration.get(
ComposeConfiguration.NON_SKIPPING_GROUP_OPTIMIZATION_ENABLED_KEY,
- false
+ FeatureFlag.OptimizeNonSkippingGroups.default
)
val decoysEnabled = configuration.getBoolean(
ComposeConfiguration.DECOYS_ENABLED_KEY,
@@ -496,7 +741,7 @@
val strongSkippingEnabled = configuration.get(
ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY,
- false
+ FeatureFlag.StrongSkipping.default
)
val stabilityConfigPaths = configuration.getList(
@@ -507,6 +752,22 @@
true
)
+ val featureFlags = FeatureFlags(
+ configuration.get(
+ ComposeConfiguration.FEATURE_FLAGS, emptyList()
+ )
+ )
+ featureFlags.validateFeatureFlags(configuration)
+
+ // Compatibility with older features configuration options
+ // New features should not create a explicit option
+ featureFlags.setFeature(FeatureFlag.IntrinsicRemember, intrinsicRememberEnabled)
+ featureFlags.setFeature(FeatureFlag.StrongSkipping, strongSkippingEnabled)
+ featureFlags.setFeature(
+ FeatureFlag.OptimizeNonSkippingGroups,
+ nonSkippingGroupOptimizationEnabled
+ )
+
val msgCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
val stableTypeMatchers = mutableSetOf<FqNameMatcher>()
@@ -534,17 +795,15 @@
generateFunctionKeyMetaClasses = generateFunctionKeyMetaClasses,
sourceInformationEnabled = sourceInformationEnabled,
traceMarkersEnabled = traceMarkersEnabled,
- intrinsicRememberEnabled = intrinsicRememberEnabled,
- nonSkippingGroupOptimizationEnabled = nonSkippingGroupOptimizationEnabled,
decoysEnabled = decoysEnabled,
metricsDestination = metricsDestination,
reportsDestination = reportsDestination,
validateIr = validateIr,
useK2 = useK2,
- strongSkippingEnabled = strongSkippingEnabled,
stableTypeMatchers = stableTypeMatchers,
moduleMetricsFactory = moduleMetricsFactory,
descriptorSerializerContext = descriptorSerializerContext,
+ featureFlags = featureFlags,
)
}
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
index 0d00d78..4d3f0fb 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
@@ -149,7 +149,7 @@
* The maven version string of this compiler. This string should be updated before/after every
* release.
*/
- const val compilerVersion: String = "1.5.12"
+ const val compilerVersion: String = "1.5.13"
private val minimumRuntimeVersion: String
get() = runtimeVersionToMavenVersionTable[minimumRuntimeVersionInt] ?: "unknown"
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
index 434e260..f7435dc 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
@@ -19,6 +19,8 @@
import androidx.compose.compiler.plugins.kotlin.ComposeCallableIds
import androidx.compose.compiler.plugins.kotlin.ComposeClassIds
import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
+import androidx.compose.compiler.plugins.kotlin.FeatureFlag
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.FunctionMetrics
import androidx.compose.compiler.plugins.kotlin.KtxNameConventions
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
@@ -146,6 +148,7 @@
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
+import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -155,7 +158,8 @@
val context: IrPluginContext,
val symbolRemapper: DeepCopySymbolRemapper,
val metrics: ModuleMetrics,
- val stabilityInferencer: StabilityInferencer
+ val stabilityInferencer: StabilityInferencer,
+ private val featureFlags: FeatureFlags,
) : IrElementTransformerVoid(), ModuleLoweringPass {
protected val builtIns = context.irBuiltIns
@@ -176,6 +180,12 @@
protected val composableIrClass: IrClass
get() = symbolRemapper.getReferencedClass(_composableIrClass.symbol).owner
+ protected val jvmSyntheticIrClass by guardedLazy {
+ context.referenceClass(
+ ClassId(StandardClassIds.BASE_JVM_PACKAGE, Name.identifier("JvmSynthetic"))
+ )!!.owner
+ }
+
fun referenceFunction(symbol: IrFunctionSymbol): IrFunctionSymbol {
return symbolRemapper.getReferencedFunction(symbol)
}
@@ -218,6 +228,8 @@
)
}
+ val FeatureFlag.enabled get() = featureFlags.isEnabled(this)
+
fun metricsFor(function: IrFunction): FunctionMetrics =
(function as? IrAttributeContainer)?.let {
context.irTrace[ComposeWritableSlices.FUNCTION_METRICS, it] ?: run {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ClassStabilityTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ClassStabilityTransformer.kt
index 19f9a04..9cd186c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ClassStabilityTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ClassStabilityTransformer.kt
@@ -17,6 +17,7 @@
package androidx.compose.compiler.plugins.kotlin.lower
import androidx.compose.compiler.plugins.kotlin.ComposeClassIds
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.Stability
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
@@ -65,8 +66,9 @@
symbolRemapper: DeepCopySymbolRemapper,
metrics: ModuleMetrics,
stabilityInferencer: StabilityInferencer,
- private val classStabilityInferredCollection: ClassStabilityInferredCollection? = null
-) : AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer),
+ private val classStabilityInferredCollection: ClassStabilityInferredCollection? = null,
+ featureFlags: FeatureFlags,
+) : AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer, featureFlags),
ClassLoweringPass,
ModuleLoweringPass {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index f3d15f5..5229a6c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -18,6 +18,8 @@
import androidx.compose.compiler.plugins.kotlin.ComposeCallableIds
import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
+import androidx.compose.compiler.plugins.kotlin.FeatureFlag
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.FunctionMetrics
import androidx.compose.compiler.plugins.kotlin.KtxNameConventions
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
@@ -28,6 +30,7 @@
import androidx.compose.compiler.plugins.kotlin.analysis.knownStable
import androidx.compose.compiler.plugins.kotlin.analysis.knownUnstable
import androidx.compose.compiler.plugins.kotlin.irTrace
+import androidx.compose.compiler.plugins.kotlin.lower.ComposerParamTransformer.ComposeDefaultValueStubOrigin
import androidx.compose.compiler.plugins.kotlin.lower.decoys.DecoyFqNames
import kotlin.math.abs
import kotlin.math.absoluteValue
@@ -459,11 +462,9 @@
stabilityInferencer: StabilityInferencer,
private val collectSourceInformation: Boolean,
private val traceMarkersEnabled: Boolean,
- private val intrinsicRememberEnabled: Boolean,
- private val nonSkippingGroupOptimizationEnabled: Boolean,
- private val strongSkippingEnabled: Boolean
+ featureFlags: FeatureFlags,
) :
- AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer),
+ AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer, featureFlags),
FileLoweringPass,
ModuleLoweringPass {
@@ -613,7 +614,7 @@
// Uses `rememberComposableLambda` as a indication that the runtime supports
// generating remember after call as it was added at the same time as the slot table was
// modified to support remember after call.
- nonSkippingGroupOptimizationEnabled && rememberComposableLambdaFunction != null
+ FeatureFlag.OptimizeNonSkippingGroups.enabled && rememberComposableLambdaFunction != null
}
private val IrType.arguments: List<IrTypeArgument>
@@ -698,6 +699,12 @@
val scope = currentFunctionScope
// if the function isn't composable, there's nothing to do
if (!scope.isComposable) return super.visitFunction(declaration)
+ if (declaration.origin == ComposeDefaultValueStubOrigin) {
+ // this is a synthetic function stub, don't touch the body, only remove the stub origin
+ declaration.origin = IrDeclarationOrigin.DEFINED
+ return declaration
+ }
+
val restartable = declaration.shouldBeRestartable()
val isLambda = declaration.isLambda()
@@ -1229,7 +1236,10 @@
// if there are unstable params, then we fence the whole expression with a check to
// see if any of the unstable params were the ones that were provided to the
// function. If they were, then we short-circuit and always execute
- if (!strongSkippingEnabled && hasAnyUnstableParams && defaultParam != null) {
+ if (
+ !FeatureFlag.StrongSkipping.enabled &&
+ hasAnyUnstableParams && defaultParam != null
+ ) {
shouldExecute = irOrOr(
defaultParam.irHasAnyProvidedAndUnstable(unstableMask),
shouldExecute
@@ -1443,7 +1453,12 @@
used = isUsed
)
- if (!strongSkippingEnabled && isUsed && isUnstable && isRequired) {
+ if (
+ !FeatureFlag.StrongSkipping.enabled &&
+ isUsed &&
+ isUnstable &&
+ isRequired
+ ) {
// if it is a used + unstable parameter with no default expression and we are
// not in strong skipping mode, the fn will _never_ skip
mightSkip = false
@@ -1471,7 +1486,7 @@
// this will only ever be true when mightSkip is false, but we put this
// branch here so that `dirty` gets smart cast in later branches
}
- !strongSkippingEnabled && isUnstable && defaultParam != null &&
+ !FeatureFlag.StrongSkipping.enabled && isUnstable && defaultParam != null &&
defaultValue != null -> {
// if it has a default parameter then the function can still potentially skip
skipPreamble.statements.add(
@@ -1484,7 +1499,7 @@
)
)
}
- strongSkippingEnabled || !isUnstable -> {
+ FeatureFlag.StrongSkipping.enabled || !isUnstable -> {
val defaultValueIsStatic = defaultExprIsStatic[slotIndex]
val callChanged = irCallChanged(stability, changedParam, slotIndex, param)
@@ -1506,7 +1521,7 @@
)
)
- val skipCondition = if (strongSkippingEnabled)
+ val skipCondition = if (FeatureFlag.StrongSkipping.enabled)
irIsUncertain(changedParam, slotIndex)
else
irIsUncertainAndStable(changedParam, slotIndex)
@@ -1665,7 +1680,7 @@
changedParam: IrChangedBitMaskValue,
slotIndex: Int,
param: IrValueDeclaration
- ) = if (strongSkippingEnabled && stability.isUncertain()) {
+ ) = if (FeatureFlag.StrongSkipping.enabled && stability.isUncertain()) {
irIfThenElse(
type = context.irBuiltIns.booleanType,
condition = irIsStable(changedParam, slotIndex),
@@ -2191,7 +2206,7 @@
private fun irChanged(
value: IrExpression,
compareInstanceForFunctionTypes: Boolean,
- compareInstanceForUnstableValues: Boolean = strongSkippingEnabled
+ compareInstanceForUnstableValues: Boolean = FeatureFlag.StrongSkipping.enabled
): IrExpression = irChanged(
irCurrentComposer(),
value,
@@ -2946,7 +2961,7 @@
private fun visitComposableCall(expression: IrCall): IrExpression {
return when (expression.symbol.owner.kotlinFqName) {
ComposeFqNames.remember -> {
- if (intrinsicRememberEnabled) {
+ if (FeatureFlag.IntrinsicRemember.enabled) {
visitRememberCall(expression)
} else {
visitNormalComposableCall(expression)
@@ -3532,7 +3547,7 @@
arguments.fastForEachIndexed { slot, argInfo ->
val stability = argInfo.stability
when {
- !strongSkippingEnabled && stability.knownUnstable() -> {
+ !FeatureFlag.StrongSkipping.enabled && stability.knownUnstable() -> {
bitMaskConstant = bitMaskConstant or StabilityBits.UNSTABLE.bitsForSlot(slot)
// If it is known to be unstable, there's no purpose in propagating any
// additional metadata _for this parameter_, but we still want to propagate
@@ -4561,7 +4576,7 @@
// we _only_ use this pattern for the slots where the body of the function
// actually uses that parameter, otherwise we pass in 0b000 which will transfer
// none of the bits to the rhs
- val lhsMask = if (strongSkippingEnabled) 0b001 else 0b101
+ val lhsMask = if (FeatureFlag.StrongSkipping.enabled) 0b001 else 0b101
val lhs = (start until end).fold(0) { mask, slot ->
if (usedParams[slot]) mask or bitsForSlot(lhsMask, slot) else mask
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTargetAnnotationsTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTargetAnnotationsTransformer.kt
index 305f3a6..7b55a1e 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTargetAnnotationsTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTargetAnnotationsTransformer.kt
@@ -18,6 +18,7 @@
import androidx.compose.compiler.plugins.kotlin.ComposeClassIds
import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.KtxNameConventions
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.ComposeWritableSlices
@@ -102,8 +103,15 @@
context: IrPluginContext,
symbolRemapper: ComposableSymbolRemapper,
metrics: ModuleMetrics,
- stabilityInferencer: StabilityInferencer
-) : AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer) {
+ stabilityInferencer: StabilityInferencer,
+ featureFlags: FeatureFlags,
+) : AbstractComposeLowering(
+ context,
+ symbolRemapper,
+ metrics,
+ stabilityInferencer,
+ featureFlags,
+) {
private val ComposableTargetClass = symbolRemapper.getReferencedClassOrNull(
getTopLevelClassOrNull(ComposeClassIds.ComposableTarget)
)
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
index 3d408cf..71c2626 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
@@ -18,6 +18,8 @@
import androidx.compose.compiler.plugins.kotlin.ComposeCallableIds
import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
+import androidx.compose.compiler.plugins.kotlin.FeatureFlag
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.ComposeWritableSlices
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
@@ -297,10 +299,8 @@
symbolRemapper: DeepCopySymbolRemapper,
metrics: ModuleMetrics,
stabilityInferencer: StabilityInferencer,
- private val strongSkippingModeEnabled: Boolean,
- private val intrinsicRememberEnabled: Boolean,
- private val nonSkippingGroupOptimizationEnabled: Boolean,
-) : AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer),
+ featureFlags: FeatureFlags,
+) : AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer, featureFlags),
ModuleLoweringPass {
@@ -346,7 +346,7 @@
// Uses `rememberComposableLambda` as a indication that the runtime supports
// generating remember after call as it was added at the same time as the slot table was
// modified to support remember after call.
- nonSkippingGroupOptimizationEnabled && rememberComposableLambdaFunction != null
+ FeatureFlag.OptimizeNonSkippingGroups.enabled && rememberComposableLambdaFunction != null
}
private fun getOrCreateComposableSingletonsClass(): IrClass {
@@ -583,7 +583,7 @@
captures.addAll(localCaptures)
}
- if (hasReceiver && (strongSkippingModeEnabled || receiverIsStable)) {
+ if (hasReceiver && (FeatureFlag.StrongSkipping.enabled || receiverIsStable)) {
// Save the receivers into a temporaries and memoize the function reference using
// the resulting temporaries
val builder = DeclarationIrBuilder(
@@ -971,7 +971,9 @@
functionContext.declaration.hasAnnotation(ComposeFqNames.DontMemoize) ||
expression.hasDontMemoizeAnnotation ||
captures.any {
- it.isVar() || (!it.isStable() && !strongSkippingModeEnabled) || it.isInlinedLambda()
+ it.isVar() ||
+ (!it.isStable() && !FeatureFlag.StrongSkipping.enabled) ||
+ it.isInlinedLambda()
}
) {
metrics.recordLambda(
@@ -989,7 +991,7 @@
singleton = false
)
- return if (!intrinsicRememberEnabled) {
+ return if (!FeatureFlag.IntrinsicRemember.enabled) {
// generate cache directly only if strong skipping is enabled without intrinsic remember
// otherwise, generated memoization won't benefit from capturing changed values
irCache(captureExpressions, expression)
@@ -1026,7 +1028,7 @@
calculation
)
- return if (nonSkippingGroupOptimizationEnabled) {
+ return if (useNonSkippingGroupOptimization) {
cache
} else {
// If the non-skipping group optimization is disabled then we need to wrap
@@ -1120,7 +1122,7 @@
value,
inferredStable = false,
compareInstanceForFunctionTypes = false,
- compareInstanceForUnstableValues = strongSkippingModeEnabled
+ compareInstanceForUnstableValues = FeatureFlag.StrongSkipping.enabled
)
private fun IrValueDeclaration.isVar(): Boolean =
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
index c71ec1b..33ef235 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
@@ -16,6 +16,7 @@
package androidx.compose.compiler.plugins.kotlin.lower
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.KtxNameConventions
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
@@ -31,13 +32,17 @@
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
+import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
+import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
+import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrLocalDelegatedProperty
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.declarations.copyAttributes
+import org.jetbrains.kotlin.ir.declarations.createBlockBody
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -58,14 +63,18 @@
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.createType
+import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.isMarkedNullable
+import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
+import org.jetbrains.kotlin.ir.util.addChild
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.copyTo
import org.jetbrains.kotlin.ir.util.copyTypeParametersFrom
+import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.explicitParameters
import org.jetbrains.kotlin.ir.util.functions
@@ -92,8 +101,9 @@
stabilityInferencer: StabilityInferencer,
private val decoysEnabled: Boolean,
metrics: ModuleMetrics,
+ featureFlags: FeatureFlags,
) :
- AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer),
+ AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer, featureFlags),
ModuleLoweringPass {
/**
@@ -421,15 +431,9 @@
// anonymous parameters has an issue where it is not safe to dex, so we sanitize
// the names here to ensure that dex is always safe.
val newName = dexSafeName(param.name)
-
- val newType = defaultParameterType(
- param,
- this.hasDefaultExpressionDefinedForValueParameter(param.index)
- ).remapTypeParameters()
param.copyTo(
fn,
name = newName,
- type = newType,
isAssignable = param.defaultValue != null,
defaultValue = param.defaultValue?.copyWithNewTypeParams(
source = this, target = fn
@@ -563,6 +567,25 @@
}
}
+ fn.makeStubForDefaultValuesIfNeeded()?.also {
+ when (val parent = fn.parent) {
+ is IrClass -> parent.addChild(it)
+ is IrFile -> parent.addChild(it)
+ else -> {
+ // ignore
+ }
+ }
+ }
+
+ // update parameter types so they are ready to accept the default values
+ fn.valueParameters.fastForEach { param ->
+ val newType = defaultParameterType(
+ param,
+ fn.hasDefaultExpressionDefinedForValueParameter(param.index)
+ )
+ param.type = newType
+ }
+
inlineLambdaInfo.scan(fn)
fn.transformChildrenVoid(object : IrElementTransformerVoid() {
@@ -650,7 +673,7 @@
/**
* With klibs, composable functions are always deserialized from IR instead of being restored
- * into stubs.
+ *
* In this case, we need to avoid transforming those functions twice (because synthetic
* parameters are being added). We know however, that all the other modules were compiled
* before, so if the function comes from other [IrModuleFragment], we must skip it.
@@ -663,4 +686,91 @@
decoysEnabled && valueParameters.firstOrNull {
it.name == KtxNameConventions.COMPOSER_PARAMETER
} != null
+
+ /**
+ * Creates stubs for @Composable function with value class parameters that have a default and
+ * are wrapping a non-primitive instance.
+ * Before Compose compiler 1.5.12, not specifying such parameter resulted in a nullptr exception
+ * (b/330655412) at runtime, caused by Kotlin compiler inserting checkParameterNotNull.
+ *
+ * Converting such parameters to be nullable caused a binary compatibility issue because
+ * nullability changed the value class mangle on a function signature. This stub creates a
+ * binary compatible function to support old compilers while redirecting to a new function.
+ */
+ private fun IrSimpleFunction.makeStubForDefaultValuesIfNeeded(): IrSimpleFunction? {
+ if (!visibility.isPublicAPI && !hasAnnotation(PublishedApiFqName)) {
+ return null
+ }
+
+ if (!hasComposableAnnotation()) {
+ return null
+ }
+
+ var makeStub = false
+ for (i in valueParameters.indices) {
+ val param = valueParameters[i]
+ if (
+ hasDefaultExpressionDefinedForValueParameter(i) &&
+ param.type.isInlineClassType() &&
+ !param.type.isNullable() &&
+ param.type.unboxInlineClass().let {
+ !it.isPrimitiveType() && !it.isNullable()
+ }
+ ) {
+ makeStub = true
+ break
+ }
+ }
+
+ if (!makeStub) {
+ return null
+ }
+
+ val source = this
+ val copy = source.deepCopyWithSymbols(parent)
+ copy.valueParameters.fastForEach {
+ it.defaultValue = null
+ }
+ copy.origin = ComposeDefaultValueStubOrigin
+ copy.annotations += IrConstructorCallImpl(
+ UNDEFINED_OFFSET,
+ UNDEFINED_OFFSET,
+ jvmSyntheticIrClass.defaultType,
+ jvmSyntheticIrClass.primaryConstructor!!.symbol,
+ 0,
+ 0,
+ 0,
+ )
+ copy.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
+ statements.add(
+ IrReturnImpl(
+ UNDEFINED_OFFSET,
+ UNDEFINED_OFFSET,
+ copy.returnType,
+ copy.symbol,
+ irCall(source).apply {
+ dispatchReceiver = copy.dispatchReceiverParameter?.let { irGet(it) }
+ extensionReceiver = copy.extensionReceiverParameter?.let { irGet(it) }
+ copy.typeParameters.fastForEachIndexed { index, param ->
+ putTypeArgument(index, param.defaultType)
+ }
+ copy.valueParameters.fastForEachIndexed { index, param ->
+ putValueArgument(index, irGet(param))
+ }
+ }
+ )
+ )
+ }
+
+ transformedFunctions[copy] = copy
+ transformedFunctionSet += copy
+
+ return copy
+ }
+
+ private val PublishedApiFqName = StandardClassIds.Annotations.PublishedApi.asSingleFqName()
+ object ComposeDefaultValueStubOrigin : IrDeclarationOriginImpl(
+ name = "ComposeDefaultValueStubOrigin",
+ isSynthetic = true
+ )
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DurableFunctionKeyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DurableFunctionKeyTransformer.kt
index 5dee036..75c3505 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DurableFunctionKeyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DurableFunctionKeyTransformer.kt
@@ -17,6 +17,7 @@
package androidx.compose.compiler.plugins.kotlin.lower
import androidx.compose.compiler.plugins.kotlin.ComposeClassIds
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.ComposeWritableSlices.DURABLE_FUNCTION_KEY
import androidx.compose.compiler.plugins.kotlin.analysis.ComposeWritableSlices.DURABLE_FUNCTION_KEYS
@@ -104,13 +105,15 @@
context: IrPluginContext,
symbolRemapper: DeepCopySymbolRemapper,
metrics: ModuleMetrics,
- stabilityInferencer: StabilityInferencer
+ stabilityInferencer: StabilityInferencer,
+ featureFlags: FeatureFlags,
) : DurableKeyTransformer(
DurableKeyVisitor(),
context,
symbolRemapper,
stabilityInferencer,
- metrics
+ metrics,
+ featureFlags,
) {
fun removeKeyMetaClasses(moduleFragment: IrModuleFragment) {
moduleFragment.transformChildrenVoid(object : IrElementTransformerVoid() {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DurableKeyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DurableKeyTransformer.kt
index 23d6edc..7936c3c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DurableKeyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/DurableKeyTransformer.kt
@@ -16,6 +16,7 @@
package androidx.compose.compiler.plugins.kotlin.lower
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
@@ -73,8 +74,9 @@
symbolRemapper: DeepCopySymbolRemapper,
stabilityInferencer: StabilityInferencer,
metrics: ModuleMetrics,
-) :
- AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer),
+ featureFlags: FeatureFlags,
+ ) :
+ AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer, featureFlags),
ModuleLoweringPass {
override fun lower(module: IrModuleFragment) {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/KlibAssignableParamTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/KlibAssignableParamTransformer.kt
index 2b1b313..d16f9a3 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/KlibAssignableParamTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/KlibAssignableParamTransformer.kt
@@ -16,6 +16,7 @@
package androidx.compose.compiler.plugins.kotlin.lower
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
@@ -62,8 +63,14 @@
symbolRemapper: DeepCopySymbolRemapper,
metrics: ModuleMetrics,
stabilityInferencer: StabilityInferencer,
-) : AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer),
- ModuleLoweringPass {
+ featureFlags: FeatureFlags,
+) : AbstractComposeLowering(
+ context,
+ symbolRemapper,
+ metrics,
+ stabilityInferencer,
+ featureFlags
+), ModuleLoweringPass {
override fun lower(module: IrModuleFragment) {
module.transformChildrenVoid(this)
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/LiveLiteralTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/LiveLiteralTransformer.kt
index d027eea..a455c925 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/LiveLiteralTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/LiveLiteralTransformer.kt
@@ -18,6 +18,7 @@
import androidx.compose.compiler.plugins.kotlin.ComposeCallableIds
import androidx.compose.compiler.plugins.kotlin.ComposeClassIds
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
@@ -164,9 +165,10 @@
context: IrPluginContext,
symbolRemapper: DeepCopySymbolRemapper,
metrics: ModuleMetrics,
- stabilityInferencer: StabilityInferencer
+ stabilityInferencer: StabilityInferencer,
+ featureFlags: FeatureFlags,
) :
- AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer),
+ AbstractComposeLowering(context, symbolRemapper, metrics, stabilityInferencer, featureFlags),
ModuleLoweringPass {
override fun lower(module: IrModuleFragment) {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/WrapJsComposableLambdaLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/WrapJsComposableLambdaLowering.kt
index af87015..3b527cc 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/WrapJsComposableLambdaLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/WrapJsComposableLambdaLowering.kt
@@ -18,6 +18,7 @@
import androidx.compose.compiler.plugins.kotlin.ComposeCallableIds
import androidx.compose.compiler.plugins.kotlin.ComposeClassIds
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import androidx.compose.compiler.plugins.kotlin.lower.decoys.CreateDecoysTransformer
@@ -86,16 +87,18 @@
metrics: ModuleMetrics,
signatureBuilder: IdSignatureSerializer?,
stabilityInferencer: StabilityInferencer,
- private val decoysEnabled: Boolean
+ private val decoysEnabled: Boolean,
+ featureFlags: FeatureFlags,
) : AbstractComposeLowering(
context,
symbolRemapper,
metrics,
stabilityInferencer,
+ featureFlags,
) {
private val rememberFunSymbol by lazy {
val composerParamTransformer = ComposerParamTransformer(
- context, symbolRemapper, stabilityInferencer, decoysEnabled, metrics
+ context, symbolRemapper, stabilityInferencer, decoysEnabled, metrics, featureFlags
)
symbolRemapper.getReferencedSimpleFunction(
getTopLevelFunctions(ComposeCallableIds.remember).map { it.owner }.first {
@@ -108,7 +111,12 @@
// If a module didn't have any explicit remember calls,
// so `fun remember` wasn't transformed yet, then we have to transform it now.
val createDecoysTransformer = CreateDecoysTransformer(
- context, symbolRemapper, signatureBuilder, stabilityInferencer, metrics
+ context,
+ symbolRemapper,
+ signatureBuilder,
+ stabilityInferencer,
+ metrics,
+ featureFlags,
)
with(createDecoysTransformer) {
if (!it.isDecoy()) {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/AbstractDecoysLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/AbstractDecoysLowering.kt
index 171c6f4..bfb4453 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/AbstractDecoysLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/AbstractDecoysLowering.kt
@@ -16,6 +16,7 @@
package androidx.compose.compiler.plugins.kotlin.lower.decoys
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import androidx.compose.compiler.plugins.kotlin.lower.AbstractComposeLowering
@@ -39,11 +40,13 @@
metrics: ModuleMetrics,
stabilityInferencer: StabilityInferencer,
override val signatureBuilder: IdSignatureSerializer,
+ featureFlags: FeatureFlags,
) : AbstractComposeLowering(
context = pluginContext,
symbolRemapper = symbolRemapper,
metrics = metrics,
- stabilityInferencer = stabilityInferencer
+ stabilityInferencer = stabilityInferencer,
+ featureFlags = featureFlags
), DecoyTransformBase {
override fun visitFile(declaration: IrFile): IrFile {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/CreateDecoysTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/CreateDecoysTransformer.kt
index df2db0c..845a671 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/CreateDecoysTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/CreateDecoysTransformer.kt
@@ -16,6 +16,7 @@
package androidx.compose.compiler.plugins.kotlin.lower.decoys
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import androidx.compose.compiler.plugins.kotlin.lower.ModuleLoweringPass
@@ -84,12 +85,14 @@
signatureBuilder: IdSignatureSerializer,
stabilityInferencer: StabilityInferencer,
metrics: ModuleMetrics,
+ featureFlags: FeatureFlags,
) : AbstractDecoysLowering(
pluginContext = pluginContext,
symbolRemapper = symbolRemapper,
metrics = metrics,
stabilityInferencer = stabilityInferencer,
- signatureBuilder = signatureBuilder
+ signatureBuilder = signatureBuilder,
+ featureFlags = featureFlags
), ModuleLoweringPass {
private val originalFunctions: MutableMap<IrFunction, IrDeclarationParent> = mutableMapOf()
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt
index c73f49b..037b408 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/DecoyTransformBase.kt
@@ -40,6 +40,7 @@
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeArgument
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
+import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.SymbolRenamer
@@ -206,7 +207,17 @@
source: IrFunction,
target: IrFunction
): T {
- return deepCopyWithSymbols(target) { symbolRemapper, typeRemapper ->
+ val typeParamsAwareSymbolRemapper = object : DeepCopySymbolRemapper() {
+ init {
+ for ((orig, new) in source.typeParameters.zip(target.typeParameters)) {
+ typeParameters[orig.symbol] = new.symbol
+ }
+ }
+ }
+ return deepCopyWithSymbols(
+ target,
+ typeParamsAwareSymbolRemapper
+ ) { symbolRemapper, typeRemapper ->
val typeParamRemapper = object : TypeRemapper by typeRemapper {
override fun remapType(type: IrType): IrType {
return typeRemapper.remapType(type.remapTypeParameters(source, target))
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/RecordDecoySignaturesTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/RecordDecoySignaturesTransformer.kt
index 6b40aaa..428611e 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/RecordDecoySignaturesTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/RecordDecoySignaturesTransformer.kt
@@ -18,6 +18,7 @@
package androidx.compose.compiler.plugins.kotlin.lower.decoys
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import androidx.compose.compiler.plugins.kotlin.lower.ModuleLoweringPass
@@ -44,13 +45,15 @@
override val signatureBuilder: IdSignatureSerializer,
metrics: ModuleMetrics,
val mangler: KotlinMangler.IrMangler,
- stabilityInferencer: StabilityInferencer
+ stabilityInferencer: StabilityInferencer,
+ featureFlags: FeatureFlags,
) : AbstractDecoysLowering(
pluginContext = pluginContext,
symbolRemapper = symbolRemapper,
metrics = metrics,
signatureBuilder = signatureBuilder,
- stabilityInferencer = stabilityInferencer
+ stabilityInferencer = stabilityInferencer,
+ featureFlags = featureFlags,
), ModuleLoweringPass {
override fun lower(module: IrModuleFragment) {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/SubstituteDecoyCallsTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/SubstituteDecoyCallsTransformer.kt
index 6f30836..969a439 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/SubstituteDecoyCallsTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/decoys/SubstituteDecoyCallsTransformer.kt
@@ -16,6 +16,7 @@
package androidx.compose.compiler.plugins.kotlin.lower.decoys
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import androidx.compose.compiler.plugins.kotlin.lower.ComposerParamTransformer
@@ -57,15 +58,22 @@
signatureBuilder: IdSignatureSerializer,
stabilityInferencer: StabilityInferencer,
metrics: ModuleMetrics,
+ featureFlags: FeatureFlags,
) : AbstractDecoysLowering(
pluginContext = pluginContext,
symbolRemapper = symbolRemapper,
metrics = metrics,
stabilityInferencer = stabilityInferencer,
- signatureBuilder = signatureBuilder
+ signatureBuilder = signatureBuilder,
+ featureFlags = featureFlags,
), ModuleLoweringPass {
private val decoysTransformer = CreateDecoysTransformer(
- pluginContext, symbolRemapper, signatureBuilder, stabilityInferencer, metrics
+ pluginContext,
+ symbolRemapper,
+ signatureBuilder,
+ stabilityInferencer,
+ metrics,
+ featureFlags,
)
private val lazyDeclarationsCache = mutableMapOf<IrFunctionSymbol, IrFunction>()
@@ -238,7 +246,8 @@
private val addComposerParameterInplace = object : IrElementTransformerVoid() {
private val composerParamTransformer = ComposerParamTransformer(
- context, symbolRemapper, stabilityInferencer, true, metrics
+ context, symbolRemapper, stabilityInferencer, true, metrics, featureFlags
+
)
override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
return composerParamTransformer.visitSimpleFunction(declaration)
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/AddHiddenFromObjCLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/AddHiddenFromObjCLowering.kt
index d749568..0ef8a25 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/AddHiddenFromObjCLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/AddHiddenFromObjCLowering.kt
@@ -16,6 +16,7 @@
package androidx.compose.compiler.plugins.kotlin.lower.hiddenfromobjc
+import androidx.compose.compiler.plugins.kotlin.FeatureFlags
import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import androidx.compose.compiler.plugins.kotlin.lower.AbstractComposeLowering
@@ -25,6 +26,7 @@
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrStatement
+import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
@@ -41,20 +43,29 @@
/**
* AddHiddenFromObjCLowering looks for functions and properties with @Composable types and
* adds the `kotlin.native.HiddenFromObjC` annotation to them.
- * @see https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native/-hidden-from-obj-c/
+ * [docs](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native/-hidden-from-obj-c/)
*/
class AddHiddenFromObjCLowering(
private val pluginContext: IrPluginContext,
symbolRemapper: ComposableSymbolRemapper,
metrics: ModuleMetrics,
- private val hideFromObjCDeclarationsSet: HideFromObjCDeclarationsSet,
+ private val hideFromObjCDeclarationsSet: HideFromObjCDeclarationsSet?,
stabilityInferencer: StabilityInferencer,
-) : AbstractComposeLowering(pluginContext, symbolRemapper, metrics, stabilityInferencer) {
+ featureFlags: FeatureFlags,
+) : AbstractComposeLowering(
+ pluginContext,
+ symbolRemapper,
+ metrics,
+ stabilityInferencer,
+ featureFlags
+) {
private val hiddenFromObjCAnnotation: IrClassSymbol by lazy {
getTopLevelClass(ClassId.fromString("kotlin/native/HiddenFromObjC"))
}
+ private var currentShouldAnnotateClass = false
+
override fun lower(module: IrModuleFragment) {
require(context.platform.isNative()) {
"AddHiddenFromObjCLowering is expected to run only for k/native. " +
@@ -63,6 +74,27 @@
module.transformChildrenVoid(this)
}
+ /** `visitClass` is only needed until [issue](https://youtrack.jetbrains.com/issue/KT-65288/) fix
+ * after the issue is resolved, `visitClass` could be removed entirely
+ */
+ override fun visitClass(declaration: IrClass): IrStatement {
+ val previousShouldAnnotateClass = currentShouldAnnotateClass
+ currentShouldAnnotateClass = false
+
+ val cls = super.visitClass(declaration) as IrClass
+
+ // We see an issue only with data classes containing something Composable.
+ // Adding an annotation to all classes makes the FirNativeHiddenFromObjCInheritanceChecker (kotlin) complain.
+ // data classes can't be open, so it should work.
+ if (currentShouldAnnotateClass && cls.isData) {
+ cls.addHiddenFromObjCAnnotation()
+ hideFromObjCDeclarationsSet?.add(cls)
+ }
+
+ currentShouldAnnotateClass = previousShouldAnnotateClass
+ return cls
+ }
+
override fun visitFunction(declaration: IrFunction): IrStatement {
val f = super.visitFunction(declaration) as IrFunction
if (f.isLocal ||
@@ -72,7 +104,8 @@
if (f.hasComposableAnnotation() || f.needsComposableRemapping()) {
f.addHiddenFromObjCAnnotation()
- hideFromObjCDeclarationsSet.add(f)
+ hideFromObjCDeclarationsSet?.add(f)
+ currentShouldAnnotateClass = true
}
return f
@@ -88,7 +121,8 @@
if (shouldAdd) {
p.addHiddenFromObjCAnnotation()
- hideFromObjCDeclarationsSet.add(p)
+ hideFromObjCDeclarationsSet?.add(p)
+ currentShouldAnnotateClass = true
}
return p
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/AddHiddenFromObjCSerializationPlugin.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/AddHiddenFromObjCSerializationPlugin.kt
index 62e06a8..86b897f 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/AddHiddenFromObjCSerializationPlugin.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/AddHiddenFromObjCSerializationPlugin.kt
@@ -16,6 +16,7 @@
package androidx.compose.compiler.plugins.kotlin.lower.hiddenfromobjc
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
@@ -47,6 +48,20 @@
id = extension.stringTable.getQualifiedClassNameIndex(annotationToAdd)
}.build()
+ override fun afterClass(
+ descriptor: ClassDescriptor,
+ proto: ProtoBuf.Class.Builder,
+ versionRequirementTable: MutableVersionRequirementTable,
+ childSerializer: DescriptorSerializer,
+ extension: SerializerExtension
+ ) {
+ if (descriptor in hideFromObjCDeclarationsSet) {
+ val annotationProto = createAnnotationProto(extension)
+ proto.addExtension(KlibMetadataSerializerProtocol.classAnnotation, annotationProto)
+ proto.flags = proto.flags or hasAnnotationFlag
+ }
+ }
+
override fun afterConstructor(
descriptor: ConstructorDescriptor,
proto: ProtoBuf.Constructor.Builder,
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/HideFromObjCDeclarationsSet.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/HideFromObjCDeclarationsSet.kt
index ec2b064..5495671 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/HideFromObjCDeclarationsSet.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/hiddenfromobjc/HideFromObjCDeclarationsSet.kt
@@ -18,6 +18,7 @@
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
+import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.name.FqName
@@ -51,6 +52,10 @@
set.add(property.descriptor.fqNameSafe)
}
+ fun add(cls: IrClass) {
+ set.add(cls.descriptor.fqNameSafe)
+ }
+
operator fun contains(item: DeclarationDescriptor): Boolean {
return set.contains(item.fqNameSafe)
}
diff --git a/compose/compiler/design/strong-skipping.md b/compose/compiler/design/strong-skipping.md
index 7c26b69..6b0b5f6 100644
--- a/compose/compiler/design/strong-skipping.md
+++ b/compose/compiler/design/strong-skipping.md
@@ -95,7 +95,7 @@
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>() {
compilerOptions.freeCompilerArgs.addAll(
"-P",
- "plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true",
+ "plugin:androidx.compose.compiler.plugins.kotlin:featureFlag=StrongSkipping",
)
}
```
\ No newline at end of file
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index 0dc6488..996864d 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -97,7 +97,7 @@
androidx {
name = "Compose Desktop"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
publish = Publish.SNAPSHOT_AND_RELEASE
inceptionYear = "2020"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/foundation/foundation-layout/build.gradle b/compose/foundation/foundation-layout/build.gradle
index 33f9f46..6bbd065 100644
--- a/compose/foundation/foundation-layout/build.gradle
+++ b/compose/foundation/foundation-layout/build.gradle
@@ -115,7 +115,7 @@
androidx {
name = "Compose Layouts"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Compose layout implementations"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/foundation/foundation/api/current.ignore b/compose/foundation/foundation/api/current.ignore
index 9d462540..c6ac07b 100644
--- a/compose/foundation/foundation/api/current.ignore
+++ b/compose/foundation/foundation/api/current.ignore
@@ -1,4 +1,6 @@
// Baseline format: 1.0
+AddedAbstractMethod: androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider#calculateSnapOffset(float):
+ Added method androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider.calculateSnapOffset(float)
AddedAbstractMethod: androidx.compose.foundation.lazy.grid.LazyGridItemScope#animateItem(androidx.compose.ui.Modifier, androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>, androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>, androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>):
Added method androidx.compose.foundation.lazy.grid.LazyGridItemScope.animateItem(androidx.compose.ui.Modifier,androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>,androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>,androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>)
AddedAbstractMethod: androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemScope#animateItem(androidx.compose.ui.Modifier, androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>, androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>, androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>):
@@ -15,10 +17,6 @@
Method androidx.compose.foundation.gestures.snapping.SnapFlingBehaviorKt.rememberSnapFlingBehavior has changed return type from androidx.compose.foundation.gestures.snapping.SnapFlingBehavior to androidx.compose.foundation.gestures.TargetedFlingBehavior
-ParameterNameChange: androidx.compose.foundation.gestures.snapping.SnapFlingBehavior#performFling(androidx.compose.foundation.gestures.ScrollScope, float, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>, kotlin.coroutines.Continuation<? super java.lang.Float>) parameter #2:
- Attempted to change parameter name from onSettlingDistanceUpdated to onRemainingDistanceUpdated in method androidx.compose.foundation.gestures.snapping.SnapFlingBehavior.performFling
-
-
RemovedClass: androidx.compose.foundation.lazy.layout.LazyLayoutItemProviderKt:
Removed class androidx.compose.foundation.lazy.layout.LazyLayoutItemProviderKt
RemovedClass: androidx.compose.foundation.relocation.BringIntoViewResponderKt:
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index e026507..9a75490 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -636,25 +636,27 @@
public final class LazyGridSnapLayoutInfoProviderKt {
method public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
}
public final class LazyListSnapLayoutInfoProviderKt {
method public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
- method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
}
- public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.TargetedFlingBehavior {
- ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
- method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onRemainingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.TargetedFlingBehavior {
+ ctor @Deprecated public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
+ method @Deprecated public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onRemainingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
}
public final class SnapFlingBehaviorKt {
method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.TargetedFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider);
+ method public static androidx.compose.foundation.gestures.TargetedFlingBehavior snapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
}
public interface SnapLayoutInfoProvider {
- method public default float calculateApproachOffset(float initialVelocity);
- method public float calculateSnappingOffset(float currentVelocity);
+ method public default float calculateApproachOffset(float velocity, float decayOffset);
+ method public float calculateSnapOffset(float velocity);
}
@androidx.compose.runtime.Stable public interface SnapPosition {
@@ -1132,6 +1134,7 @@
public static sealed interface LazyLayoutPrefetchState.PrefetchHandle {
method public void cancel();
+ method public void markAsUrgent();
}
public final class Lazy_androidKt {
@@ -1156,8 +1159,7 @@
}
@SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public interface PrefetchRequestScope {
- method public long getAvailableTimeNanos();
- property public abstract long availableTimeNanos;
+ method public long availableTimeNanos();
}
@SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public interface PrefetchScheduler {
diff --git a/compose/foundation/foundation/api/restricted_current.ignore b/compose/foundation/foundation/api/restricted_current.ignore
index 9d462540..c6ac07b 100644
--- a/compose/foundation/foundation/api/restricted_current.ignore
+++ b/compose/foundation/foundation/api/restricted_current.ignore
@@ -1,4 +1,6 @@
// Baseline format: 1.0
+AddedAbstractMethod: androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider#calculateSnapOffset(float):
+ Added method androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider.calculateSnapOffset(float)
AddedAbstractMethod: androidx.compose.foundation.lazy.grid.LazyGridItemScope#animateItem(androidx.compose.ui.Modifier, androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>, androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>, androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>):
Added method androidx.compose.foundation.lazy.grid.LazyGridItemScope.animateItem(androidx.compose.ui.Modifier,androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>,androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>,androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>)
AddedAbstractMethod: androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridItemScope#animateItem(androidx.compose.ui.Modifier, androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>, androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset>, androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>):
@@ -15,10 +17,6 @@
Method androidx.compose.foundation.gestures.snapping.SnapFlingBehaviorKt.rememberSnapFlingBehavior has changed return type from androidx.compose.foundation.gestures.snapping.SnapFlingBehavior to androidx.compose.foundation.gestures.TargetedFlingBehavior
-ParameterNameChange: androidx.compose.foundation.gestures.snapping.SnapFlingBehavior#performFling(androidx.compose.foundation.gestures.ScrollScope, float, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>, kotlin.coroutines.Continuation<? super java.lang.Float>) parameter #2:
- Attempted to change parameter name from onSettlingDistanceUpdated to onRemainingDistanceUpdated in method androidx.compose.foundation.gestures.snapping.SnapFlingBehavior.performFling
-
-
RemovedClass: androidx.compose.foundation.lazy.layout.LazyLayoutItemProviderKt:
Removed class androidx.compose.foundation.lazy.layout.LazyLayoutItemProviderKt
RemovedClass: androidx.compose.foundation.relocation.BringIntoViewResponderKt:
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index cc020da..e6fa4da3 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -638,25 +638,27 @@
public final class LazyGridSnapLayoutInfoProviderKt {
method public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
}
public final class LazyListSnapLayoutInfoProviderKt {
method public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
- method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
+ method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
}
- public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.TargetedFlingBehavior {
- ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
- method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onRemainingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.TargetedFlingBehavior {
+ ctor @Deprecated public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
+ method @Deprecated public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onRemainingDistanceUpdated, kotlin.coroutines.Continuation<? super java.lang.Float>);
}
public final class SnapFlingBehaviorKt {
method @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.TargetedFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider);
+ method public static androidx.compose.foundation.gestures.TargetedFlingBehavior snapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decayAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
}
public interface SnapLayoutInfoProvider {
- method public default float calculateApproachOffset(float initialVelocity);
- method public float calculateSnappingOffset(float currentVelocity);
+ method public default float calculateApproachOffset(float velocity, float decayOffset);
+ method public float calculateSnapOffset(float velocity);
}
@androidx.compose.runtime.Stable public interface SnapPosition {
@@ -1134,6 +1136,7 @@
public static sealed interface LazyLayoutPrefetchState.PrefetchHandle {
method public void cancel();
+ method public void markAsUrgent();
}
public final class Lazy_androidKt {
@@ -1158,8 +1161,7 @@
}
@SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public interface PrefetchRequestScope {
- method public long getAvailableTimeNanos();
- property public abstract long availableTimeNanos;
+ method public long availableTimeNanos();
}
@SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public interface PrefetchScheduler {
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/pager/PagerScrollingBenchmark.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/pager/PagerScrollingBenchmark.kt
index eba73c1..9987d83 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/pager/PagerScrollingBenchmark.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/pager/PagerScrollingBenchmark.kt
@@ -284,18 +284,15 @@
}
}
-@OptIn(ExperimentalFoundationApi::class)
val NoOpInfoProvider = object : SnapLayoutInfoProvider {
- override fun calculateApproachOffset(initialVelocity: Float): Float {
- return 0f
- }
+ override fun calculateApproachOffset(velocity: Float, decayOffset: Float): Float =
+ 0.0f
- override fun calculateSnappingOffset(currentVelocity: Float): Float {
+ override fun calculateSnapOffset(velocity: Float): Float {
return 0f
}
}
-@OptIn(ExperimentalFoundationApi::class)
val VerticalPagerContent: @Composable PagerRemeasureTestCase.(
state: PagerState,
useKeys: Boolean,
@@ -322,7 +319,6 @@
}
}
-@OptIn(ExperimentalFoundationApi::class)
val HorizontalPagerContent: @Composable PagerRemeasureTestCase.(
state: PagerState,
useKeys: Boolean,
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/BasicTextField2ToggleTextBenchmark.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/BasicTextField2ToggleTextBenchmark.kt
new file mode 100644
index 0000000..bb2d93c
--- /dev/null
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/BasicTextField2ToggleTextBenchmark.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.benchmark.text
+
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkFirstCompose
+import androidx.compose.testutils.benchmark.benchmarkFirstDraw
+import androidx.compose.testutils.benchmark.benchmarkFirstLayout
+import androidx.compose.testutils.benchmark.benchmarkFirstMeasure
+import androidx.compose.testutils.benchmark.benchmarkLayoutPerf
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkDraw
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkLayout
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkMeasure
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkRecompose
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.text.benchmark.TextBenchmarkTestRule
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.test.filters.SmallTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@SmallTest
+@RunWith(Parameterized::class)
+class BasicTextField2ToggleTextBenchmark(
+ private val textLength: Int
+) {
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "length={0}")
+ fun initParameters(): Array<Any> = arrayOf(32, 512).filterForCi()
+ }
+
+ private val textBenchmarkRule = TextBenchmarkTestRule()
+ private val benchmarkRule = ComposeBenchmarkRule()
+
+ @get:Rule
+ val testRule = RuleChain
+ .outerRule(textBenchmarkRule)
+ .around(benchmarkRule)
+
+ private val width = textBenchmarkRule.widthDp.dp
+ private val fontSize = textBenchmarkRule.fontSizeSp.sp
+
+ private val caseFactory = {
+ textBenchmarkRule.generator { generator ->
+ BasicTextField2ToggleTextTestCase(
+ textGenerator = generator,
+ textLength = textLength,
+ textNumber = textBenchmarkRule.repeatTimes,
+ width = width,
+ fontSize = fontSize
+ )
+ }
+ }
+
+ /**
+ * Measure the time taken to compose a [BasicTextField] composable from scratch with the
+ * given input. This is the time taken to call the [BasicTextField] composable function.
+ */
+ @Test
+ fun first_compose() {
+ benchmarkRule.benchmarkFirstCompose(caseFactory)
+ }
+
+ /**
+ * Measure the time taken by the first time measure the [BasicTextField] composable with the
+ * given input. This is mainly the time used to measure all the [Measurable]s in the
+ * [BasicTextField] composable.
+ */
+ @Test
+ fun first_measure() {
+ benchmarkRule.benchmarkFirstMeasure(caseFactory)
+ }
+
+ /**
+ * Measure the time taken by the first time layout the [BasicTextField] composable with the
+ * given input.
+ */
+ @Test
+ fun first_layout() {
+ benchmarkRule.benchmarkFirstLayout(caseFactory)
+ }
+
+ /**
+ * Measure the time taken by first time draw the [BasicTextField] composable with the given
+ * input.
+ */
+ @Test
+ fun first_draw() {
+ benchmarkRule.benchmarkFirstDraw(caseFactory)
+ }
+
+ /**
+ * Measure the time taken by layout the [BasicTextField] composable after the layout
+ * constrains changed. This is mainly the time used to re-measure and re-layout the composable.
+ */
+ @Test
+ fun layout() {
+ benchmarkRule.benchmarkLayoutPerf(caseFactory)
+ }
+
+ /**
+ * Measure the time taken to recompose the [BasicTextField] composable when text gets toggled.
+ */
+ @Test
+ fun toggleText_recompose() {
+ benchmarkRule.toggleStateBenchmarkRecompose(caseFactory, requireRecomposition = false)
+ }
+
+ /**
+ * Measure the time taken to measure the [BasicTextField] composable when text gets toggled.
+ */
+ @Test
+ fun toggleText_measure() {
+ benchmarkRule.toggleStateBenchmarkMeasure(
+ caseFactory = caseFactory,
+ toggleCausesRecompose = false,
+ assertOneRecomposition = false
+ )
+ }
+
+ /**
+ * Measure the time taken to layout the [BasicTextField] composable when text gets toggled.
+ */
+ @Test
+ fun toggleText_layout() {
+ benchmarkRule.toggleStateBenchmarkLayout(
+ caseFactory = caseFactory,
+ assertOneRecomposition = false,
+ toggleCausesRecompose = false
+ )
+ }
+
+ /**
+ * Measure the time taken to draw the [BasicTextField] composable when text gets toggled.
+ */
+ @Test
+ fun toggleText_draw() {
+ benchmarkRule.toggleStateBenchmarkDraw(
+ caseFactory = caseFactory,
+ toggleCausesRecompose = false,
+ assertOneRecomposition = false
+ )
+ }
+}
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/BasicTextField2ToggleTextTestCase.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/BasicTextField2ToggleTextTestCase.kt
new file mode 100644
index 0000000..c22686e
--- /dev/null
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/BasicTextField2ToggleTextTestCase.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("DEPRECATION")
+
+package androidx.compose.foundation.benchmark.text
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.input.TextFieldState
+import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.ToggleableTestCase
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.InterceptPlatformTextInput
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.benchmark.RandomTextGenerator
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.TextUnit
+import kotlinx.coroutines.awaitCancellation
+
+@OptIn(ExperimentalComposeUiApi::class)
+class BasicTextField2ToggleTextTestCase(
+ private val textGenerator: RandomTextGenerator,
+ private val textLength: Int,
+ private val textNumber: Int,
+ private val width: Dp,
+ private val fontSize: TextUnit
+) : LayeredComposeTestCase(), ToggleableTestCase {
+
+ private val states = List(textNumber) {
+ TextFieldState(textGenerator.nextParagraph(length = textLength))
+ }
+
+ @Composable
+ override fun MeasuredContent() {
+ for (state in states) {
+ BasicTextField(
+ state = state,
+ textStyle = TextStyle(color = Color.Black, fontSize = fontSize),
+ modifier = Modifier
+ .background(color = Color.Cyan)
+ .requiredWidth(width)
+ )
+ }
+ }
+
+ @Composable
+ override fun ContentWrappers(content: @Composable () -> Unit) {
+ Column(
+ modifier = Modifier
+ .width(width)
+ .verticalScroll(rememberScrollState())
+ ) {
+ InterceptPlatformTextInput(
+ interceptor = { _, _ -> awaitCancellation() },
+ content = content
+ )
+ }
+ }
+
+ override fun toggleState() {
+ states.forEach {
+ it.setTextAndPlaceCursorAtEnd(textGenerator.nextParagraph(length = textLength))
+ }
+ }
+}
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index cd6706e..0d7637f 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -70,7 +70,7 @@
api("androidx.annotation:annotation:1.1.0")
api("androidx.annotation:annotation-experimental:1.4.0")
implementation("androidx.emoji2:emoji2:1.3.0")
- implementation("androidx.core:core:1.12.0")
+ implementation("androidx.core:core:1.13.1")
}
}
@@ -155,7 +155,7 @@
androidx {
name = "Compose Foundation"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Higher level abstractions of the Compose UI primitives. This library is design system agnostic, providing the high-level building blocks for both application and design-system developers"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/lint-baseline.xml b/compose/foundation/foundation/integration-tests/foundation-demos/lint-baseline.xml
index 1527a9b..2a3618a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/lint-baseline.xml
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="NewApi"
@@ -175,7 +175,7 @@
<issue
id="PrimitiveInCollection"
message="variable items with type SnapshotStateList<Integer>: replace with IntList"
- errorLine1=" val items = remember { mutableStateListOf<Int>().apply {"
+ errorLine1=" val items = remember {"
errorLine2=" ^">
<location
file="src/main/java/androidx/compose/foundation/demos/ListDemos.kt"/>
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyListSnappingDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyListSnappingDemos.kt
index cb7d7b9..5809903 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyListSnappingDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyListSnappingDemos.kt
@@ -18,7 +18,6 @@
import androidx.compose.animation.core.DecayAnimationSpec
import androidx.compose.animation.rememberSplineBasedDecay
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
import androidx.compose.foundation.gestures.snapping.SnapPosition
@@ -42,7 +41,6 @@
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastSumBy
-@OptIn(ExperimentalFoundationApi::class)
val SnapPositionDemos = listOf(
ComposableDemo("Center") { SnapPosition(SnapPosition.Center) },
ComposableDemo("Start") { SnapPosition(SnapPosition.Start) },
@@ -62,7 +60,6 @@
/**
* Snapping happens to the next item and items have the same size
*/
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun SnapPosition(snapPosition: SnapPosition) {
val lazyListState = rememberLazyListState()
@@ -92,7 +89,6 @@
/**
* Snapping happens to the next item and items have the same size
*/
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun SameItemSizeDemo() {
val lazyListState = rememberLazyListState()
@@ -110,7 +106,6 @@
/**
* Snapping happens to the next item and items have the different sizes
*/
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun DifferentItemSizeDemo() {
val lazyListState = rememberLazyListState()
@@ -129,7 +124,6 @@
/**
* Snapping happens to the next item and items are larger than the view port
*/
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun LargeItemSizeDemo() {
val lazyListState = rememberLazyListState()
@@ -148,7 +142,6 @@
/**
* Snapping happens to the next item and list has content paddings
*/
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun DifferentContentPaddingDemo() {
val lazyListState = rememberLazyListState()
@@ -168,7 +161,6 @@
/**
* Snapping happens after a decay animation and items have the same size
*/
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun DecayedSnappingDemo() {
val lazyListState = rememberLazyListState()
@@ -181,7 +173,6 @@
/**
* Snapping happens to at max one view port item's worth distance.
*/
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ViewPortBasedSnappingDemo() {
val lazyListState = rememberLazyListState()
@@ -193,7 +184,6 @@
}
}
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun rememberNextItemSnappingLayoutInfoProvider(
state: LazyListState,
@@ -203,14 +193,14 @@
val basedSnappingLayoutInfoProvider =
SnapLayoutInfoProvider(lazyListState = state, snapPosition = snapPosition)
object : SnapLayoutInfoProvider by basedSnappingLayoutInfoProvider {
- override fun calculateApproachOffset(initialVelocity: Float): Float {
- return 0f
- }
+ override fun calculateApproachOffset(
+ velocity: Float,
+ decayOffset: Float
+ ): Float = 0.0f
}
}
}
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun rememberViewPortSnappingLayoutInfoProvider(
state: LazyListState
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/NonItemBasedSnapping.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/NonItemBasedSnapping.kt
index f982d18..15952ec 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/NonItemBasedSnapping.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/NonItemBasedSnapping.kt
@@ -56,10 +56,11 @@
private val offsetList = listOf(0, layoutSize / 2 - thumbSize / 2, (layoutSize - thumbSize))
// do not approach, our snapping positions are discrete.
- override fun calculateApproachOffset(initialVelocity: Float): Float = 0f
+ override fun calculateApproachOffset(velocity: Float, decayOffset: Float): Float =
+ 0.0f
- override fun calculateSnappingOffset(currentVelocity: Float): Float {
- val targetOffset = if (currentVelocity == 0.0f) {
+ override fun calculateSnapOffset(velocity: Float): Float {
+ val targetOffset = if (velocity == 0.0f) {
// snap to closest offset
var closestOffset = 0
var prevMinAbs = Int.MAX_VALUE
@@ -71,7 +72,7 @@
}
}
(closestOffset).toFloat()
- } else if (currentVelocity > 0) {
+ } else if (velocity > 0) {
// snap to the next offset
val offset = offsetList.firstOrNull { it > currentOffset }
(offset ?: 0).toFloat() // if offset is found, move there, if not, don't move
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnapLayoutInfoProvider.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnapLayoutInfoProvider.kt
index 82e4f91..b8ff732 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnapLayoutInfoProvider.kt
@@ -20,6 +20,7 @@
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
import kotlin.math.abs
+import kotlin.math.absoluteValue
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.roundToInt
@@ -32,6 +33,12 @@
layoutSize: () -> Float
) = object : SnapLayoutInfoProvider {
+ override fun calculateApproachOffset(velocity: Float, decayOffset: Float): Float {
+ val calculatedItemSize = itemSize.invoke()
+ return (decayOffset.absoluteValue - calculatedItemSize)
+ .coerceAtLeast(0.0f) * calculatedItemSize.sign
+ }
+
fun nextFullItemCenter(layoutCenter: Float): Float {
val intItemSize = itemSize().roundToInt()
return floor((layoutCenter + itemSize()) / itemSize().roundToInt()) *
@@ -44,12 +51,12 @@
intItemSize
}
- override fun calculateSnappingOffset(currentVelocity: Float): Float {
+ override fun calculateSnapOffset(velocity: Float): Float {
val layoutCenter = layoutSize() / 2f + scrollState.value + itemSize() / 2f
val lowerBound = nextFullItemCenter(layoutCenter) - layoutCenter
val upperBound = previousFullItemCenter(layoutCenter) - layoutCenter
- return calculateFinalOffset(currentVelocity, upperBound, lowerBound)
+ return calculateFinalOffset(velocity, upperBound, lowerBound)
}
}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnappingDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnappingDemos.kt
index c7e3266..2566f4a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnappingDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/RowSnappingDemos.kt
@@ -175,9 +175,10 @@
)
return remember(scrollState, layoutSize) {
object : SnapLayoutInfoProvider by basedSnappingLayoutInfoProvider {
- override fun calculateApproachOffset(initialVelocity: Float): Float {
- return 0f
- }
+ override fun calculateApproachOffset(
+ velocity: Float,
+ decayOffset: Float
+ ): Float = 0.0f
}
}
}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemosCommon.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemosCommon.kt
index f6d2b99..2ba91e1 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemosCommon.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemosCommon.kt
@@ -17,22 +17,19 @@
package androidx.compose.foundation.demos.snapping
import androidx.compose.animation.core.DecayAnimationSpec
-import androidx.compose.animation.core.calculateTargetValue
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
import kotlin.math.absoluteValue
import kotlin.math.sign
-@OptIn(ExperimentalFoundationApi::class)
internal class ViewPortBasedSnappingLayoutInfoProvider(
private val baseSnapLayoutInfoProvider: SnapLayoutInfoProvider,
private val decayAnimationSpec: DecayAnimationSpec<Float>,
private val viewPortStep: () -> Float,
private val itemSize: () -> Float
) : SnapLayoutInfoProvider by baseSnapLayoutInfoProvider {
- override fun calculateApproachOffset(initialVelocity: Float): Float {
- val offset = decayAnimationSpec.calculateTargetValue(0f, initialVelocity)
- val finalOffset = (offset.absoluteValue - itemSize()).coerceAtLeast(0.0f) * offset.sign
+ override fun calculateApproachOffset(velocity: Float, decayOffset: Float): Float {
+ val finalOffset = (decayOffset.absoluteValue - itemSize())
+ .coerceAtLeast(0.0f) * decayOffset.sign
val viewPortOffset = viewPortStep()
return finalOffset.coerceIn(-viewPortOffset, viewPortOffset)
}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextFieldOutputTransformationDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextFieldOutputTransformationDemos.kt
index 9b4690a..a476523 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextFieldOutputTransformationDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextFieldOutputTransformationDemos.kt
@@ -195,7 +195,9 @@
}
if (middleWedge) {
val index = asCharSequence().indexOf("ghi")
- insert(index, middle.text.toString())
+ if (index > 0) {
+ insert(index, middle.text.toString())
+ }
}
if (suffixEnabled) {
append(suffix.text)
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridPrefetcherTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridPrefetcherTest.kt
index 087d8db..c4d5b3a 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridPrefetcherTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridPrefetcherTest.kt
@@ -14,14 +14,20 @@
* limitations under the License.
*/
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+
package androidx.compose.foundation.lazy.grid
import androidx.compose.foundation.AutoTestFrameClock
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.lazy.layout.TestPrefetchScheduler
+import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Remeasurement
import androidx.compose.ui.layout.RemeasurementModifier
@@ -58,6 +64,16 @@
val itemsSizeDp = with(rule.density) { itemsSizePx.toDp() }
lateinit var state: LazyGridState
+ private val scheduler = TestPrefetchScheduler()
+
+ @OptIn(ExperimentalFoundationApi::class)
+ @Composable
+ fun rememberState(
+ initialFirstVisibleItemIndex: Int = 0,
+ initialFirstVisibleItemScrollOffset: Int = 0
+ ): LazyGridState = remember {
+ LazyGridState(initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset, scheduler)
+ }
@Test
fun notPrefetchingForwardInitially() {
@@ -85,8 +101,8 @@
}
}
- waitForPrefetch(4)
- waitForPrefetch(5)
+ waitForPrefetch()
+ waitForPrefetch()
rule.onNodeWithTag("4")
.assertExists()
@@ -106,8 +122,8 @@
}
}
- waitForPrefetch(2)
- waitForPrefetch(3)
+ waitForPrefetch()
+ waitForPrefetch()
rule.onNodeWithTag("2")
.assertExists()
@@ -127,8 +143,8 @@
}
}
- waitForPrefetch(6)
- waitForPrefetch(7)
+ waitForPrefetch()
+ waitForPrefetch()
rule.onNodeWithTag("6")
.assertExists()
@@ -144,8 +160,8 @@
}
}
- waitForPrefetch(0)
- waitForPrefetch(1)
+ waitForPrefetch()
+ waitForPrefetch()
rule.onNodeWithTag("0")
.assertExists()
@@ -165,7 +181,7 @@
}
}
- waitForPrefetch(4)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking {
@@ -174,7 +190,7 @@
}
}
- waitForPrefetch(6)
+ waitForPrefetch()
rule.onNodeWithTag("4")
.assertIsDisplayed()
@@ -194,7 +210,7 @@
}
}
- waitForPrefetch(4)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking {
@@ -203,7 +219,7 @@
}
}
- waitForPrefetch(2)
+ waitForPrefetch()
rule.onNodeWithTag("4")
.assertIsDisplayed()
@@ -225,8 +241,7 @@
}
}
- waitForPrefetch(6)
- waitForPrefetch(7)
+ waitForPrefetch()
rule.onNodeWithTag("6")
.assertExists()
@@ -244,8 +259,7 @@
}
}
- waitForPrefetch(0)
- waitForPrefetch(1)
+ waitForPrefetch()
rule.onNodeWithTag("0")
.assertExists()
@@ -283,7 +297,7 @@
}
}
- waitForPrefetch(6)
+ waitForPrefetch()
rule.onNodeWithTag("8")
.assertExists()
@@ -296,7 +310,7 @@
}
}
- waitForPrefetch(0)
+ waitForPrefetch()
rule.onNodeWithTag("0")
.assertExists()
@@ -316,7 +330,7 @@
) { constraints ->
val placeable = if (emit) {
subcompose(Unit) {
- state = rememberLazyGridState()
+ state = rememberState()
LazyGrid(
2,
Modifier.mainAxisSize(itemsSizeDp * 1.5f),
@@ -355,7 +369,7 @@
fun snappingToOtherPositionWhilePrefetchIsScheduled() {
val composedItems = mutableListOf<Int>()
rule.setContent {
- state = rememberLazyGridState()
+ state = rememberState()
LazyGrid(
1,
Modifier.mainAxisSize(itemsSizeDp * 1.5f),
@@ -410,7 +424,7 @@
}
}
- waitForPrefetch(13)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking(AutoTestFrameClock()) {
@@ -424,14 +438,13 @@
}
}
- private fun waitForPrefetch(index: Int) {
- rule.waitUntil {
- activeNodes.contains(index) && activeMeasuredNodes.contains(index)
+ private fun waitForPrefetch() {
+ rule.runOnIdle {
+ scheduler.executeActiveRequests()
}
}
private val activeNodes = mutableSetOf<Int>()
- private val activeMeasuredNodes = mutableSetOf<Int>()
private fun composeGrid(
firstItem: Int = 0,
@@ -440,7 +453,7 @@
contentPadding: PaddingValues = PaddingValues(0.dp)
) {
rule.setContent {
- state = rememberLazyGridState(
+ state = rememberState(
initialFirstVisibleItemIndex = firstItem,
initialFirstVisibleItemScrollOffset = itemOffset
)
@@ -456,7 +469,6 @@
activeNodes.add(it)
onDispose {
activeNodes.remove(it)
- activeMeasuredNodes.remove(it)
}
}
Spacer(
@@ -465,7 +477,6 @@
.testTag("$it")
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
- activeMeasuredNodes.add(it)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
index bb6d01a..5596f14 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
@@ -257,7 +257,8 @@
.then(modifier))
}
var needToCompose by mutableStateOf(false)
- val prefetchState = LazyLayoutPrefetchState()
+ val scheduler = TestPrefetchScheduler()
+ val prefetchState = LazyLayoutPrefetchState(scheduler)
rule.setContent {
LazyLayout(itemProvider, prefetchState = prefetchState) {
val item = if (needToCompose) {
@@ -273,9 +274,10 @@
assertThat(measureCount).isEqualTo(0)
prefetchState.schedulePrefetch(0, constraints)
- }
- rule.waitUntil { measureCount == 1 }
+ scheduler.executeActiveRequests()
+ assertThat(measureCount).isEqualTo(1)
+ }
rule.onNodeWithTag("0").assertIsNotDisplayed()
@@ -303,20 +305,18 @@
}
}
}
- val prefetchState = LazyLayoutPrefetchState()
+ val scheduler = TestPrefetchScheduler()
+ val prefetchState = LazyLayoutPrefetchState(scheduler)
rule.setContent {
LazyLayout(itemProvider, prefetchState = prefetchState) {
layout(100, 100) {}
}
}
- val handle = rule.runOnIdle {
- prefetchState.schedulePrefetch(0, Constraints.fixed(50, 50))
- }
-
- rule.waitUntil { composed }
-
rule.runOnIdle {
+ val handle = prefetchState.schedulePrefetch(0, Constraints.fixed(50, 50))
+ scheduler.executeActiveRequests()
+ assertThat(composed).isTrue()
handle.cancel()
}
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/layout/TestPrefetchScheduler.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/layout/TestPrefetchScheduler.kt
new file mode 100644
index 0000000..ad3b8a6
--- /dev/null
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/layout/TestPrefetchScheduler.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.layout
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class TestPrefetchScheduler : PrefetchScheduler {
+
+ private var activeRequests = mutableListOf<PrefetchRequest>()
+ override fun schedulePrefetch(prefetchRequest: PrefetchRequest) {
+ activeRequests.add(prefetchRequest)
+ }
+
+ fun executeActiveRequests() {
+ activeRequests.forEach {
+ with(it) { scope.execute() }
+ }
+ activeRequests.clear()
+ }
+
+ private val scope = object : PrefetchRequestScope {
+ override fun availableTimeNanos(): Long = Long.MAX_VALUE
+ }
+}
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListFocusMoveCompositionCountTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListFocusMoveCompositionCountTest.kt
index b8a37d5..21f1531 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListFocusMoveCompositionCountTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListFocusMoveCompositionCountTest.kt
@@ -14,11 +14,14 @@
* limitations under the License.
*/
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+
package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Modifier
@@ -45,6 +48,10 @@
private val composedItems = mutableSetOf<Int>()
+ private val state = LazyListState().also {
+ it.prefetchingEnabled = false
+ }
+
@Test
fun moveFocus() {
// Arrange.
@@ -52,7 +59,7 @@
lateinit var focusManager: FocusManager
rule.setContent {
focusManager = LocalFocusManager.current
- LazyRow(Modifier.size(rowSize)) {
+ LazyRow(Modifier.size(rowSize), state) {
items(100) { index ->
Box(
Modifier
@@ -71,7 +78,7 @@
rule.runOnIdle { focusManager.moveFocus(FocusDirection.Right) }
// Assert
- rule.runOnIdle { assertThat(composedItems).containsExactly(5, 6) }
+ rule.runOnIdle { assertThat(composedItems).containsExactly(5) }
}
@Test
@@ -81,7 +88,7 @@
lateinit var focusManager: FocusManager
rule.setContent {
focusManager = LocalFocusManager.current
- LazyRow(Modifier.size(rowSize)) {
+ LazyRow(Modifier.size(rowSize), state) {
items(100) { index ->
Box(Modifier.size(itemSize).focusable()) {
Box(Modifier.size(itemSize).focusable().testTag("$index"))
@@ -97,7 +104,7 @@
rule.runOnIdle { focusManager.moveFocus(FocusDirection.Right) }
// Assert
- rule.runOnIdle { assertThat(composedItems).containsExactly(5, 6) }
+ rule.runOnIdle { assertThat(composedItems).containsExactly(5) }
}
@Test
@@ -107,7 +114,7 @@
lateinit var focusManager: FocusManager
rule.setContent {
focusManager = LocalFocusManager.current
- LazyRow(Modifier.size(rowSize)) {
+ LazyRow(Modifier.size(rowSize), state) {
items(100) { index ->
Box(Modifier.size(itemSize).focusable()) {
Box(Modifier.size(itemSize).focusable()) {
@@ -125,6 +132,6 @@
rule.runOnIdle { focusManager.moveFocus(FocusDirection.Right) }
// Assert
- rule.runOnIdle { assertThat(composedItems).containsExactly(5, 6) }
+ rule.runOnIdle { assertThat(composedItems).containsExactly(5) }
}
}
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListItemAppearanceAnimationTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListItemAppearanceAnimationTest.kt
index 61c2ef9..03845b8 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListItemAppearanceAnimationTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListItemAppearanceAnimationTest.kt
@@ -54,6 +54,7 @@
import androidx.test.filters.SdkSuppress
import com.google.common.truth.Truth.assertThat
import kotlin.math.abs
+import kotlin.math.roundToInt
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Rule
@@ -316,6 +317,46 @@
}
}
+ @Test
+ fun itemOutsideOfViewPortBeingAnimatedIn_shouldBePlacedAtTheEndOfList() {
+ var list by mutableStateOf(
+ listOf(
+ Color.Black,
+ Color.Green,
+ Color.Blue,
+ Color.Yellow,
+ Color.DarkGray
+ )
+ )
+ rule.setContent {
+ LazyList(containerSize = itemSizeDp * 2.5f) {
+ items(list, key = { it.toArgb() }) {
+ Item(it)
+ }
+ }
+ }
+
+ rule.runOnUiThread {
+ // item 0 will leave, item 3 will pop up
+ list = listOf(Color.Green, Color.Blue, Color.Yellow, Color.DarkGray)
+ }
+
+ onAnimationFrame { fraction ->
+ if (fraction.isCloseTo(0.5f)) {
+ assertPixels((itemSize * 2.5f).roundToInt()) { offset ->
+ when (offset) {
+ // green item is first
+ in 0 until itemSize -> Color.Green
+ // blue item is second
+ in itemSize until 2 * itemSize -> Color.Blue
+ // yellow item pops up at the bottom
+ else -> Color.Yellow
+ }
+ }
+ }
+ }
+ }
+
private fun assertPixels(
mainAxisSize: Int,
crossAxisSize: Int = this.crossAxisSize,
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListItemPlacementAnimationTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListItemPlacementAnimationTest.kt
index 7ea574b..237e51f 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListItemPlacementAnimationTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListItemPlacementAnimationTest.kt
@@ -596,6 +596,48 @@
}
@Test
+ fun moveItemToTheTop_itemWithMoreChildren_outsideBounds_shouldNotCrash() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
+ val listSize = itemSize * 3
+ val listSizeDp = with(rule.density) { listSize.toDp() }
+ rule.setContent {
+ LazyList(
+ maxSize = listSizeDp,
+ startIndex = 3
+ ) {
+ items(list, key = { it }) {
+ Item(it)
+ if (it != list.last()) {
+ Box(modifier = Modifier)
+ }
+ }
+ }
+ }
+
+ assertPositions(
+ 3 to 0f,
+ 4 to itemSize,
+ 5 to itemSize * 2
+ )
+
+ // move item 5 out of bounds
+ rule.runOnUiThread {
+ list = listOf(5, 0, 1, 2, 3, 4)
+ }
+
+ // should not crash
+ onAnimationFrame { fraction ->
+ if (fraction == 1.0f) {
+ assertPositions(
+ 2 to 0f,
+ 3 to itemSize,
+ 4 to itemSize * 2
+ )
+ }
+ }
+ }
+
+ @Test
fun moveItemToTheBottomOutsideOfBounds_withSpacing() {
var list by mutableStateOf(listOf(0, 1, 2, 3, 4, 5))
val listSize = itemSize * 3 + spacing * 2
@@ -1001,6 +1043,50 @@
}
@Test
+ fun removingItemsCauseOutOfBoundsItemToPopUp_withContentPadding() {
+ var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+ val rawStartPadding = 8f
+ val rawEndPadding = 12f
+ val (startPaddingDp, endPaddingDp) = with(rule.density) {
+ rawStartPadding.toDp() to rawEndPadding.toDp()
+ }
+ rule.setContent {
+ // only 4 items will be visible 0, 1, 2, 3
+ LazyList(
+ maxSize = itemSizeDp * 4,
+ startPadding = startPaddingDp,
+ endPadding = endPaddingDp
+ ) {
+ items(list, key = { it }) {
+ Item(it)
+ }
+ }
+ }
+
+ val startPadding = if (reverseLayout) rawEndPadding else rawStartPadding
+ assertPositions(
+ 0 to startPadding,
+ 1 to startPadding + itemSize,
+ 2 to startPadding + itemSize * 2,
+ 3 to startPadding + itemSize * 3
+ )
+
+ rule.runOnUiThread {
+ list = listOf(1, 2, 3, 4)
+ }
+
+ onAnimationFrame { fraction ->
+ assertPositions(
+ 1 to startPadding + itemSize - itemSize * fraction,
+ 2 to startPadding + itemSize * 2 - itemSize * fraction,
+ 3 to startPadding + itemSize * 3 - itemSize * fraction,
+ 4 to startPadding + itemSize * 4 - itemSize * fraction,
+ fraction = fraction
+ )
+ }
+ }
+
+ @Test
fun reorderFirstAndLastItems_noNewLayoutInfoProduced() {
var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListNestedPrefetchingTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListNestedPrefetchingTest.kt
index ebd489b..229f84a 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListNestedPrefetchingTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListNestedPrefetchingTest.kt
@@ -26,6 +26,8 @@
import androidx.compose.foundation.lazy.LazyListPrefetchStrategy
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.layout.NestedPrefetchScope
+import androidx.compose.foundation.lazy.layout.PrefetchScheduler
+import androidx.compose.foundation.lazy.layout.TestPrefetchScheduler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
@@ -70,11 +72,19 @@
private val itemsSizePx = 30
private val itemsSizeDp = with(rule.density) { itemsSizePx.toDp() }
private val activeNodes = mutableSetOf<String>()
- private val activeMeasuredNodes = mutableSetOf<String>()
+ private val scheduler = TestPrefetchScheduler()
+
+ @OptIn(ExperimentalFoundationApi::class)
+ private val strategy = object : LazyListPrefetchStrategy by LazyListPrefetchStrategy() {
+ override val prefetchScheduler: PrefetchScheduler = scheduler
+ }
+
+ @OptIn(ExperimentalFoundationApi::class)
+ private fun createState(): LazyListState = LazyListState(prefetchStrategy = strategy)
@Test
fun nestedPrefetchingForwardAfterSmallScroll() {
- val state = LazyListState()
+ val state = createState()
composeList(state)
val prefetchIndex = 2
@@ -85,7 +95,7 @@
}
}
- waitForPrefetch(tagFor(prefetchIndex))
+ waitForPrefetch()
}
// We want to make sure nested children were precomposed before the parent was premeasured
@@ -111,7 +121,7 @@
@Test
fun cancelingPrefetchCancelsItsNestedPrefetches() {
- val state = LazyListState()
+ val state = createState()
composeList(state)
rule.runOnIdle {
@@ -122,7 +132,7 @@
}
}
- waitForPrefetch(tagFor(3))
+ waitForPrefetch()
rule.runOnIdle {
assertThat(activeNodes).contains(tagFor(3))
@@ -141,7 +151,7 @@
}
}
- waitForPrefetch(tagFor(7))
+ waitForPrefetch()
rule.runOnIdle {
runBlocking(AutoTestFrameClock()) {
@@ -160,7 +170,7 @@
@OptIn(ExperimentalFoundationApi::class)
@Test
fun overridingNestedPrefetchCountIsRespected() {
- val state = LazyListState()
+ val state = createState()
composeList(
state,
createNestedLazyListState = {
@@ -177,7 +187,7 @@
}
}
- waitForPrefetch(tagFor(prefetchIndex))
+ waitForPrefetch()
}
// Since the nested prefetch count on the strategy is 1, we only expect index 0 to be
@@ -197,7 +207,7 @@
fun nestedPrefetchIsMeasuredWithProvidedConstraints() {
val nestedConstraints =
Constraints(minWidth = 20, minHeight = 20, maxWidth = 20, maxHeight = 20)
- val state = LazyListState()
+ val state = createState()
composeList(
state,
createNestedLazyListState = {
@@ -214,7 +224,7 @@
}
}
- waitForPrefetch(tagFor(prefetchIndex))
+ waitForPrefetch()
}
assertThat(actions).containsExactly(
@@ -232,7 +242,7 @@
@Test
fun nestedPrefetchStartsFromFirstVisibleItemIndex() {
- val state = LazyListState()
+ val state = createState()
composeList(
state,
createNestedLazyListState = {
@@ -247,7 +257,7 @@
}
}
- waitForPrefetch(tagFor(prefetchIndex))
+ waitForPrefetch()
}
assertThat(actions).containsExactly(
@@ -273,9 +283,9 @@
}
}
- private fun waitForPrefetch(tag: String) {
- rule.waitUntil {
- activeNodes.contains(tag) && activeMeasuredNodes.contains(tag)
+ private fun waitForPrefetch() {
+ rule.runOnIdle {
+ scheduler.executeActiveRequests()
}
}
@@ -332,17 +342,14 @@
actions?.add(Action.Compose(index, nestedIndex))
onDispose {
activeNodes.remove(tag)
- activeMeasuredNodes.remove(tag)
}
}
}
private fun Modifier.trackWhenMeasured(index: Int, nestedIndex: Int? = null): Modifier {
- val tag = tagFor(index, nestedIndex)
return this then Modifier.layout { measurable, constraints ->
actions?.add(Action.Measure(index, nestedIndex))
val placeable = measurable.measure(constraints)
- activeMeasuredNodes.add(tag)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetchStrategyTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetchStrategyTest.kt
index 31dd332..0d773c2 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetchStrategyTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetchStrategyTest.kt
@@ -29,9 +29,10 @@
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
import androidx.compose.foundation.lazy.layout.NestedPrefetchScope
+import androidx.compose.foundation.lazy.layout.PrefetchScheduler
+import androidx.compose.foundation.lazy.layout.TestPrefetchScheduler
import androidx.compose.foundation.lazy.list.LazyListPrefetchStrategyTest.RecordingLazyListPrefetchStrategy.Callback
import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
@@ -74,10 +75,11 @@
private val itemsSizeDp = with(rule.density) { itemsSizePx.toDp() }
lateinit var state: LazyListState
+ private val scheduler = TestPrefetchScheduler()
@Test
fun callbacksTriggered_whenScrollForwardsWithoutVisibleItemsChanged() {
- val strategy = RecordingLazyListPrefetchStrategy()
+ val strategy = RecordingLazyListPrefetchStrategy(scheduler)
composeList(prefetchStrategy = strategy)
@@ -104,7 +106,7 @@
@Test
fun callbacksTriggered_whenScrollBackwardsWithoutVisibleItemsChanged() {
- val strategy = RecordingLazyListPrefetchStrategy()
+ val strategy = RecordingLazyListPrefetchStrategy(scheduler)
composeList(firstItem = 10, itemOffset = 10, prefetchStrategy = strategy)
@@ -131,7 +133,7 @@
@Test
fun callbacksTriggered_whenScrollWithVisibleItemsChanged() {
- val strategy = RecordingLazyListPrefetchStrategy()
+ val strategy = RecordingLazyListPrefetchStrategy(scheduler)
composeList(prefetchStrategy = strategy)
@@ -161,7 +163,7 @@
@Test
fun callbacksTriggered_whenItemsChangedWithoutScroll() {
- val strategy = RecordingLazyListPrefetchStrategy()
+ val strategy = RecordingLazyListPrefetchStrategy(scheduler)
val numItems = mutableStateOf(100)
composeList(prefetchStrategy = strategy, numItems = numItems)
@@ -196,20 +198,17 @@
}
}
- waitForPrefetch(2)
+ waitForPrefetch()
rule.onNodeWithTag("2")
.assertExists()
}
- private fun waitForPrefetch(index: Int) {
- rule.waitUntil {
- activeNodes.contains(index) && activeMeasuredNodes.contains(index)
+ private fun waitForPrefetch() {
+ rule.runOnIdle {
+ scheduler.executeActiveRequests()
}
}
- private val activeNodes = mutableSetOf<Int>()
- private val activeMeasuredNodes = mutableSetOf<Int>()
-
@OptIn(ExperimentalFoundationApi::class)
private fun composeList(
firstItem: Int = 0,
@@ -228,13 +227,6 @@
state,
) {
items(numItems.value) {
- DisposableEffect(it) {
- activeNodes.add(it)
- onDispose {
- activeNodes.remove(it)
- activeMeasuredNodes.remove(it)
- }
- }
Spacer(
Modifier
.mainAxisSize(itemsSizeDp)
@@ -242,7 +234,6 @@
.testTag("$it")
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
- activeMeasuredNodes.add(it)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
@@ -256,7 +247,10 @@
/**
* LazyListPrefetchStrategy that just records callbacks without scheduling prefetches.
*/
- private class RecordingLazyListPrefetchStrategy : LazyListPrefetchStrategy {
+ private class RecordingLazyListPrefetchStrategy(
+ override val prefetchScheduler: PrefetchScheduler?
+ ) : LazyListPrefetchStrategy {
+
sealed interface Callback {
data class OnScroll(val delta: Float, val visibleIndices: List<Int>) : Callback
data class OnVisibleItemsUpdated(val visibleIndices: List<Int>) : Callback
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetcherTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetcherTest.kt
index 70a88d8..ef1aed5 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetcherTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetcherTest.kt
@@ -22,7 +22,10 @@
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.lazy.LazyListPrefetchStrategy
import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.layout.PrefetchScheduler
+import androidx.compose.foundation.lazy.layout.TestPrefetchScheduler
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
@@ -71,6 +74,13 @@
lateinit var state: LazyListState
+ private val scheduler = TestPrefetchScheduler()
+
+ @OptIn(ExperimentalFoundationApi::class)
+ private val strategy = object : LazyListPrefetchStrategy by LazyListPrefetchStrategy() {
+ override val prefetchScheduler: PrefetchScheduler = scheduler
+ }
+
@Test
fun notPrefetchingForwardInitially() {
composeList()
@@ -97,7 +107,7 @@
}
}
- waitForPrefetch(preFetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$preFetchIndex")
.assertExists()
@@ -115,7 +125,7 @@
}
}
- waitForPrefetch(1)
+ waitForPrefetch()
rule.onNodeWithTag("1")
.assertExists()
@@ -134,7 +144,7 @@
}
}
var prefetchIndex = initialIndex + 2
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -149,7 +159,7 @@
}
prefetchIndex -= 3
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -167,7 +177,7 @@
}
}
- waitForPrefetch(2)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking {
@@ -178,7 +188,7 @@
val prefetchIndex = 3
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("${prefetchIndex - 1}")
.assertIsDisplayed()
@@ -198,7 +208,7 @@
}
}
- waitForPrefetch(2)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking {
@@ -207,7 +217,7 @@
}
}
- waitForPrefetch(1)
+ waitForPrefetch()
rule.onNodeWithTag("2")
.assertIsDisplayed()
@@ -230,7 +240,7 @@
var prefetchIndex = initialIndex + 2
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -245,7 +255,7 @@
}
prefetchIndex -= 3
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -281,7 +291,7 @@
}
var prefetchIndex = initialIndex + 1
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("${prefetchIndex + 1}")
.assertExists()
@@ -295,7 +305,7 @@
}
prefetchIndex -= 3
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -458,7 +468,7 @@
}
}
- waitForPrefetch(7)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking(AutoTestFrameClock()) {
@@ -472,14 +482,13 @@
}
}
- private fun waitForPrefetch(index: Int) {
- rule.waitUntil {
- activeNodes.contains(index) && activeMeasuredNodes.contains(index)
+ private fun waitForPrefetch() {
+ rule.runOnIdle {
+ scheduler.executeActiveRequests()
}
}
private val activeNodes = mutableSetOf<Int>()
- private val activeMeasuredNodes = mutableSetOf<Int>()
private fun composeList(
firstItem: Int = 0,
@@ -488,9 +497,11 @@
contentPadding: PaddingValues = PaddingValues(0.dp)
) {
rule.setContent {
+ @OptIn(ExperimentalFoundationApi::class)
state = rememberLazyListState(
initialFirstVisibleItemIndex = firstItem,
- initialFirstVisibleItemScrollOffset = itemOffset
+ initialFirstVisibleItemScrollOffset = itemOffset,
+ prefetchStrategy = strategy
)
LazyColumnOrRow(
Modifier.mainAxisSize(itemsSizeDp * 1.5f),
@@ -504,7 +515,6 @@
activeNodes.add(it)
onDispose {
activeNodes.remove(it)
- activeMeasuredNodes.remove(it)
}
}
Spacer(
@@ -514,7 +524,6 @@
.testTag("$it")
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
- activeMeasuredNodes.add(it)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt
index 224a1cc..72084b7 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+
package androidx.compose.foundation.lazy.staggeredgrid
import androidx.compose.foundation.AutoTestFrameClock
@@ -22,7 +24,10 @@
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.lazy.layout.TestPrefetchScheduler
+import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Remeasurement
@@ -63,6 +68,20 @@
val itemsSizeDp = with(rule.density) { itemsSizePx.toDp() }
internal lateinit var state: LazyStaggeredGridState
+ private val scheduler = TestPrefetchScheduler()
+
+ @OptIn(ExperimentalFoundationApi::class)
+ @Composable
+ fun rememberState(
+ initialFirstVisibleItemIndex: Int = 0,
+ initialFirstVisibleItemOffset: Int = 0
+ ): LazyStaggeredGridState = remember {
+ LazyStaggeredGridState(
+ intArrayOf(initialFirstVisibleItemIndex),
+ intArrayOf(initialFirstVisibleItemOffset),
+ scheduler
+ )
+ }
@Test
fun notPrefetchingForwardInitially() {
@@ -90,7 +109,7 @@
}
}
- waitForPrefetch(5)
+ waitForPrefetch()
rule.onNodeWithTag("4")
.assertExists()
@@ -110,7 +129,7 @@
}
}
- waitForPrefetch(2)
+ waitForPrefetch()
rule.onNodeWithTag("2")
.assertExists()
@@ -130,7 +149,7 @@
}
}
- waitForPrefetch(9)
+ waitForPrefetch()
rule.onNodeWithTag("8")
.assertExists()
@@ -145,7 +164,7 @@
}
}
- waitForPrefetch(2)
+ waitForPrefetch()
rule.onNodeWithTag("2")
.assertExists()
@@ -165,7 +184,7 @@
}
}
- waitForPrefetch(4)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking {
@@ -174,7 +193,7 @@
}
}
- waitForPrefetch(6)
+ waitForPrefetch()
rule.onNodeWithTag("4")
.assertIsDisplayed()
@@ -194,7 +213,7 @@
}
}
- waitForPrefetch(4)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking {
@@ -203,7 +222,7 @@
}
}
- waitForPrefetch(2)
+ waitForPrefetch()
rule.onNodeWithTag("4")
.assertIsDisplayed()
@@ -226,7 +245,7 @@
}
}
- waitForPrefetch(13)
+ waitForPrefetch()
rule.onNodeWithTag("12")
.assertExists()
@@ -378,7 +397,7 @@
fun snappingToOtherPositionWhilePrefetchIsScheduled() {
val composedItems = mutableListOf<Int>()
rule.setContent {
- state = rememberLazyStaggeredGridState()
+ state = rememberState()
LazyStaggeredGrid(
1,
Modifier.mainAxisSize(itemsSizeDp * 1.5f),
@@ -433,7 +452,7 @@
}
}
- waitForPrefetch(13)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking(AutoTestFrameClock()) {
@@ -450,7 +469,7 @@
@Test
fun scrollingWithStaggeredItemsPrefetchesCorrectly() {
rule.setContent {
- state = rememberLazyStaggeredGridState()
+ state = rememberState()
LazyStaggeredGrid(
2,
Modifier.mainAxisSize(itemsSizeDp * 5f),
@@ -461,7 +480,6 @@
activeNodes.add(it)
onDispose {
activeNodes.remove(it)
- activeMeasuredNodes.remove(it)
}
}
Spacer(
@@ -471,7 +489,6 @@
.testTag("$it")
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
- activeMeasuredNodes.add(it)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
@@ -495,8 +512,8 @@
}
}
- waitForPrefetch(7)
- waitForPrefetch(8)
+ waitForPrefetch()
+ waitForPrefetch()
// ┌─┬─┐
// │2├─┤
@@ -520,14 +537,14 @@
// │6├─┤
// └─┴─┘
- waitForPrefetch(9)
+ waitForPrefetch()
}
@Test
fun fullSpanIsPrefetchedCorrectly() {
val nodeConstraints = mutableMapOf<Int, Constraints>()
rule.setContent {
- state = rememberLazyStaggeredGridState()
+ state = rememberState()
LazyStaggeredGrid(
2,
Modifier.mainAxisSize(itemsSizeDp * 5f).crossAxisSize(itemsSizeDp * 2f),
@@ -546,7 +563,6 @@
activeNodes.add(it)
onDispose {
activeNodes.remove(it)
- activeMeasuredNodes.remove(it)
}
}
Spacer(
@@ -555,7 +571,6 @@
.testTag("$it")
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
- activeMeasuredNodes.add(it)
nodeConstraints.put(it, constraints)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
@@ -577,7 +592,7 @@
state.scrollBy(itemsSizeDp * 5f)
assertThat(activeNodes).contains(9)
- waitForPrefetch(10)
+ waitForPrefetch()
val expectedConstraints = if (vertical) {
Constraints.fixedWidth(itemsSizePx * 2)
} else {
@@ -589,7 +604,7 @@
@Test
fun fullSpanIsPrefetchedCorrectly_scrollingBack() {
rule.setContent {
- state = rememberLazyStaggeredGridState()
+ state = rememberState()
LazyStaggeredGrid(
2,
Modifier.mainAxisSize(itemsSizeDp * 5f),
@@ -608,7 +623,6 @@
activeNodes.add(it)
onDispose {
activeNodes.remove(it)
- activeMeasuredNodes.remove(it)
}
}
Spacer(
@@ -618,7 +632,6 @@
.testTag("$it")
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
- activeMeasuredNodes.add(it)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
@@ -647,27 +660,26 @@
state.scrollBy(-1.dp)
- waitForPrefetch(10)
+ waitForPrefetch()
}
- private fun waitForPrefetch(index: Int) {
- rule.waitUntil {
- activeNodes.contains(index) && activeMeasuredNodes.contains(index)
+ private fun waitForPrefetch() {
+ rule.runOnIdle {
+ scheduler.executeActiveRequests()
}
}
private val activeNodes = mutableSetOf<Int>()
- private val activeMeasuredNodes = mutableSetOf<Int>()
private fun composeStaggeredGrid(
firstItem: Int = 0,
itemOffset: Int = 0,
) {
- state = LazyStaggeredGridState(
- initialFirstVisibleItemIndex = firstItem,
- initialFirstVisibleItemOffset = itemOffset
- )
rule.setContent {
+ state = rememberState(
+ initialFirstVisibleItemIndex = firstItem,
+ initialFirstVisibleItemOffset = itemOffset
+ )
LazyStaggeredGrid(
2,
Modifier.mainAxisSize(itemsSizeDp * 1.5f),
@@ -678,7 +690,6 @@
activeNodes.add(it)
onDispose {
activeNodes.remove(it)
- activeMeasuredNodes.remove(it)
}
}
Spacer(
@@ -688,7 +699,6 @@
.testTag("$it")
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
- activeMeasuredNodes.add(it)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
diff --git a/compose/foundation/foundation/lint-baseline.xml b/compose/foundation/foundation/lint-baseline.xml
index 749cb82..25edb88 100644
--- a/compose/foundation/foundation/lint-baseline.xml
+++ b/compose/foundation/foundation/lint-baseline.xml
@@ -1,86 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
-
- <issue
- id="BanSuppressTag"
- message="@suppress is not allowed in documentation"
- errorLine1="class TextDelegate("
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/TextDelegate.kt"/>
- </issue>
-
- <issue
- id="BanSuppressTag"
- message="@suppress is not allowed in documentation"
- errorLine1=" fun applyCompositionDecoration("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt"/>
- </issue>
-
- <issue
- id="BanThreadSleep"
- message="Uses Thread.sleep()"
- errorLine1=" Thread.sleep(5)"
- errorLine2=" ~~~~~">
- <location
- file="src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/grid/LazyScrollTest.kt"/>
- </issue>
-
- <issue
- id="BanThreadSleep"
- message="Uses Thread.sleep()"
- errorLine1=" Thread.sleep(5)"
- errorLine2=" ~~~~~">
- <location
- file="src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt"/>
- </issue>
-
- <issue
- id="BanThreadSleep"
- message="Uses Thread.sleep()"
- errorLine1=" Thread.sleep(5)"
- errorLine2=" ~~~~~">
- <location
- file="src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimatedScrollTest.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="field anchors with type Map<T, Float>: replace with ObjectFloatMap"
- errorLine1=" internal val anchors = mutableMapOf<T, Float>()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="return type Map<T, Float> of getAnchors$lint_module: replace with ObjectFloatMap"
- errorLine1=" internal val anchors = mutableMapOf<T, Float>()"
- errorLine2=" ~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="constructor MapDraggableAnchors has parameter anchors with type Map<T, Float>: replace with ObjectFloatMap"
- errorLine1="private class MapDraggableAnchors<T>(private val anchors: Map<T, Float>) : DraggableAnchors<T> {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="field anchors with type Map<T, Float>: replace with ObjectFloatMap"
- errorLine1="private class MapDraggableAnchors<T>(private val anchors: Map<T, Float>) : DraggableAnchors<T> {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt"/>
- </issue>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="PrimitiveInCollection"
@@ -487,139 +406,4 @@
file="src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt"/>
</issue>
- <issue
- id="PrimitiveInCollection"
- message="return type Map<Long, Selection> of createSubSelections: replace with LongObjectMap"
- errorLine1=" fun createSubSelections(selection: Selection): Map<Long, Selection>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="constructor MultiSelectionLayout has parameter selectableIdToInfoListIndex with type Map<Long, Integer>: replace with LongIntMap"
- errorLine1=" val selectableIdToInfoListIndex: Map<Long, Int>,"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="field selectableIdToInfoListIndex with type Map<Long, Integer>: replace with LongIntMap"
- errorLine1=" val selectableIdToInfoListIndex: Map<Long, Int>,"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="return type Map<Long, Integer> of getSelectableIdToInfoListIndex: replace with LongIntMap"
- errorLine1=" val selectableIdToInfoListIndex: Map<Long, Int>,"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="method createAndPutSubSelection has parameter $this$createAndPutSubSelection with type Map<Long, Selection>: replace with LongObjectMap"
- errorLine1=" private fun MutableMap<Long, Selection>.createAndPutSubSelection("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="field selectableIdToInfoListIndex with type Map<Long, Integer>: replace with LongIntMap"
- errorLine1=" private val selectableIdToInfoListIndex: MutableMap<Long, Int> = mutableMapOf()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="variable newSubselection with type Map<Long, ? extends Selection>: replace with LongObjectMap"
- errorLine1=" val (newSelection, newSubselection) = selectAll("
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="variable subselections with type Map<Long, Selection>: replace with LongObjectMap"
- errorLine1=" val subselections = mutableMapOf<Long, Selection>()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="variable idToIndexMap with type Map<Long, Integer>: replace with LongIntMap"
- errorLine1=" val idToIndexMap = mutableMapOf<Long, Int>()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="return type Map<Long, Selection> of getSubselections: replace with LongObjectMap"
- errorLine1=" val subselections: Map<Long, Selection>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="field _selectableMap with type Map<Long, Selectable>: replace with LongObjectMap"
- errorLine1=" private val _selectableMap = mutableMapOf<Long, Selectable>()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="return type Map<Long, Selectable> of getSelectableMap$lint_module: replace with LongObjectMap"
- errorLine1=" internal val selectableMap: Map<Long, Selectable>"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="method setSubselections has parameter <set-?> with type Map<Long, Selection>: replace with LongObjectMap"
- errorLine1=" override var subselections: Map<Long, Selection> by mutableStateOf(emptyMap())"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="field baselineCache with type Map<AlignmentLine, Integer>: replace with ObjectIntMap"
- errorLine1=" private var baselineCache: MutableMap<AlignmentLine, Int>? = null"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="variable cache with type Map<AlignmentLine, Integer>: replace with ObjectIntMap"
- errorLine1=" var cache = baselineCache"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt"/>
- </issue>
-
</issues>
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt
index 3b003f7..d57ab48 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/LazyDslSamples.kt
@@ -96,7 +96,10 @@
stickyHeader {
Text(
"Section $section",
- Modifier.fillMaxWidth().background(Color.LightGray).padding(8.dp)
+ Modifier
+ .fillMaxWidth()
+ .background(Color.LightGray)
+ .padding(8.dp)
)
}
items(10) {
@@ -109,9 +112,9 @@
@Sampled
@Composable
fun AnimateItemSample() {
- var list by remember { mutableStateOf(listOf("A", "B", "C")) }
+ var list by remember { mutableStateOf(listOf("1", "2", "3")) }
Column {
- Button(onClick = { list = list + "D" }) {
+ Button(onClick = { list = list + "${list.count() + 1}" }) {
Text("Add new item")
}
Button(onClick = { list = list.shuffled() }) {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
index 7a75d11..a52b748 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
@@ -29,6 +29,7 @@
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
@@ -106,6 +107,8 @@
import com.google.common.truth.Truth.assertThat
import kotlin.reflect.KClass
import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.junit.After
@@ -1335,26 +1338,52 @@
private fun Modifier.dynamicPointerInputModifier(
enabled: Boolean,
key: Any? = Unit,
- onPress: () -> Unit = { },
+ onEnter: () -> Unit = { },
onMove: () -> Unit = { },
+ onPress: () -> Unit = { },
onRelease: () -> Unit = { },
- ) = if (enabled) {
+ onExit: () -> Unit = { },
+ ) = if (enabled) {
pointerInput(key) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent()
- if (event.type == PointerEventType.Press) {
- onPress()
- } else if (event.type == PointerEventType.Move) {
- onMove()
- } else if (event.type == PointerEventType.Release) {
- onRelease()
+ when (event.type) {
+ PointerEventType.Enter -> {
+ onEnter()
+ }
+ PointerEventType.Press -> {
+ onPress()
+ }
+ PointerEventType.Move -> {
+ onMove()
+ }
+ PointerEventType.Release -> {
+ onRelease()
+ }
+ PointerEventType.Exit -> {
+ onExit()
+ }
}
}
}
}
} else this
+ private fun Modifier.dynamicPointerInputModifierWithDetectTapGestures(
+ enabled: Boolean,
+ key: Any? = Unit,
+ onTap: () -> Unit = { }
+ ) = if (enabled) {
+ pointerInput(key) {
+ detectTapGestures {
+ onTap()
+ }
+ }
+ } else {
+ this
+ }
+
private fun Modifier.dynamicClickableModifier(
enabled: Boolean,
onClick: () -> Unit
@@ -1365,8 +1394,20 @@
) { onClick() }
} else this
+ // !!!!! MOUSE & TOUCH EVENTS TESTS WITH DYNAMIC MODIFIER INPUT TESTS SECTION (START) !!!!!
+ // The next ~20 tests test enabling/disabling dynamic input modifiers (both pointer input and
+ // clickable) using various combinations (touch vs. mouse, Unit vs. unique keys, nested UI
+ // elements vs. all modifiers on one UI element, etc.)
+
+ /* Uses pointer input block (awaitPointerEventScope + awaitPointerEvent) for the non-dynamic
+ * pointer input and clickable{} for the dynamic pointer input (both on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Touch "click" (down/move/up)
+ * 2. Assert
+ */
@Test
- fun dynamicClickableModifierTouch_addsAbovePointerInputWithKey_triggersBothModifiers() {
+ fun dynamicClickableModifier_addsAbovePointerInputWithKeyTouchEvents_correctEvents() {
// This is part of a dynamic modifier
var clickableClickCounter by mutableStateOf(0)
// Note: I'm tracking press instead of release because clickable{} consumes release
@@ -1410,8 +1451,15 @@
}
}
+ /* Uses pointer input block (awaitPointerEventScope + awaitPointerEvent) for the non-dynamic
+ * pointer input and clickable{} for the dynamic pointer input (both on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Touch "click" (down/move/up)
+ * 2. Assert
+ */
@Test
- fun dynamicClickableModifierTouch_addsAbovePointerInputWithUnitKey_triggersBothModifiers() {
+ fun dynamicClickableModifier_addsAbovePointerInputWithUnitKeyTouchEvents_correctEvents() {
// This is part of a dynamic modifier
var clickableClickCounter by mutableStateOf(0)
// Note: I'm tracking press instead of release because clickable{} consumes release
@@ -1455,9 +1503,18 @@
}
}
+ /* Uses pointer input block (awaitPointerEventScope + awaitPointerEvent) for the non-dynamic
+ * pointer input and clickable{} for the dynamic pointer input (both on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Mouse enter
+ * 2. Mouse "click()" (press/release)
+ * 3. Mouse exit
+ * 4. Assert
+ */
@OptIn(ExperimentalTestApi::class)
@Test
- fun dynamicClickableModifierMouse_addsAbovePointerInputWithKey_triggersBothModifiers() {
+ fun dynamicClickableModifier_addsAbovePointerInputWithKeyMouseEvents_correctEvents() {
// This is part of a dynamic modifier
var clickableClickCounter by mutableStateOf(0)
// Note: I'm tracking press instead of release because clickable{} consumes release
@@ -1509,9 +1566,18 @@
}
}
+ /* Uses pointer input block (awaitPointerEventScope + awaitPointerEvent) for the non-dynamic
+ * pointer input and clickable{} for the dynamic pointer input (both on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Mouse enter
+ * 2. Mouse "click()" (press/release)
+ * 3. Mouse exit
+ * 4. Assert
+ */
@OptIn(ExperimentalTestApi::class)
@Test
- fun dynamicClickableModifierMouse_addsAbovePointerInputWithUnitKey_triggersBothModifiers() {
+ fun dynamicClickableModifier_addsAbovePointerInputWithUnitKeyMouseEvents_correctEvents() {
// This is part of a dynamic modifier
var clickableClickCounter by mutableStateOf(0)
// Note: I'm tracking press instead of release because clickable{} consumes release
@@ -1563,8 +1629,16 @@
}
}
+ /* Uses clickable{} for the non-dynamic pointer input and pointer input
+ * block (awaitPointerEventScope + awaitPointerEvent) for the dynamic pointer input (both
+ * on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Touch "click" (down/move/up)
+ * 2. Assert
+ */
@Test
- fun dynamicInputModifierTouch_addsAboveClickableWithKey_triggersBothModifiers() {
+ fun dynamicInputModifier_addsAboveClickableWithKeyTouchEvents_correctEvents() {
var clickableClickCounter by mutableStateOf(0)
// Note: I'm tracking press instead of release because clickable{} consumes release
var dynamicPressCounter by mutableStateOf(0)
@@ -1603,8 +1677,16 @@
}
}
+ /* Uses clickable{} for the non-dynamic pointer input and pointer input
+ * block (awaitPointerEventScope + awaitPointerEvent) for the dynamic pointer input (both
+ * on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Touch "click" (down/move/up)
+ * 2. Assert
+ */
@Test
- fun dynamicInputModifierTouch_addsAboveClickableWithUnitKey_triggersInBothModifiers() {
+ fun dynamicInputModifier_addsAboveClickableWithUnitKeyTouchEvents_correctEvents() {
var clickableClickCounter by mutableStateOf(0)
// Note: I'm tracking press instead of release because clickable{} consumes release
var dynamicPressCounter by mutableStateOf(0)
@@ -1642,9 +1724,22 @@
}
}
- // Tests a dynamic pointer input AND a dynamic clickable{} above an existing pointer input.
+ /* Uses pointer input block for the non-dynamic pointer input and BOTH a clickable{} and
+ * pointer input block (awaitPointerEventScope + awaitPointerEvent) for the dynamic pointer
+ * inputs (both on same Box).
+ * Both the dynamic Pointer and clickable{} are disabled to start and then enabled.
+ * Event sequences:
+ * 1. Touch "click" (down/move/up)
+ * 2. Assert
+ * 3. Touch down
+ * 4. Assert
+ * 5. Touch move
+ * 6. Assert
+ * 7. Touch up
+ * 8. Assert
+ */
@Test
- fun dynamicInputModifiersInTouchStream_addsAboveClickableWithUnitKey_triggersAllModifiers() {
+ fun dynamicInputAndClickableModifier_addsAbovePointerInputWithUnitKeyTouchEventsWithMove() {
var activeDynamicClickable by mutableStateOf(false)
var dynamicClickableCounter by mutableStateOf(0)
@@ -1676,6 +1771,10 @@
.dynamicClickableModifier(activeDynamicClickable) {
dynamicClickableCounter++
}
+ // Note the .background() above the static pointer input block
+ // TODO (jjw): Remove once bug fixed for when a dynamic pointer input follows
+ // directly after another pointer input (both using Unit key).
+ // Workaround: add a modifier between them OR use unique keys (that is, not Unit)
.background(Color.Green)
.pointerInput(Unit) {
originalPointerInputLambdaExecutionCount++
@@ -1768,13 +1867,19 @@
}
}
- /*
- * Tests adding dynamic modifier with COMPLETE mouse events, that is, the expected events from
- * using a hardware device with an Android device.
+ /* Uses clickable{} for the non-dynamic pointer input and pointer input
+ * block (awaitPointerEventScope + awaitPointerEvent) for the dynamic pointer input (both
+ * on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Mouse enter
+ * 2. Mouse "click()" (press/release)
+ * 3. Mouse exit
+ * 4. Assert
*/
@OptIn(ExperimentalTestApi::class)
@Test
- fun dynamicInputModifierMouse_addsAboveClickableWithKey_triggersBothModifiers() {
+ fun dynamicInputModifier_addsAboveClickableWithKeyMouseEvents_correctEvents() {
var clickableClickCounter by mutableStateOf(0)
// Note: I'm tracking press instead of release because clickable{} consumes release
var dynamicPressCounter by mutableStateOf(0)
@@ -1820,13 +1925,19 @@
}
}
- /*
- * Tests adding dynamic modifier with COMPLETE mouse events, that is, the expected events from
- * using a hardware device with an Android device.
+ /* Uses clickable{} for the non-dynamic pointer input and pointer input
+ * block (awaitPointerEventScope + awaitPointerEvent) for the dynamic pointer input (both
+ * on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Mouse enter
+ * 2. Mouse "click()" (press/release)
+ * 3. Mouse exit
+ * 4. Assert
*/
@OptIn(ExperimentalTestApi::class)
@Test
- fun dynamicInputModifierMouse_addsAboveClickableWithUnitKey_triggersInBothModifiers() {
+ fun dynamicInputModifier_addsAboveClickableWithUnitKeyMouseEvents_correctEvents() {
var clickableClickCounter by mutableStateOf(0)
// Note: I'm tracking press instead of release because clickable{} consumes release
var dynamicPressCounter by mutableStateOf(0)
@@ -1871,15 +1982,26 @@
}
}
- /* Tests dynamically adding a pointer input DURING an event stream (specifically, Hover).
- * Hover is the only scenario where you can add a new pointer input modifier during the event
- * stream AND receive events in the same active stream from that new pointer input modifier.
- * It isn't possible in the down/up scenario because you add the new modifier during the down
- * but you don't get another down until the next event stream.
+ /* Uses pointer input block (awaitPointerEventScope + awaitPointerEvent) for both the
+ * non-dynamic pointer input and the dynamic pointer input (both on same Box).
+ *
+ * Tests dynamically adding a pointer input ABOVE an existing pointer input DURING an
+ * event stream (specifically, Hover).
+ *
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Mouse enter
+ * 2. Assert
+ * 3. Mouse press
+ * 4. Assert
+ * 5. Mouse release
+ * 6. Assert
+ * 7. Mouse exit
+ * 8. Assert
*/
@OptIn(ExperimentalTestApi::class)
@Test
- fun dynamicInputModifierHoverMouse_addsAbovePointerInputWithUnitKey_triggersBothModifiers() {
+ fun dynamicInputModifier_addsAbovePointerInputWithUnitKeyMouseEvents_correctEvents() {
var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
var originalPointerInputEventCounter by mutableStateOf(0)
@@ -1959,14 +2081,17 @@
}
}
- /* This is the same as the test above, but
- * 1. Using clickable{}
- * 2. It enables the dynamic pointer input and starts the hover event stream in a more
- * hacky way (using mouse click without hover which triggers hover enter on release).
+ /* Uses clickable{} for the non-dynamic pointer input and pointer input
+ * block (awaitPointerEventScope + awaitPointerEvent) for the dynamic pointer input (both
+ * on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Mouse "click" (incomplete [down/up only], does not include expected hover in/out)
+ * 2. Assert
*/
@OptIn(ExperimentalTestApi::class)
@Test
- fun dynamicInputModifierIncompleteMouse_addsAboveClickableHackyEvents_triggersBothModifiers() {
+ fun dynamicInputModifier_addsAboveClickableIncompleteMouseEvents_correctEvents() {
var clickableClickCounter by mutableStateOf(0)
// Note: I'm tracking press instead of release because clickable{} consumes release
var dynamicPressCounter by mutableStateOf(0)
@@ -1989,16 +2114,6 @@
)
}
- // Usually, a proper event stream from hardware for mouse input would be:
- // - enter() (hover enter)
- // - click()
- // - exit()
- // However, in this case, I'm just calling click() which triggers actions:
- // - press
- // - release
- // - hover enter
- // This starts a hover event stream (in a more hacky way) and also enables the dynamic
- // pointer input to start recording events.
rule.onNodeWithTag("myClickable").performMouseInput {
click()
}
@@ -2018,6 +2133,1749 @@
}
}
+ /* Uses pointer input block (awaitPointerEventScope + awaitPointerEvent) for both the
+ * non-dynamic pointer input and the dynamic pointer input (both on same Box).
+ *
+ * Tests dynamically adding a pointer input AFTER an existing pointer input DURING an
+ * event stream (specifically, Hover).
+ * Hover is the only scenario where you can add a new pointer input modifier during the event
+ * stream AND receive events in the same active stream from that new pointer input modifier.
+ * It isn't possible in the down/up scenario because you add the new modifier during the down
+ * but you don't get another down until the next event stream.
+ *
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Mouse enter
+ * 2. Assert
+ * 3. Mouse press
+ * 4. Assert
+ * 5. Mouse release
+ * 6. Assert
+ * 7. Mouse exit
+ * 8. Assert
+ */
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputModifier_addsBelowPointerInputWithUnitKeyMouseEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEventCounter by mutableStateOf(0)
+
+ var dynamicPressCounter by mutableStateOf(0)
+ var dynamicReleaseCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier
+ .size(200.dp)
+ .testTag("myClickable")
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ awaitPointerEvent()
+ originalPointerInputEventCounter++
+ activateDynamicPointerInput = true
+ }
+ }
+ }
+ .dynamicPointerInputModifier(
+ enabled = activateDynamicPointerInput,
+ onPress = {
+ dynamicPressCounter++
+ },
+ onRelease = {
+ dynamicReleaseCounter++
+ }
+ )
+ )
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ enter()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEventCounter)
+ assertEquals(0, dynamicPressCounter)
+ assertEquals(0, dynamicReleaseCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ press()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ // Both the original and enabled dynamic pointer input modifiers will get the event
+ // since they are on the same Box.
+ assertEquals(2, originalPointerInputEventCounter)
+ assertEquals(1, dynamicPressCounter)
+ assertEquals(0, dynamicReleaseCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ assertTrue(activateDynamicPointerInput)
+ release()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(3, originalPointerInputEventCounter)
+ assertEquals(1, dynamicPressCounter)
+ assertEquals(1, dynamicReleaseCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ exit()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(4, originalPointerInputEventCounter)
+ assertEquals(1, dynamicPressCounter)
+ assertEquals(1, dynamicReleaseCounter)
+ }
+ }
+
+ /* Uses pointer input block (awaitPointerEventScope + awaitPointerEvent) for both the
+ * non-dynamic pointer input and the dynamic pointer input (both on same Box).
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Mouse "click" (incomplete [down/up only], does not include expected hover in/out)
+ * 2. Assert
+ */
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputModifier_addsBelowPointerInputUnitKeyIncompleteMouseEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEventCounter by mutableStateOf(0)
+
+ var dynamicPressCounter by mutableStateOf(0)
+ var dynamicReleaseCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier
+ .size(200.dp)
+ .testTag("myClickable")
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ awaitPointerEvent()
+ originalPointerInputEventCounter++
+ activateDynamicPointerInput = true
+ }
+ }
+ }
+ .dynamicPointerInputModifier(
+ enabled = activateDynamicPointerInput,
+ onPress = {
+ dynamicPressCounter++
+ },
+ onRelease = {
+ dynamicReleaseCounter++
+ }
+ )
+ )
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ click()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(3, originalPointerInputEventCounter) // Enter, Press, Release
+ assertEquals(0, dynamicPressCounter)
+ assertEquals(0, dynamicReleaseCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ click()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ // Because the mouse is still within the box area, Compose doesn't need to trigger an
+ // Exit. Instead, it just triggers two events (Press and Release) which is why the
+ // total is only 5.
+ assertEquals(5, originalPointerInputEventCounter) // Press, Release
+ assertEquals(1, dynamicPressCounter)
+ assertEquals(1, dynamicReleaseCounter)
+ }
+ }
+
+ /* The next set of tests uses two nested boxes inside a box. The two nested boxes each contain
+ * their own pointer input modifier (vs. the tests above that apply two pointer input modifiers
+ * to the same box).
+ */
+
+ /* Uses pointer input block (awaitPointerEventScope + awaitPointerEvent) for both the
+ * non-dynamic pointer input and the dynamic pointer input.
+ * The Dynamic Pointer is disabled to start and then enabled.
+ * Event sequences:
+ * 1. Mouse "click" (incomplete [down/up only], does not include expected hover in/out)
+ * 2. Assert
+ */
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputNestedBox_addsBelowPointerInputUnitKeyIncompleteMouseEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEventCounter by mutableStateOf(0)
+
+ var dynamicPressCounter by mutableStateOf(0)
+ var dynamicReleaseCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ awaitPointerEvent()
+ originalPointerInputEventCounter++
+ activateDynamicPointerInput = true
+ }
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifier(
+ enabled = activateDynamicPointerInput,
+ onPress = {
+ dynamicPressCounter++
+ },
+ onRelease = {
+ dynamicReleaseCounter++
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ click()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(3, originalPointerInputEventCounter)
+ assertEquals(0, dynamicPressCounter)
+ assertEquals(0, dynamicReleaseCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ click()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(3, originalPointerInputEventCounter)
+ assertEquals(1, dynamicPressCounter)
+ assertEquals(1, dynamicReleaseCounter)
+ }
+ }
+
+ /* Uses pointer input block (awaitPointerEventScope + awaitPointerEvent) for both the
+ * non-dynamic pointer input and the dynamic pointer input.
+ * The Dynamic Pointer is disabled to start, then enabled, and finally disabled.
+ * Event sequences:
+ * 1. Mouse "click" (incomplete [down/up only], does not include expected hover in/out)
+ * 2. Assert
+ */
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputNestedBox_togglesBelowPointerInputUnitKeyIncompleteMouseEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEventCounter by mutableStateOf(0)
+
+ var dynamicPressCounter by mutableStateOf(0)
+ var dynamicReleaseCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ originalPointerInputEventCounter++
+
+ // Note: We only set the activateDynamicPointerInput to true on
+ // Release because we do not want it set on just any event.
+ // Specifically, we do not want it set on Exit, because, in the
+ // case of this event, the exit will be triggered around the same
+ // time as the other dynamic pointer input receives a press (when
+ // it is enabled) because, as soon as that gets that event, Compose
+ // sees this box no longer the hit target (the box with the dynamic
+ // pointer input is now), so it triggers an exit on this original
+ // non-dynamic pointer input. If we allowed
+ // activateDynamicPointerInput to be set during any event, it would
+ // undo us setting activateDynamicPointerInput to false in the other
+ // pointer input handler.
+ if (event.type == PointerEventType.Release) {
+ activateDynamicPointerInput = true
+ }
+ }
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Cyan)
+ .dynamicPointerInputModifier(
+ enabled = activateDynamicPointerInput,
+ onPress = {
+ dynamicPressCounter++
+ },
+ onRelease = {
+ dynamicReleaseCounter++
+ activateDynamicPointerInput = false
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ click()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(3, originalPointerInputEventCounter)
+ assertEquals(0, dynamicPressCounter)
+ assertEquals(0, dynamicReleaseCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ click()
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(3, originalPointerInputEventCounter)
+ assertEquals(1, dynamicPressCounter)
+ assertEquals(1, dynamicReleaseCounter)
+ }
+ }
+
+ /* Uses Foundation's detectTapGestures{} for the non-dynamic pointer input. The dynamic pointer
+ * input uses the lower level pointer input commands.
+ * The Dynamic Pointer is disabled to start, then enabled, and finally disabled.
+ * Event sequences:
+ * 1. Mouse "click" (incomplete [down/up only], does not include expected hover in/out)
+ * 2. Assert
+ */
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputNestedBoxGesture_togglesBelowWithUnitKeyIncompleteMouseEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEventCounter by mutableStateOf(0)
+
+ var dynamicPressCounter by mutableStateOf(0)
+ var dynamicReleaseCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+ detectTapGestures {
+ originalPointerInputEventCounter++
+ activateDynamicPointerInput = true
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifier(
+ enabled = activateDynamicPointerInput,
+ onPress = {
+ dynamicPressCounter++
+ },
+ onRelease = {
+ dynamicReleaseCounter++
+ activateDynamicPointerInput = false
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ click()
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEventCounter)
+ assertEquals(0, dynamicPressCounter)
+ assertEquals(0, dynamicReleaseCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ click()
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEventCounter)
+ assertEquals(1, dynamicPressCounter)
+ assertEquals(1, dynamicReleaseCounter)
+ }
+ }
+
+ /* Uses Foundation's detectTapGestures{} for both the non-dynamic pointer input and the
+ * dynamic pointer input (vs. the lower level pointer input commands).
+ * The Dynamic Pointer is disabled to start, then enabled, and finally disabled.
+ * Event sequences:
+ * 1. Touch "click" (down/move/up)
+ * 2. Assert
+ */
+ @Test
+ fun dynamicInputNestedBoxGesture_togglesBelowWithKeyTouchEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputTapGestureCounter by mutableStateOf(0)
+
+ var dynamicTapGestureCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput("myUniqueKey1") {
+ originalPointerInputLambdaExecutionCount++
+
+ detectTapGestures {
+ originalPointerInputTapGestureCounter++
+ activateDynamicPointerInput = true
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifierWithDetectTapGestures(
+ enabled = activateDynamicPointerInput,
+ key = "myUniqueKey2",
+ onTap = {
+ dynamicTapGestureCounter++
+ activateDynamicPointerInput = false
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ // This command is the same as
+ // rule.onNodeWithTag("myClickable").performTouchInput { click() }
+ rule.onNodeWithTag("myClickable").performClick()
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputTapGestureCounter)
+ assertEquals(0, dynamicTapGestureCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performClick()
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputTapGestureCounter)
+ assertFalse(activateDynamicPointerInput)
+ assertEquals(1, dynamicTapGestureCounter)
+ }
+ }
+
+ /*
+ * The next four tests are based on the test above (nested boxes using a pointer input
+ * modifier blocks with the Foundation Gesture detectTapGestures{}).
+ *
+ * The difference is the dynamic pointer input modifier is enabled to start (while in the
+ * other tests it is disabled to start).
+ *
+ * The tests below tests out variations (mouse vs. touch and Unit keys vs. unique keys).
+ */
+
+ /* Dynamic Pointer enabled to start, disabled, then re-enabled (uses UNIQUE key)
+ * Event sequences:
+ * 1. Touch "click" (down/move/up)
+ * 2. Assert
+ */
+ @Test
+ fun dynamicInputNestedBoxGesture_togglesBelowOffWithKeyTouchEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputTapGestureCounter by mutableStateOf(0)
+
+ var dynamicTapGestureCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(true)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput("myUniqueKey1") {
+ originalPointerInputLambdaExecutionCount++
+
+ detectTapGestures {
+ originalPointerInputTapGestureCounter++
+ activateDynamicPointerInput = true
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifierWithDetectTapGestures(
+ enabled = activateDynamicPointerInput,
+ key = "myUniqueKey2",
+ onTap = {
+ dynamicTapGestureCounter++
+ activateDynamicPointerInput = false
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performClick()
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ assertEquals(0, originalPointerInputLambdaExecutionCount)
+ assertEquals(0, originalPointerInputTapGestureCounter)
+ // Since second box is created following first box, it will get the event
+ assertEquals(1, dynamicTapGestureCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performClick()
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputTapGestureCounter)
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, dynamicTapGestureCounter)
+ }
+ }
+
+ /* Dynamic Pointer enabled to start, disabled, then re-enabled (uses UNIT for key)
+ * Event sequences:
+ * 1. Touch "click" (down/move/up)
+ * 2. Assert
+ */
+ @Test
+ fun dynamicInputNestedBoxGesture_togglesBelowOffWithUnitKeyTouchEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputTapGestureCounter by mutableStateOf(0)
+
+ var dynamicTapGestureCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(true)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+
+ detectTapGestures {
+ originalPointerInputTapGestureCounter++
+ activateDynamicPointerInput = true
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifierWithDetectTapGestures(
+ enabled = activateDynamicPointerInput,
+ key = Unit,
+ onTap = {
+ dynamicTapGestureCounter++
+ activateDynamicPointerInput = false
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performClick()
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ assertEquals(0, originalPointerInputLambdaExecutionCount)
+ assertEquals(0, originalPointerInputTapGestureCounter)
+ // Since second box is created following first box, it will get the event
+ assertEquals(1, dynamicTapGestureCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performClick()
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputTapGestureCounter)
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, dynamicTapGestureCounter)
+ }
+ }
+
+ /* Dynamic Pointer enabled to start, disabled, then re-enabled (uses UNIQUE key)
+ * Event sequences:
+ * 1. Mouse "click" (incomplete [down/up only], does not include expected hover in/out)
+ * 2. Assert
+ */
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputNestedBoxGesture_togglesBelowOffWithKeyIncompleteMouseEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputTapGestureCounter by mutableStateOf(0)
+
+ var dynamicTapGestureCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(true)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput("myUniqueKey1") {
+ originalPointerInputLambdaExecutionCount++
+
+ detectTapGestures {
+ originalPointerInputTapGestureCounter++
+ activateDynamicPointerInput = true
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifierWithDetectTapGestures(
+ enabled = activateDynamicPointerInput,
+ key = "myUniqueKey2",
+ onTap = {
+ dynamicTapGestureCounter++
+ activateDynamicPointerInput = false
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput { click() }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ assertEquals(0, originalPointerInputLambdaExecutionCount)
+ assertEquals(0, originalPointerInputTapGestureCounter)
+ // Since second box is created following first box, it will get the event
+ assertEquals(1, dynamicTapGestureCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput { click() }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputTapGestureCounter)
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, dynamicTapGestureCounter)
+ }
+ }
+
+ /* Dynamic Pointer enabled to start, disabled, then re-enabled (uses Unit for key)
+ * Event sequences:
+ * 1. Mouse "click" (incomplete [down/up only], does not include expected hover in/out)
+ * 2. Assert
+ */
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputNestedBoxGesture_togglesBelowOffUnitKeyIncompleteMouseEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputTapGestureCounter by mutableStateOf(0)
+
+ var dynamicTapGestureCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(true)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+
+ detectTapGestures {
+ originalPointerInputTapGestureCounter++
+ activateDynamicPointerInput = true
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifierWithDetectTapGestures(
+ enabled = activateDynamicPointerInput,
+ key = Unit,
+ onTap = {
+ dynamicTapGestureCounter++
+ activateDynamicPointerInput = false
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertTrue(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput { click() }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ assertEquals(0, originalPointerInputLambdaExecutionCount)
+ assertEquals(0, originalPointerInputTapGestureCounter)
+ // Since second box is created following first box, it will get the event
+ assertEquals(1, dynamicTapGestureCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput { click() }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputTapGestureCounter)
+ assertTrue(activateDynamicPointerInput)
+ assertEquals(1, dynamicTapGestureCounter)
+ }
+ }
+ // !!!!! MOUSE & TOUCH EVENTS TESTS WITH DYNAMIC MODIFIER INPUT TESTS SECTION (END) !!!!!
+
+ // !!!!! HOVER EVENTS ONLY WITH DYNAMIC MODIFIER INPUT TESTS SECTION (START) !!!!!
+ /* These tests dynamically add a pointer input BEFORE or AFTER an existing pointer input DURING
+ * an event stream (specifically, Hover). Some tests use unique keys while others use UNIT as
+ * the key. Finally, some of the tests apply the modifiers to the same Box while others use
+ * sibling blocks (read the test name for details).
+ *
+ * Test name explains the test.
+ * All tests start with the dynamic pointer disabled and enable it on the first hover enter
+ *
+ * Event sequences:
+ * 1. Hover enter
+ * 2. Assert
+ * 3. Move
+ * 4. Assert
+ * 5. Move
+ * 6. Assert
+ * 7. Hover exit
+ * 8. Assert
+ */
+
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputModifier_addsAbovePointerInputWithUnitKeyHoverEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEnterEventCounter by mutableStateOf(0)
+ var originalPointerInputMoveEventCounter by mutableStateOf(0)
+ var originalPointerInputExitEventCounter by mutableStateOf(0)
+
+ var dynamicEnterCounter by mutableStateOf(0)
+ var dynamicMoveCounter by mutableStateOf(0)
+ var dynamicExitCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier
+ .size(200.dp)
+ .testTag("myClickable")
+ .dynamicPointerInputModifier(
+ enabled = activateDynamicPointerInput,
+ onEnter = {
+ dynamicEnterCounter++
+ },
+ onMove = {
+ dynamicMoveCounter++
+ },
+ onExit = {
+ dynamicExitCounter++
+ }
+ )
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Enter -> {
+ originalPointerInputEnterEventCounter++
+ activateDynamicPointerInput = true
+ }
+ PointerEventType.Move -> {
+ originalPointerInputMoveEventCounter++
+ }
+ PointerEventType.Exit -> {
+ originalPointerInputExitEventCounter++
+ }
+ }
+ }
+ }
+ }
+ )
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ enter()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ // Because the dynamic pointer input is added after the "enter" event (and it is part of the
+ // same modifier chain), it will receive events as well now.
+ // (Because the dynamic modifier is added BEFORE the original modifier, it WILL reset the
+ // event stream for the original modifier.)
+ // Original pointer input: Hover Exit event then a Hover Enter event
+ // Dynamic pointer input: Hover enter event
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(2, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(2, originalPointerInputEnterEventCounter)
+ assertEquals(1, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ exit()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(2, originalPointerInputEnterEventCounter)
+ assertEquals(1, originalPointerInputMoveEventCounter)
+ assertEquals(2, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(1, dynamicExitCounter)
+ }
+ }
+
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputModifier_addsAbovePointerInputWithKeyHoverEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEnterEventCounter by mutableStateOf(0)
+ var originalPointerInputMoveEventCounter by mutableStateOf(0)
+ var originalPointerInputExitEventCounter by mutableStateOf(0)
+
+ var dynamicEnterCounter by mutableStateOf(0)
+ var dynamicMoveCounter by mutableStateOf(0)
+ var dynamicExitCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier
+ .size(200.dp)
+ .testTag("myClickable")
+ .dynamicPointerInputModifier(
+ key = "unique_key_1234",
+ enabled = activateDynamicPointerInput,
+ onEnter = {
+ dynamicEnterCounter++
+ },
+ onMove = {
+ dynamicMoveCounter++
+ },
+ onExit = {
+ dynamicExitCounter++
+ }
+ )
+ .background(Color.Green)
+ .pointerInput("unique_key_5678") {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Enter -> {
+ originalPointerInputEnterEventCounter++
+ activateDynamicPointerInput = true
+ }
+ PointerEventType.Move -> {
+ originalPointerInputMoveEventCounter++
+ }
+ PointerEventType.Exit -> {
+ originalPointerInputExitEventCounter++
+ }
+ }
+ }
+ }
+ }
+ )
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ enter()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ // Because the dynamic pointer input is added after the "enter" event (and it is part of the
+ // same modifier chain), it will receive events as well now.
+ // (Because the dynamic modifier is added BEFORE the original modifier, it WILL reset the
+ // event stream for the original modifier.)
+ // Original pointer input: Hover Exit event then a Hover Enter event
+ // Dynamic pointer input: Hover enter event
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(2, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(2, originalPointerInputEnterEventCounter)
+ assertEquals(1, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ exit()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(2, originalPointerInputEnterEventCounter)
+ assertEquals(1, originalPointerInputMoveEventCounter)
+ assertEquals(2, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(1, dynamicExitCounter)
+ }
+ }
+
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputModifier_addsBelowPointerInputWithUnitKeyHoverEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEnterEventCounter by mutableStateOf(0)
+ var originalPointerInputMoveEventCounter by mutableStateOf(0)
+ var originalPointerInputExitEventCounter by mutableStateOf(0)
+
+ var dynamicEnterCounter by mutableStateOf(0)
+ var dynamicMoveCounter by mutableStateOf(0)
+ var dynamicExitCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier
+ .size(200.dp)
+ .testTag("myClickable")
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Enter -> {
+ originalPointerInputEnterEventCounter++
+ activateDynamicPointerInput = true
+ }
+ PointerEventType.Move -> {
+ originalPointerInputMoveEventCounter++
+ }
+ PointerEventType.Exit -> {
+ originalPointerInputExitEventCounter++
+ }
+ }
+ }
+ }
+ }
+ .background(Color.Green)
+ .dynamicPointerInputModifier(
+ enabled = activateDynamicPointerInput,
+ onEnter = {
+ dynamicEnterCounter++
+ },
+ onMove = {
+ dynamicMoveCounter++
+ },
+ onExit = {
+ dynamicExitCounter++
+ }
+ )
+ )
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ enter()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ // Because the dynamic pointer input is added after the "enter" event (and it is part of the
+ // same modifier chain), it will receive events as well now.
+ // (Because the dynamic modifier is added BELOW the original modifier, it will not reset the
+ // event stream for the original modifier.)
+ // Original pointer input: Move event
+ // Dynamic pointer input: Hover enter event
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(1, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(2, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ exit()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(2, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(1, dynamicExitCounter)
+ }
+ }
+
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputModifier_addsBelowPointerInputWithKeyHoverEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEnterEventCounter by mutableStateOf(0)
+ var originalPointerInputMoveEventCounter by mutableStateOf(0)
+ var originalPointerInputExitEventCounter by mutableStateOf(0)
+
+ var dynamicEnterCounter by mutableStateOf(0)
+ var dynamicMoveCounter by mutableStateOf(0)
+ var dynamicExitCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier
+ .size(200.dp)
+ .testTag("myClickable")
+ .pointerInput("unique_key_5678") {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Enter -> {
+ originalPointerInputEnterEventCounter++
+ activateDynamicPointerInput = true
+ }
+ PointerEventType.Move -> {
+ originalPointerInputMoveEventCounter++
+ }
+ PointerEventType.Exit -> {
+ originalPointerInputExitEventCounter++
+ }
+ }
+ }
+ }
+ }
+ .background(Color.Green)
+ .dynamicPointerInputModifier(
+ key = "unique_key_1234",
+ enabled = activateDynamicPointerInput,
+ onEnter = {
+ dynamicEnterCounter++
+ },
+ onMove = {
+ dynamicMoveCounter++
+ },
+ onExit = {
+ dynamicExitCounter++
+ }
+ )
+ )
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ enter()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ // Because the dynamic pointer input is added after the "enter" event (and it is part of the
+ // same modifier chain), it will receive events as well now.
+ // (Because the dynamic modifier is added BELOW the original modifier, it will not reset the
+ // event stream for the original modifier.)
+ // Original pointer input: Move event
+ // Dynamic pointer input: Hover enter event
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(1, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(2, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ exit()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(2, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(1, dynamicExitCounter)
+ }
+ }
+
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputNestedBox_addsBelowPointerInputUnitKeyHoverEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEnterEventCounter by mutableStateOf(0)
+ var originalPointerInputMoveEventCounter by mutableStateOf(0)
+ var originalPointerInputExitEventCounter by mutableStateOf(0)
+
+ var dynamicEnterCounter by mutableStateOf(0)
+ var dynamicMoveCounter by mutableStateOf(0)
+ var dynamicExitCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Enter -> {
+ originalPointerInputEnterEventCounter++
+ activateDynamicPointerInput = true
+ }
+ PointerEventType.Move -> {
+ originalPointerInputMoveEventCounter++
+ }
+ PointerEventType.Exit -> {
+ originalPointerInputExitEventCounter++
+ }
+ }
+ }
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifier(
+ enabled = activateDynamicPointerInput,
+ onEnter = {
+ dynamicEnterCounter++
+ },
+ onMove = {
+ dynamicMoveCounter++
+ },
+ onExit = {
+ dynamicExitCounter++
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ enter()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ exit()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(1, dynamicExitCounter)
+ }
+ }
+
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputNestedBox_addsBelowPointerInputKeyHoverEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEnterEventCounter by mutableStateOf(0)
+ var originalPointerInputMoveEventCounter by mutableStateOf(0)
+ var originalPointerInputExitEventCounter by mutableStateOf(0)
+
+ var dynamicEnterCounter by mutableStateOf(0)
+ var dynamicMoveCounter by mutableStateOf(0)
+ var dynamicExitCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput("unique_key_1234") {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Enter -> {
+ originalPointerInputEnterEventCounter++
+ activateDynamicPointerInput = true
+ }
+ PointerEventType.Move -> {
+ originalPointerInputMoveEventCounter++
+ }
+ PointerEventType.Exit -> {
+ originalPointerInputExitEventCounter++
+ }
+ }
+ }
+ }
+ }
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifier(
+ key = "unique_key_5678",
+ enabled = activateDynamicPointerInput,
+ onEnter = {
+ dynamicEnterCounter++
+ },
+ onMove = {
+ dynamicMoveCounter++
+ },
+ onExit = {
+ dynamicExitCounter++
+ }
+ )
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ enter()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ exit()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(1, dynamicEnterCounter)
+ assertEquals(1, dynamicMoveCounter)
+ assertEquals(1, dynamicExitCounter)
+ }
+ }
+
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputNestedBox_addsAbovePointerInputUnitKeyHoverEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEnterEventCounter by mutableStateOf(0)
+ var originalPointerInputMoveEventCounter by mutableStateOf(0)
+ var originalPointerInputExitEventCounter by mutableStateOf(0)
+
+ var dynamicEnterCounter by mutableStateOf(0)
+ var dynamicMoveCounter by mutableStateOf(0)
+ var dynamicExitCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifier(
+ enabled = activateDynamicPointerInput,
+ onEnter = {
+ dynamicEnterCounter++
+ },
+ onMove = {
+ dynamicMoveCounter++
+ },
+ onExit = {
+ dynamicExitCounter++
+ }
+ )
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput(Unit) {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Enter -> {
+ originalPointerInputEnterEventCounter++
+ activateDynamicPointerInput = true
+ }
+ PointerEventType.Move -> {
+ originalPointerInputMoveEventCounter++
+ }
+ PointerEventType.Exit -> {
+ originalPointerInputExitEventCounter++
+ }
+ }
+ }
+ }
+ }
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ enter()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(1, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(2, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ exit()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(2, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+ }
+
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ fun dynamicInputNestedBox_addsAbovePointerInputKeyHoverEvents_correctEvents() {
+ var originalPointerInputLambdaExecutionCount by mutableStateOf(0)
+ var originalPointerInputEnterEventCounter by mutableStateOf(0)
+ var originalPointerInputMoveEventCounter by mutableStateOf(0)
+ var originalPointerInputExitEventCounter by mutableStateOf(0)
+
+ var dynamicEnterCounter by mutableStateOf(0)
+ var dynamicMoveCounter by mutableStateOf(0)
+ var dynamicExitCounter by mutableStateOf(0)
+ var activateDynamicPointerInput by mutableStateOf(false)
+
+ rule.setContent {
+ Box(Modifier.size(100.dp).testTag("myClickable")) {
+ Box(Modifier
+ .fillMaxSize()
+ .dynamicPointerInputModifier(
+ key = "unique_key_5678",
+ enabled = activateDynamicPointerInput,
+ onEnter = {
+ dynamicEnterCounter++
+ },
+ onMove = {
+ dynamicMoveCounter++
+ },
+ onExit = {
+ dynamicExitCounter++
+ }
+ )
+ )
+ Box(Modifier
+ .fillMaxSize()
+ .background(Color.Green)
+ .pointerInput("unique_key_1234") {
+ originalPointerInputLambdaExecutionCount++
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ when (event.type) {
+ PointerEventType.Enter -> {
+ originalPointerInputEnterEventCounter++
+ activateDynamicPointerInput = true
+ }
+ PointerEventType.Move -> {
+ originalPointerInputMoveEventCounter++
+ }
+ PointerEventType.Exit -> {
+ originalPointerInputExitEventCounter++
+ }
+ }
+ }
+ }
+ }
+ )
+ }
+ }
+
+ rule.runOnIdle {
+ assertFalse(activateDynamicPointerInput)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ enter()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(0, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(1, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ moveBy(Offset(1.0f, 1.0f))
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(2, originalPointerInputMoveEventCounter)
+ assertEquals(0, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+
+ rule.onNodeWithTag("myClickable").performMouseInput {
+ exit()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, originalPointerInputLambdaExecutionCount)
+ assertEquals(1, originalPointerInputEnterEventCounter)
+ assertEquals(2, originalPointerInputMoveEventCounter)
+ assertEquals(1, originalPointerInputExitEventCounter)
+ assertEquals(0, dynamicEnterCounter)
+ assertEquals(0, dynamicMoveCounter)
+ assertEquals(0, dynamicExitCounter)
+ }
+ }
+ // !!!!! HOVER EVENTS ONLY WITH DYNAMIC MODIFIER INPUT TESTS SECTION (END) !!!!!
+
@OptIn(ExperimentalTestApi::class)
@Test
@LargeTest
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt
index b67d4b3..ef97634 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt
@@ -163,7 +163,7 @@
@Test
fun longClickSemantics() {
var counter = 0
- val onClick: () -> Unit = { ++counter }
+ val onLongClick: () -> Unit = { ++counter }
rule.setContent {
Box {
@@ -171,7 +171,7 @@
"ClickableText",
modifier = Modifier
.testTag("myClickable")
- .combinedClickable(onLongClick = onClick) {}
+ .combinedClickable(onLongClick = onLongClick) {}
)
}
}
@@ -193,6 +193,59 @@
}
@Test
+ fun changingLongClickSemantics() {
+ var counter = 0
+ var onLongClick: (() -> Unit)? by mutableStateOf(null)
+
+ rule.setContent {
+ Box {
+ BasicText(
+ "ClickableText",
+ modifier = Modifier
+ .testTag("myClickable")
+ .combinedClickable(onLongClick = onLongClick) {}
+ )
+ }
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .assertIsEnabled()
+ .assert(SemanticsMatcher.keyNotDefined(SemanticsActions.OnLongClick))
+
+ rule.runOnIdle {
+ // Add a no-op long click
+ onLongClick = { /* no-op */ }
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .assertIsEnabled()
+ .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.OnLongClick))
+ .performSemanticsAction(SemanticsActions.OnLongClick)
+
+ rule.runOnIdle {
+ // no-op long click handler
+ assertThat(counter).isEqualTo(0)
+ // Change to mutate counter
+ onLongClick = { ++counter }
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .performSemanticsAction(SemanticsActions.OnLongClick)
+
+ rule.runOnIdle {
+ // Changes should now be applied
+ assertThat(counter).isEqualTo(1)
+ // Make onLongClick null
+ onLongClick = null
+ }
+
+ rule.onNodeWithTag("myClickable")
+ .assertIsEnabled()
+ // Long click action should be removed
+ .assert(SemanticsMatcher.keyNotDefined(SemanticsActions.OnLongClick))
+ }
+
+ @Test
fun click() {
var counter = 0
val onClick: () -> Unit = {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/FocusableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/FocusableTest.kt
index bf9e395..21fb754 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/FocusableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/FocusableTest.kt
@@ -695,7 +695,7 @@
}
@Test
- fun movableContent_movedContentRemainsFocused() {
+ fun movableContent_movedContentBecomesUnfocused() {
var moveContent by mutableStateOf(false)
val focusRequester = FocusRequester()
val interactionSource = MutableInteractionSource()
@@ -752,15 +752,23 @@
moveContent = true // moving content
}
- // Assert that focus is kept during movable content change.
+ // Assert that focus is reset
rule.runOnIdle {
- assertThat(state.isFocused).isTrue()
+ assertThat(state.isFocused).isFalse()
}
rule.onNodeWithTag(focusTag)
- .assertIsFocused()
+ .assertIsNotFocused()
- // Checks if we still received the correct Focus/Unfocus events. When moving contents, the
- // focus event node will send a sequence of Focus/Unfocus/Focus events.
+ rule.runOnIdle {
+ assertThat(interactions.first()).isInstanceOf(FocusInteraction.Focus::class.java)
+ assertThat(interactions[1]).isInstanceOf(FocusInteraction.Unfocus::class.java)
+ }
+
+ rule.runOnIdle {
+ focusRequester.requestFocus() // request focus again
+ assertThat(state.isFocused).isTrue()
+ }
+
rule.runOnIdle {
assertThat(interactions.first()).isInstanceOf(FocusInteraction.Focus::class.java)
assertThat(interactions[1]).isInstanceOf(FocusInteraction.Unfocus::class.java)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/MagnifierTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/MagnifierTest.kt
index c1da808..c49618d5 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/MagnifierTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/MagnifierTest.kt
@@ -409,6 +409,10 @@
fun platformMagnifierModifier_updatesProperties_whenZoomChanged() {
var zoom by mutableStateOf(1f)
val platformMagnifier = CountingPlatformMagnifier()
+ val factory = PlatformMagnifierFactory(
+ platformMagnifier,
+ canUpdateZoom = true
+ )
rule.setContent {
Box(
Modifier.magnifier(
@@ -416,10 +420,7 @@
magnifierCenter = { Offset.Unspecified },
zoom = zoom,
onSizeChanged = null,
- platformMagnifierFactory = PlatformMagnifierFactory(
- platformMagnifier,
- canUpdateZoom = true
- )
+ platformMagnifierFactory = factory
)
)
}
@@ -556,7 +557,7 @@
@SdkSuppress(minSdkVersion = 28)
@Test
- fun platformMagnifierModifier_firesOnSizeChanged_initially_whenSourceCenterUnspecified() {
+ fun platformMagnifierModifier_doesNotFireOnSizeChanged_initially_whenSourceCenterUnspecified() {
val magnifierSize = IntSize(10, 11)
val sizeEvents = mutableListOf<DpSize>()
val platformMagnifier = CountingPlatformMagnifier().apply {
@@ -574,6 +575,34 @@
)
}
+ rule.runOnIdle { assertThat(sizeEvents).isEmpty() }
+ }
+
+ @SdkSuppress(minSdkVersion = 28)
+ @Test
+ fun platformMagnifierModifier_firesOnSizeChanged_afterSourceCenterIsSpecified() {
+ val magnifierSize = IntSize(10, 11)
+ val sizeEvents = mutableListOf<DpSize>()
+ val platformMagnifier = CountingPlatformMagnifier().apply {
+ size = magnifierSize
+ }
+ var sourceCenter by mutableStateOf(Offset.Unspecified)
+ rule.setContent {
+ Box(
+ Modifier.magnifier(
+ sourceCenter = { sourceCenter },
+ magnifierCenter = { Offset.Unspecified },
+ zoom = Float.NaN,
+ onSizeChanged = { sizeEvents += it },
+ platformMagnifierFactory = PlatformMagnifierFactory(platformMagnifier)
+ )
+ )
+ }
+
+ rule.runOnIdle { assertThat(sizeEvents).isEmpty() }
+
+ sourceCenter = Offset(1f, 1f)
+
rule.runOnIdle {
assertThat(sizeEvents).containsExactly(
with(rule.density) {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuCommon.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuCommon.kt
index a173669..0b3760d 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuCommon.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuCommon.kt
@@ -59,7 +59,7 @@
onClick: () -> Unit = {},
) {
item(
- label = label,
+ label = { label },
modifier = modifier,
enabled = enabled,
leadingIcon = leadingIcon,
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapFlingBehaviorTest.kt
index 8d2c398..5f4c65d 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapFlingBehaviorTest.kt
@@ -396,7 +396,7 @@
// act and assert: next calculated offset is the first value emitted by
// remainingScrollOffset this indicates the last snap step will start
rule.mainClock.advanceTimeUntil {
- scrollOffset.last() == snapLayoutInfoProvider.calculateSnappingOffset(10000f)
+ scrollOffset.last() == snapLayoutInfoProvider.calculateSnapOffset(10000f)
}
rule.mainClock.autoAdvance = true
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProviderTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProviderTest.kt
index c372d48..b023790 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProviderTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProviderTest.kt
@@ -81,7 +81,7 @@
offset.x
}
assertEquals(
- layoutInfoProvider.calculateSnappingOffset(0f).roundToInt(),
+ layoutInfoProvider.calculateSnapOffset(0f).roundToInt(),
expectedResult
)
}
@@ -116,7 +116,7 @@
offset.x
}
assertEquals(
- layoutInfoProvider.calculateSnappingOffset(2 * minVelocityThreshold.toFloat())
+ layoutInfoProvider.calculateSnapOffset(2 * minVelocityThreshold.toFloat())
.roundToInt(),
expectedResult
)
@@ -153,7 +153,7 @@
offset.x
}
assertEquals(
- layoutInfoProvider.calculateSnappingOffset(-2 * minVelocityThreshold.toFloat())
+ layoutInfoProvider.calculateSnapOffset(-2 * minVelocityThreshold.toFloat())
.roundToInt(),
expectedResult
)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt
index d96f5fc..529265e 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt
@@ -391,7 +391,7 @@
// act and assert: next calculated offset is the first value emitted by
// remainingScrollOffset this indicates the last snap step will start
rule.mainClock.advanceTimeUntil {
- scrollOffset.last() == snapLayoutInfoProvider.calculateSnappingOffset(10000f)
+ scrollOffset.last() == snapLayoutInfoProvider.calculateSnapOffset(10000f)
}
rule.mainClock.autoAdvance = true
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProviderTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProviderTest.kt
index ffdb393..47e2214 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProviderTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProviderTest.kt
@@ -74,7 +74,7 @@
rule.runOnIdle {
assertEquals(
- layoutInfoProvider.calculateSnappingOffset(0f).roundToInt(),
+ layoutInfoProvider.calculateSnapOffset(0f).roundToInt(),
state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == 100 }?.offset ?: 0
)
}
@@ -105,7 +105,7 @@
.visibleItemsInfo
.firstOrNull { it.index == state.firstVisibleItemIndex + 1 }?.offset
assertEquals(
- layoutInfoProvider.calculateSnappingOffset(2 * minVelocityThreshold.toFloat())
+ layoutInfoProvider.calculateSnapOffset(2 * minVelocityThreshold.toFloat())
.roundToInt(),
offset ?: 0
)
@@ -137,7 +137,7 @@
.visibleItemsInfo
.firstOrNull { it.index == state.firstVisibleItemIndex }?.offset
assertEquals(
- layoutInfoProvider.calculateSnappingOffset(-2 * minVelocityThreshold.toFloat())
+ layoutInfoProvider.calculateSnapOffset(-2 * minVelocityThreshold.toFloat())
.roundToInt(),
offset ?: 0
)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehaviorTest.kt
index 3cfc45f..e6d37c8 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehaviorTest.kt
@@ -206,17 +206,17 @@
@Test
fun findClosestOffset_noFlingDirection_shouldReturnAbsoluteDistance() {
val testLayoutInfoProvider = TestLayoutInfoProvider()
- val offset = testLayoutInfoProvider.calculateSnappingOffset(0f)
+ val offset = testLayoutInfoProvider.calculateSnapOffset(0f)
assertEquals(offset, MinOffset)
}
@Test
fun findClosestOffset_flingDirection_shouldReturnCorrectBound() {
val testLayoutInfoProvider = TestLayoutInfoProvider()
- val forwardOffset = testLayoutInfoProvider.calculateSnappingOffset(with(rule.density) {
+ val forwardOffset = testLayoutInfoProvider.calculateSnapOffset(with(rule.density) {
MinFlingVelocityDp.toPx()
})
- val backwardOffset = testLayoutInfoProvider.calculateSnappingOffset(-with(rule.density) {
+ val backwardOffset = testLayoutInfoProvider.calculateSnapOffset(-with(rule.density) {
MinFlingVelocityDp.toPx()
})
assertEquals(forwardOffset, MaxOffset)
@@ -276,7 +276,7 @@
}
@Test
- fun approach_notSpecified_useHighVelocityApproachAndSnap() {
+ fun approach_usedDefaultApproach_useHighVelocityApproachAndSnap() {
val splineAnimationSpec =
InspectSplineAnimationSpec(SplineBasedFloatDecayAnimationSpec(rule.density))
val decaySpec: DecayAnimationSpec<Float> = splineAnimationSpec.generateDecayAnimationSpec()
@@ -299,14 +299,13 @@
}
@Test
- fun approach_notSpecified_canDecay_shouldDecayMinusBoundDifference() {
+ fun approach_usedDefaultApproach_shouldDecay() {
val splineAnimationSpec =
InspectSplineAnimationSpec(SplineBasedFloatDecayAnimationSpec(rule.density))
val decaySpec: DecayAnimationSpec<Float> = splineAnimationSpec.generateDecayAnimationSpec()
val flingVelocity = 5 * TestVelocity
val decayTargetOffset = decaySpec.calculateTargetValue(0.0f, flingVelocity)
val testLayoutInfoProvider = TestLayoutInfoProvider(approachOffset = Float.NaN)
- val approachOffset = decayTargetOffset - (MaxOffset - MinOffset)
var actualApproachOffset = 0f
rule.mainClock.autoAdvance = false
@@ -326,16 +325,18 @@
rule.mainClock.advanceTimeUntil { splineAnimationSpec.animationWasExecutions > 0 }
rule.runOnIdle {
- Truth.assertThat(approachOffset).isWithin(0.1f).of(actualApproachOffset)
+ Truth.assertThat(decayTargetOffset).isWithin(0.1f).of(actualApproachOffset)
}
}
@Test
- fun approach_notSpecified_cannotDecay_shouldJustSnapToBound() {
+ fun approach_cannotDecay_shouldJustSnapToBound() {
val splineAnimationSpec =
InspectSplineAnimationSpec(SplineBasedFloatDecayAnimationSpec(rule.density))
val decaySpec: DecayAnimationSpec<Float> = splineAnimationSpec.generateDecayAnimationSpec()
- val testLayoutInfoProvider = TestLayoutInfoProvider(approachOffset = Float.NaN)
+ val testLayoutInfoProvider = TestLayoutInfoProvider(
+ approachOffset = MaxOffset
+ )
var animationOffset = 0f
rule.setContent {
@@ -358,6 +359,7 @@
}
}
+ @Suppress("Deprecation")
@Test
fun disableSystemAnimations_defaultFlingBehaviorShouldContinueToWork() {
@@ -426,6 +428,7 @@
}
}
+ @Suppress("Deprecation")
@Test
fun defaultFlingBehavior_useScrollMotionDurationScale() {
// Arrange
@@ -511,20 +514,24 @@
}
}
- override fun calculateSnappingOffset(currentVelocity: Float): Float {
+ override fun calculateSnapOffset(velocity: Float): Float {
return calculateFinalOffset(
- calculateFinalSnappingItem(currentVelocity),
+ calculateFinalSnappingItem(velocity),
minOffset,
maxOffset
)
}
- override fun calculateApproachOffset(initialVelocity: Float): Float {
- return approachOffset
+ override fun calculateApproachOffset(
+ velocity: Float,
+ decayOffset: Float
+ ): Float {
+ return if (approachOffset.isNaN()) (decayOffset) else approachOffset
}
}
}
+@Suppress("Deprecation")
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun VelocityEffect(
@@ -598,7 +605,7 @@
snapLayoutInfoProvider,
highVelocityApproachSpec
) {
- SnapFlingBehavior(
+ snapFlingBehavior(
snapLayoutInfoProvider = snapLayoutInfoProvider,
decayAnimationSpec = highVelocityApproachSpec,
snapAnimationSpec = snapAnimationSpec,
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
index bf5658f..dd8c1ce 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
@@ -30,9 +30,11 @@
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.lazy.layout.PrefetchScheduler
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -131,13 +133,21 @@
key: ((index: Int) -> Any)? = null,
snapPosition: SnapPosition = config.snapPosition.first,
flingBehavior: TargetedFlingBehavior? = null,
+ prefetchScheduler: PrefetchScheduler? = null,
pageContent: @Composable PagerScope.(page: Int) -> Unit = { Page(index = it) }
) {
rule.setContent {
- val state = rememberPagerState(initialPage, initialPageOffsetFraction, pageCount).also {
- pagerState = it
+ val state = if (prefetchScheduler == null) {
+ rememberPagerState(initialPage, initialPageOffsetFraction, pageCount)
+ } else {
+ remember {
+ object : PagerState(initialPage, initialPageOffsetFraction, prefetchScheduler) {
+ override val pageCount: Int get() = pageCount()
+ }
+ }
}
+ pagerState = state
composeView = LocalView.current
focusManager = LocalFocusManager.current
CompositionLocalProvider(
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
index d72d1e3..19d6748 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
@@ -17,6 +17,7 @@
package androidx.compose.foundation.pager
import android.view.accessibility.AccessibilityNodeProvider
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -41,6 +42,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+@OptIn(ExperimentalFoundationApi::class)
@RunWith(Parameterized::class)
class PagerAccessibilityTest(config: ParamConfig) : BasePagerTest(config = config) {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt
index 1f9cc0b..f9414d3 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt
@@ -56,6 +56,7 @@
var pageSizePx = 300
val pageSizeDp = with(rule.density) { pageSizePx.toDp() }
var touchSlope: Float = 0.0f
+ private val scheduler = TestPrefetchScheduler()
@Test
fun notPrefetchingForwardInitially() {
@@ -83,7 +84,7 @@
}
}
- waitForPrefetch(preFetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$preFetchIndex")
.assertExists()
@@ -102,7 +103,7 @@
}
}
- waitForPrefetch(preFetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$preFetchIndex")
.assertExists()
@@ -126,7 +127,7 @@
up()
}
- waitForPrefetch(preFetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$preFetchIndex")
.assertExists()
@@ -151,7 +152,7 @@
up()
}
- waitForPrefetch(preFetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$preFetchIndex")
.assertExists()
@@ -170,7 +171,7 @@
}
}
var prefetchIndex = initialIndex + 2
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -185,7 +186,7 @@
}
prefetchIndex -= 3
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -203,7 +204,7 @@
}
}
- waitForPrefetch(2)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking {
@@ -214,7 +215,7 @@
val prefetchIndex = 3
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("${prefetchIndex - 1}")
.assertIsDisplayed()
@@ -236,7 +237,7 @@
}
}
- waitForPrefetch(preFetchIndex)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking {
@@ -245,7 +246,7 @@
}
}
- waitForPrefetch(preFetchIndex - 1)
+ waitForPrefetch()
rule.onNodeWithTag("$preFetchIndex")
.assertIsDisplayed()
@@ -268,7 +269,7 @@
var prefetchIndex = initialIndex + 2
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -283,7 +284,7 @@
}
prefetchIndex -= 3
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -319,7 +320,7 @@
}
var prefetchIndex = initialIndex + 1
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("${prefetchIndex + 1}")
.assertExists()
@@ -333,7 +334,7 @@
}
prefetchIndex -= 3
- waitForPrefetch(prefetchIndex)
+ waitForPrefetch()
rule.onNodeWithTag("$prefetchIndex")
.assertExists()
@@ -457,7 +458,7 @@
}
}
- waitForPrefetch(7)
+ waitForPrefetch()
rule.runOnIdle {
runBlocking(AutoTestFrameClock()) {
@@ -477,14 +478,13 @@
return consumed
}
- private fun waitForPrefetch(index: Int) {
- rule.waitUntil {
- activeNodes.contains(index) && activeMeasuredNodes.contains(index)
+ private fun waitForPrefetch() {
+ rule.runOnIdle {
+ scheduler.executeActiveRequests()
}
}
private val activeNodes = mutableSetOf<Int>()
- private val activeMeasuredNodes = mutableSetOf<Int>()
private fun composePager(
initialPage: Int = 0,
@@ -499,6 +499,7 @@
beyondViewportPageCount = paramConfig.beyondViewportPageCount,
initialPage = initialPage,
initialPageOffsetFraction = initialPageOffsetFraction,
+ prefetchScheduler = scheduler,
pageCount = { 100 },
pageSize = {
object : PageSize {
@@ -516,7 +517,6 @@
activeNodes.add(it)
onDispose {
activeNodes.remove(it)
- activeMeasuredNodes.remove(it)
}
}
@@ -527,7 +527,6 @@
.testTag("$it")
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
- activeMeasuredNodes.add(it)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/TestPrefetchScheduler.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/TestPrefetchScheduler.kt
new file mode 100644
index 0000000..04c60b750
--- /dev/null
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/TestPrefetchScheduler.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.lazy.layout.PrefetchRequest
+import androidx.compose.foundation.lazy.layout.PrefetchRequestScope
+import androidx.compose.foundation.lazy.layout.PrefetchScheduler
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class TestPrefetchScheduler : PrefetchScheduler {
+
+ private var activeRequests = mutableListOf<PrefetchRequest>()
+ override fun schedulePrefetch(prefetchRequest: PrefetchRequest) {
+ activeRequests.add(prefetchRequest)
+ }
+
+ fun executeActiveRequests() {
+ activeRequests.forEach {
+ with(it) { scope.execute() }
+ }
+ activeRequests.clear()
+ }
+
+ private val scope = object : PrefetchRequestScope {
+ override fun availableTimeNanos(): Long = Long.MAX_VALUE
+ }
+}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLayoutTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLayoutTest.kt
new file mode 100644
index 0000000..b45e79b
--- /dev/null
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLayoutTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text
+
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.sp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+// largest size of a unfocused dimension from Constraints.kt
+private val UnfocusedDimensionConstraintMax = 2 shl 13
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class BasicTextLayoutTest {
+
+ @get:Rule
+ val rule = createComposeRule()
+
+ @Test
+ fun simple_layoutText_doesNotThrow_when2shl14char() {
+ var textLayoutResult: TextLayoutResult? = null
+ rule.setContent {
+ BasicText(
+ text = "a".repeat(2 shl 14),
+ style = TextStyle(fontSize = 48.sp),
+ onTextLayout = { textLayoutResult = it }
+ )
+ }
+ rule.waitForIdle()
+
+ assertThat(textLayoutResult?.multiParagraph?.height).isGreaterThan(
+ UnfocusedDimensionConstraintMax
+ )
+ }
+
+ @Test
+ fun annotatedString_layoutText_doesNotThrow_when2shl14char() {
+ var textLayoutResult: TextLayoutResult? = null
+ rule.setContent {
+ BasicText(
+ text = AnnotatedString("a".repeat(2 shl 14)),
+ style = TextStyle(fontSize = 48.sp),
+ onTextLayout = { textLayoutResult = it }
+ )
+ }
+ rule.waitForIdle()
+ assertThat(textLayoutResult?.multiParagraph?.height).isGreaterThan(
+ UnfocusedDimensionConstraintMax
+ )
+ }
+}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt
index a855d47..f65b5c2 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt
@@ -804,7 +804,7 @@
BasicText(
buildAnnotatedString {
withLink(Url("qwerty") {
- clickedUrlAnnotation = it as LinkAnnotation.Url
+ clickedUrlAnnotation = it as Url
}
) { append("link") }
}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldFocusTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldFocusTest.kt
index 933c14d..13b9c15 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldFocusTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldFocusTest.kt
@@ -50,7 +50,7 @@
@get:Rule
val rule = createComposeRule()
- val keyboardHelper = KeyboardHelper(rule)
+ private val keyboardHelper = KeyboardHelper(rule)
@Test
fun hideKeyboardWhenDisposed() {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHandwritingGestureTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHandwritingGestureTest.kt
index 47eb377..37c4219 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHandwritingGestureTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHandwritingGestureTest.kt
@@ -23,22 +23,31 @@
import android.view.inputmethod.InputConnection
import android.view.inputmethod.InsertGesture
import android.view.inputmethod.JoinOrSplitGesture
+import android.view.inputmethod.PreviewableHandwritingGesture
import android.view.inputmethod.RemoveSpaceGesture
import android.view.inputmethod.SelectGesture
import android.view.inputmethod.SelectRangeGesture
import androidx.annotation.RequiresApi
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.setFocusableContent
import androidx.compose.foundation.text.input.InputMethodInterceptor
+import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertPixelColor
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.toAndroidRectF
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.graphics.toPixelMap
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalTextToolbar
@@ -47,12 +56,15 @@
import androidx.compose.ui.platform.TextToolbarStatus
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.requestFocus
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue
+import androidx.core.graphics.ColorUtils
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
@@ -72,6 +84,10 @@
private val Tag = "CoreTextField"
+ private val backgroundColor = Color.Red
+ private val textColor = Color.Green
+ private val selectionColor = Color.Blue
+
private val lineMargin = 16f
@Test
@@ -96,6 +112,31 @@
}
@Test
+ fun textField_selectGesture_preview_wordLevel() {
+ val text = "abc def ghi"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val localBoundingBox = textLayoutResult.boundingBoxOf("b")
+ val screenBoundingBox = localToScreen(localBoundingBox).toAndroidRectF()
+ SelectGesture.Builder()
+ .setSelectionArea(screenBoundingBox)
+ .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertSelectionPreviewHighlight(textLayoutResult, text.rangeOf("abc"))
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_selectGesture_characterLevel() {
val text = "abcdef"
testHandwritingGesture(
@@ -117,6 +158,31 @@
}
@Test
+ fun textField_selectGesture_preview_characterLevel() {
+ val text = "abc def ghi"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val localBoundingBox = textLayoutResult.boundingBoxOf("bc")
+ val screenBoundingBox = localToScreen(localBoundingBox).toAndroidRectF()
+ SelectGesture.Builder()
+ .setSelectionArea(screenBoundingBox)
+ .setGranularity(HandwritingGesture.GRANULARITY_CHARACTER)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertSelectionPreviewHighlight(textLayoutResult, text.rangeOf("bc"))
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_selectGesture_characterLevel_noSelection_insertFallbackText() {
val text = "abcdef"
val fallback = "fallbackText"
@@ -161,6 +227,29 @@
}
@Test
+ fun textField_selectGesture_preview_characterLevel_noSelection() {
+ val text = "abcdef"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { _ ->
+ SelectGesture.Builder()
+ .setSelectionArea(RectF(0f, 0f, 1f, 1f))
+ .setGranularity(HandwritingGesture.GRANULARITY_CHARACTER)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertNoHighlight(textLayoutResult)
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_selectGesture_wordLevel_noSelection_insertFallbackText() {
val text = "abc def ghi"
val fallback = "fallback"
@@ -211,6 +300,31 @@
}
@Test
+ fun textField_selectGesture_preview_wordLevel_noSelection() {
+ val text = "abc def ghi"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val localBoundingBox = textLayoutResult.boundingBoxOf("a")
+ val screenBoundingBox = localToScreen(localBoundingBox).toAndroidRectF()
+ SelectGesture.Builder()
+ .setSelectionArea(screenBoundingBox)
+ .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertNoHighlight(textLayoutResult)
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_deleteGesture_wordLevel_removeSpaceBeforeDeletion() {
val text = "abc def ghi"
testHandwritingGesture(
@@ -234,6 +348,31 @@
}
@Test
+ fun textField_deleteGesture_preview_wordLevel() {
+ val text = "abc def ghi"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val localBoundingBox = textLayoutResult.boundingBoxOf("h")
+ val screenBoundingBox = localToScreen(localBoundingBox).toAndroidRectF()
+ DeleteGesture.Builder()
+ .setDeletionArea(screenBoundingBox)
+ .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertDeletionPreviewHighlight(textLayoutResult, text.rangeOf("ghi"))
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_deleteGesture_wordLevel_onlyRemoveSpaceBeforeDeletion() {
val text = "abc\n def ghi"
testHandwritingGesture(
@@ -327,6 +466,31 @@
}
@Test
+ fun textField_deleteGesture_preview_characterLevel() {
+ val text = "abcdefghi"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val localBoundingBox = textLayoutResult.boundingBoxOf("def")
+ val screenBoundingBox = localToScreen(localBoundingBox).toAndroidRectF()
+ DeleteGesture.Builder()
+ .setDeletionArea(screenBoundingBox)
+ .setGranularity(HandwritingGesture.GRANULARITY_CHARACTER)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertDeletionPreviewHighlight(textLayoutResult, text.rangeOf("def"))
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_deleteGesture_characterLevel_notRemoveSpaces() {
val text = "abcdef ghi"
testHandwritingGesture(
@@ -397,6 +561,29 @@
}
}
+ @Test
+ fun textField_deleteGesture_preview_noDeletion() {
+ val text = "abc def ghi"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { _ ->
+ DeleteGesture.Builder()
+ .setDeletionArea(RectF(-1f, -1f, 0f, 0f))
+ .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertNoHighlight(textLayoutResult)
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
fun textField_selectRangeGesture_characterLevel() {
val text = "abc\ndef"
testHandwritingGesture(
@@ -425,6 +612,38 @@
}
@Test
+ fun textField_selectRangeGesture_preview_characterLevel() {
+ val text = "abc\ndef"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val startArea = textLayoutResult.boundingBoxOf("c").let {
+ localToScreen(it).toAndroidRectF()
+ }
+
+ val endArea = textLayoutResult.boundingBoxOf("d").let {
+ localToScreen(it).toAndroidRectF()
+ }
+
+ SelectRangeGesture.Builder()
+ .setSelectionStartArea(startArea)
+ .setSelectionEndArea(endArea)
+ .setGranularity(HandwritingGesture.GRANULARITY_CHARACTER)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertSelectionPreviewHighlight(textLayoutResult, text.rangeOf("c\nd"))
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_selectRangeGesture_wordLevel() {
val text = "abc\ndef jhi"
testHandwritingGesture(
@@ -453,6 +672,41 @@
}
@Test
+ fun textField_selectRangeGesture_preview_wordLevel() {
+ val text = "abc\ndef jhi"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val startArea = textLayoutResult.boundingBoxOf("b").let {
+ localToScreen(it).toAndroidRectF()
+ }
+
+ val endArea = textLayoutResult.boundingBoxOf("e").let {
+ localToScreen(it).toAndroidRectF()
+ }
+
+ SelectRangeGesture.Builder()
+ .setSelectionStartArea(startArea)
+ .setSelectionEndArea(endArea)
+ .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertSelectionPreviewHighlight(
+ textLayoutResult,
+ text.rangeOf("abc\ndef")
+ )
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_selectRangeGesture_nothingSelectedInStartArea_insertFallbackText() {
val text = "abc\ndef"
val fallback = "fallbackText"
@@ -515,6 +769,35 @@
}
@Test
+ fun textField_selectRangeGesture_preview_nothingSelectedInStartArea() {
+ val text = "abc\ndef"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val endArea = textLayoutResult.boundingBoxOf("d").let {
+ localToScreen(it).toAndroidRectF()
+ }
+ // The startArea selects nothing, but the endArea contains one character, it
+ // should still fallback.
+ SelectRangeGesture.Builder()
+ .setSelectionStartArea(RectF(0f, 0f, 1f, 1f))
+ .setSelectionEndArea(endArea)
+ .setGranularity(HandwritingGesture.GRANULARITY_CHARACTER)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertNoHighlight(textLayoutResult)
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_selectRangeGesture_noSelection_fail() {
val text = "abcdef"
testHandwritingGesture(
@@ -564,6 +847,37 @@
}
@Test
+ fun textField_deleteRangeGesture_preview_characterLevel() {
+ val text = "abc\ndef"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val startArea = textLayoutResult.boundingBoxOf("c").let {
+ localToScreen(it).toAndroidRectF()
+ }
+ val endArea = textLayoutResult.boundingBoxOf("d").let {
+ localToScreen(it).toAndroidRectF()
+ }
+
+ DeleteRangeGesture.Builder()
+ .setDeletionStartArea(startArea)
+ .setDeletionEndArea(endArea)
+ .setGranularity(HandwritingGesture.GRANULARITY_CHARACTER)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertDeletionPreviewHighlight(textLayoutResult, text.rangeOf("c\nd"))
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_deleteRangeGesture_wordLevel() {
val text = "abc def\n jhi lmn"
testHandwritingGesture(
@@ -592,6 +906,39 @@
}
@Test
+ fun textField_deleteRangeGesture_preview_wordLevel() {
+ val text = "abc def\n jhi lmn"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val startArea = textLayoutResult.boundingBoxOf("e").let {
+ localToScreen(it).toAndroidRectF()
+ }
+ val endArea = textLayoutResult.boundingBoxOf("h").let {
+ localToScreen(it).toAndroidRectF()
+ }
+
+ DeleteRangeGesture.Builder()
+ .setDeletionStartArea(startArea)
+ .setDeletionEndArea(endArea)
+ .setGranularity(HandwritingGesture.GRANULARITY_WORD)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertDeletionPreviewHighlight(
+ textLayoutResult, text.rangeOf("def\n jhi")
+ )
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_deleteRangeGesture_nothingDeletedInStartArea_insertFallbackText() {
val text = "abc\ndef"
val fallback = "fallbackText"
@@ -654,6 +1001,35 @@
}
@Test
+ fun textField_deleteRangeGesture_preview_nothingDeletedInStartArea() {
+ val text = "abc def\n jhi lmn"
+ val initialCursor = 3
+ testHandwritingGesture(
+ text = text,
+ initialSelection = TextRange(initialCursor),
+ gestureFactory = { textLayoutResult ->
+ val endArea = textLayoutResult.boundingBoxOf("d").let {
+ localToScreen(it).toAndroidRectF()
+ }
+ // The startArea selects nothing, but the endArea contains one character, it
+ // should still fallback.
+ DeleteRangeGesture.Builder()
+ .setDeletionStartArea(RectF(0f, 0f, 1f, 1f))
+ .setDeletionEndArea(endArea)
+ .setGranularity(HandwritingGesture.GRANULARITY_CHARACTER)
+ .build()
+ },
+ preview = true,
+ imageAssertion = { imageBitmap, textLayoutResult ->
+ imageBitmap.assertNoHighlight(textLayoutResult)
+ }
+ ) { textFieldValue, _, _ ->
+ assertThat(textFieldValue.text).isEqualTo(text)
+ assertThat(textFieldValue.selection).isEqualTo(TextRange(initialCursor))
+ }
+ }
+
+ @Test
fun textField_deleteRangeGesture_noDeletion_fail() {
val text = "abcdef"
testHandwritingGesture(
@@ -926,7 +1302,7 @@
) { textFieldState, resultCode, textToolbar ->
assertThat(resultCode).isEqualTo(InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS)
val expectedText = "axxxbcdef"
- assertThat(textFieldState.text.toString()).isEqualTo(expectedText)
+ assertThat(textFieldState.text).isEqualTo(expectedText)
// Cursor is placed before 'b'
assertThat(textFieldState.selection).isEqualTo(TextRange(expectedText.indexOf('b')))
assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
@@ -957,7 +1333,7 @@
assertThat(resultCode).isEqualTo(InputConnection.HANDWRITING_GESTURE_RESULT_FALLBACK)
val expectedText = text.insert(initialCursor, fallback)
- assertThat(textFieldState.text.toString()).isEqualTo(expectedText)
+ assertThat(textFieldState.text).isEqualTo(expectedText)
val expectedSelection = TextRange(initialCursor + fallback.length)
assertThat(textFieldState.selection).isEqualTo(expectedSelection)
assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
@@ -982,7 +1358,7 @@
}
) { textFieldState, resultCode, textToolbar ->
assertThat(resultCode).isEqualTo(InputConnection.HANDWRITING_GESTURE_RESULT_FAILED)
- assertThat(textFieldState.text.toString()).isEqualTo(text)
+ assertThat(textFieldState.text).isEqualTo(text)
assertThat(textFieldState.selection).isEqualTo(TextRange(text.length))
assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
}
@@ -1268,6 +1644,8 @@
text: String,
initialSelection: TextRange = TextRange(text.length),
gestureFactory: LayoutCoordinates.(TextLayoutResult) -> HandwritingGesture,
+ preview: Boolean = false,
+ imageAssertion: ((ImageBitmap, TextLayoutResult) -> Unit)? = null,
assertion: (TextFieldValue, resultCode: Int, TextToolbar) -> Unit
) {
var textFieldValue by mutableStateOf(TextFieldValue(text, initialSelection))
@@ -1280,14 +1658,20 @@
override val handwritingGestureLineMargin: Float = lineMargin
}
CompositionLocalProvider(
+ LocalTextSelectionColors provides TextSelectionColors(
+ selectionColor,
+ selectionColor
+ ),
LocalTextToolbar provides textToolbar,
LocalViewConfiguration provides viewConfiguration
) {
CoreTextField(
value = textFieldValue,
onValueChange = { textFieldValue = it },
+ textStyle = TextStyle(color = textColor),
modifier = Modifier
.fillMaxSize()
+ .background(backgroundColor)
.testTag(Tag)
.onGloballyPositioned { layoutCoordinates = it },
onTextLayout = {
@@ -1303,12 +1687,20 @@
var resultCode = InputConnection.HANDWRITING_GESTURE_RESULT_UNKNOWN
inputMethodInterceptor.withInputConnection {
- performHandwritingGesture(gesture, /* executor= */null) { resultCode = it }
+ if (preview) {
+ previewHandwritingGesture(gesture as PreviewableHandwritingGesture, null)
+ } else {
+ performHandwritingGesture(gesture, /* executor= */ null) { resultCode = it }
+ }
}
rule.runOnIdle {
assertion.invoke(textFieldValue, resultCode, textToolbar)
}
+
+ if (imageAssertion != null) {
+ imageAssertion(rule.onNodeWithTag(Tag).captureToImage(), textLayoutResult!!)
+ }
}
private fun setContent(
@@ -1327,6 +1719,56 @@
return rect.translate(localOriginInScreen)
}
+ private fun ImageBitmap.assertSelectionPreviewHighlight(
+ textLayoutResult: TextLayoutResult,
+ range: TextRange
+ ) {
+ assertHighlight(textLayoutResult, range, selectionColor)
+ }
+
+ private fun ImageBitmap.assertDeletionPreviewHighlight(
+ textLayoutResult: TextLayoutResult,
+ range: TextRange
+ ) {
+ val deletionPreviewColor = textColor.copy(alpha = textColor.alpha * 0.2f)
+ val compositeColor = Color(
+ ColorUtils.compositeColors(
+ deletionPreviewColor.toArgb(),
+ backgroundColor.toArgb()
+ )
+ )
+ assertHighlight(textLayoutResult, range, compositeColor)
+ }
+
+ private fun ImageBitmap.assertNoHighlight(textLayoutResult: TextLayoutResult) {
+ assertHighlight(textLayoutResult, TextRange.Zero, Color.Unspecified)
+ }
+
+ private fun ImageBitmap.assertHighlight(
+ textLayoutResult: TextLayoutResult,
+ range: TextRange,
+ highlightColor: Color
+ ) {
+ val pixelMap =
+ toPixelMap(width = textLayoutResult.size.width, height = textLayoutResult.size.height)
+ for (offset in 0 until textLayoutResult.layoutInput.text.length) {
+ if (textLayoutResult.layoutInput.text[offset] == '\n') {
+ continue
+ }
+ // Check the top left pixel of each character (assumes LTR). This pixel is always part
+ // of the background (not part of the text foreground).
+ val line = textLayoutResult.multiParagraph.getLineForOffset(offset)
+ val lineTop = textLayoutResult.multiParagraph.getLineTop(line).ceilToIntPx()
+ val horizontal =
+ textLayoutResult.multiParagraph.getHorizontalPosition(offset, true).ceilToIntPx()
+ if (offset in range) {
+ pixelMap.assertPixelColor(highlightColor, horizontal, lineTop)
+ } else {
+ pixelMap.assertPixelColor(backgroundColor, horizontal, lineTop)
+ }
+ }
+ }
+
private fun FakeTextToolbar(): TextToolbar {
return object : TextToolbar {
private var _status: TextToolbarStatus = TextToolbarStatus.Hidden
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHandwritingTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHandwritingTest.kt
index 3dc1a55..4931a0a 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHandwritingTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHandwritingTest.kt
@@ -39,6 +39,8 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.requestFocus
+import androidx.compose.ui.text.input.ImeOptions
+import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -55,6 +57,7 @@
@get:Rule
val rule = createComposeRule()
private val inputMethodInterceptor = InputMethodInterceptor(rule)
+ private val keyboardHelper = KeyboardHelper(rule)
private val Tag = "CoreTextField"
@@ -240,6 +243,45 @@
performHandwritingAndExpect(stylusHandwritingStarted = true)
}
+ @Test
+ fun coreTextField_passwordField_notStartStylusHandwriting() {
+ inputMethodManagerFactory = { fakeImm }
+
+ setContent {
+ val value = remember { TextFieldValue() }
+ CoreTextField(
+ value = value,
+ onValueChange = { },
+ imeOptions = ImeOptions(keyboardType = KeyboardType.Password),
+ modifier = Modifier
+ .fillMaxSize()
+ .testTag(Tag),
+ )
+ }
+
+ performHandwritingAndExpect(stylusHandwritingStarted = false)
+ }
+
+ @Test
+ fun coreTextField_passwordField_attemptStylusHandwritingShowSoftInput() {
+ rule.setContent {
+ keyboardHelper.initialize()
+ val value = remember { TextFieldValue() }
+ CoreTextField(
+ value = value,
+ onValueChange = { },
+ imeOptions = ImeOptions(keyboardType = KeyboardType.Password),
+ modifier = Modifier
+ .fillMaxSize()
+ .testTag(Tag),
+ )
+ }
+
+ rule.onNodeWithTag(Tag).performStylusHandwriting()
+ keyboardHelper.waitForKeyboardVisibility(true)
+ assertThat(keyboardHelper.isSoftwareKeyboardShown()).isTrue()
+ }
+
private fun testStylusHandwriting(
stylusHandwritingStarted: Boolean,
interaction: SemanticsNodeInteraction.() -> Unit
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt
index ba05692..cd007e1 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt
@@ -34,7 +34,6 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.focus.FocusRequester
@@ -68,7 +67,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalComposeUiApi::class)
@MediumTest
@RunWith(AndroidJUnit4::class)
class CoreTextFieldInputServiceIntegrationTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/DrawPhaseAttributesToggleTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/DrawPhaseAttributesToggleTest.kt
index 8055349..157a7cb 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/DrawPhaseAttributesToggleTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/DrawPhaseAttributesToggleTest.kt
@@ -33,7 +33,6 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextDecoration
import androidx.test.filters.MediumTest
@@ -58,7 +57,6 @@
override fun toString(): String = "toggling $description"
}
- @OptIn(ExperimentalTextApi::class)
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextDelegateIntegrationTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextDelegateIntegrationTest.kt
index ecc70a7..8c80b3c 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextDelegateIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextDelegateIntegrationTest.kt
@@ -18,7 +18,6 @@
import android.graphics.Bitmap
import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
@@ -36,7 +35,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(InternalFoundationTextApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class TextDelegateIntegrationTest {
@@ -44,7 +42,6 @@
private val fontFamily = TEST_FONT_FAMILY
private val density = Density(density = 1f)
private val context = InstrumentationRegistry.getInstrumentation().context
- @OptIn(ExperimentalTextApi::class)
private val fontFamilyResolver = createFontFamilyResolver(context)
@Test
@@ -200,6 +197,23 @@
assertThat(layoutResultLtr.size.width).isEqualTo(layoutResultRtl.size.width)
}
+
+ @Test
+ fun layoutText_doesntThrow_when2shl14char() {
+ val textDelegate = TextDelegate(
+ text = AnnotatedString(text = "a".repeat(2 shl 14)),
+ style = TextStyle.Default,
+ density = density,
+ fontFamilyResolver = fontFamilyResolver
+ )
+ val subject = textDelegate.layout(
+ Constraints() /* unbounded */,
+ layoutDirection = LayoutDirection.Ltr,
+ null
+ )
+ assertThat(subject.size.width).isGreaterThan(0)
+ assertThat(subject.size.height).isGreaterThan(0)
+ }
}
private fun TextLayoutResult.toBitmap() = Bitmap.createBitmap(
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextDelegateWidthWithLetterSpacingTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextDelegateWidthWithLetterSpacingTest.kt
index 77b3ec4..2bf69a4 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextDelegateWidthWithLetterSpacingTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextDelegateWidthWithLetterSpacingTest.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.text
import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.createFontFamilyResolver
import androidx.compose.ui.text.style.TextOverflow
@@ -32,7 +31,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(InternalFoundationTextApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class TextDelegateWidthWithLetterSpacingTest {
@@ -46,7 +44,6 @@
private val lineHeight = 16.sp
private val fontSize = 12.sp
private val context = InstrumentationRegistry.getInstrumentation().context
- @OptIn(ExperimentalTextApi::class)
private val fontFamilyResolver = createFontFamilyResolver(context)
@Test
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextFieldDelegateIntegrationTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextFieldDelegateIntegrationTest.kt
index 661cd13..c5dd825 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextFieldDelegateIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextFieldDelegateIntegrationTest.kt
@@ -20,7 +20,6 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextPainter
import androidx.compose.ui.text.TextRange
@@ -38,13 +37,11 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(InternalFoundationTextApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class TextFieldDelegateIntegrationTest {
private val density = Density(density = 1f)
private val context = InstrumentationRegistry.getInstrumentation().context
- @OptIn(ExperimentalTextApi::class)
private val fontFamilyResolver = createFontFamilyResolver(context)
private val layoutDirection = LayoutDirection.Ltr
@@ -73,15 +70,52 @@
TextFieldDelegate.draw(
canvas = actualCanvas,
value = TextFieldValue(text = "Hello, World", selection = selection),
- selectionPaint = Paint().apply { color = selectionColor },
+ selectionPreviewHighlightRange = TextRange.Zero,
+ deletionPreviewHighlightRange = TextRange.Zero,
offsetMapping = OffsetMapping.Identity,
- textLayoutResult = layoutResult
+ textLayoutResult = layoutResult,
+ highlightPaint = Paint(),
+ selectionBackgroundColor = selectionColor
)
assertThat(actualBitmap.sameAs(expectedBitmap)).isTrue()
}
@Test
+ fun draw_highlight_test() {
+ val textDelegate = TextDelegate(
+ text = AnnotatedString("Hello, World"),
+ style = TextStyle.Default,
+ maxLines = 2,
+ density = density,
+ fontFamilyResolver = fontFamilyResolver
+ )
+ val layoutResult = textDelegate.layout(Constraints.fixedWidth(1024), layoutDirection)
+ val deletionPreviewHighlightRange = TextRange(3, 5)
+
+ val actualBitmap = layoutResult.toBitmap()
+ val actualCanvas = Canvas(android.graphics.Canvas(actualBitmap))
+ TextFieldDelegate.draw(
+ canvas = actualCanvas,
+ value = TextFieldValue(text = "Hello, World", selection = TextRange.Zero),
+ selectionPreviewHighlightRange = TextRange.Zero,
+ deletionPreviewHighlightRange = deletionPreviewHighlightRange,
+ offsetMapping = OffsetMapping.Identity,
+ textLayoutResult = layoutResult,
+ highlightPaint = Paint(),
+ selectionBackgroundColor = Color.Blue
+ )
+
+ val expectedBitmap = layoutResult.toBitmap()
+ val expectedCanvas = Canvas(android.graphics.Canvas(expectedBitmap))
+ val selectionPath = layoutResult.multiParagraph.getPathForRange(3, 5)
+ // Default text color is black, so deletion preview highlight is black with 20% alpha.
+ expectedCanvas.drawPath(selectionPath, Paint().apply { color = Color(0f, 0f, 0f, 0.2f) })
+ TextPainter.paint(expectedCanvas, layoutResult)
+ assertThat(actualBitmap.sameAs(expectedBitmap)).isTrue()
+ }
+
+ @Test
fun layout_height_constraint_max_height() {
val textDelegate = TextDelegate(
text = AnnotatedString("Hello, World"),
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextLayoutResultIntegrationTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextLayoutResultIntegrationTest.kt
index 2a3379c..3b03af5 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextLayoutResultIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextLayoutResultIntegrationTest.kt
@@ -20,7 +20,6 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.createFontFamilyResolver
@@ -36,7 +35,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(InternalFoundationTextApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
class TextLayoutResultIntegrationTest {
@@ -44,7 +42,6 @@
private val fontFamily = TEST_FONT_FAMILY
private val density = Density(density = 1f)
private val context = InstrumentationRegistry.getInstrumentation().context
- @OptIn(ExperimentalTextApi::class)
private val fontFamilyResolver = createFontFamilyResolver(context)
private val layoutDirection = LayoutDirection.Ltr
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextStyleInvalidationTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextStyleInvalidationTest.kt
index 065dfa4..43d1d2b 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextStyleInvalidationTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/TextStyleInvalidationTest.kt
@@ -27,7 +27,6 @@
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.layout.layout
import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
@@ -54,7 +53,6 @@
import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters
-@Suppress("DEPRECATION")
@RunWith(Parameterized::class)
@MediumTest
class TextStyleInvalidationTest(private val config: Config) {
@@ -78,7 +76,6 @@
}
}
- @OptIn(ExperimentalTextApi::class)
companion object {
@Parameters(name = "{0}")
@JvmStatic
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicSecureTextFieldTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicSecureTextFieldTest.kt
index eb0228a..0aa4592 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicSecureTextFieldTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicSecureTextFieldTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -59,7 +58,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class, ExperimentalTestApi::class)
+@OptIn(ExperimentalTestApi::class)
@MediumTest
@RunWith(AndroidJUnit4::class)
internal class BasicSecureTextFieldTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldDrawPhaseToggleTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldDrawPhaseToggleTest.kt
index 4a8dfe9..d06b95c 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldDrawPhaseToggleTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldDrawPhaseToggleTest.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.text.input
import android.os.Build
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.TEST_FONT_FAMILY
@@ -44,7 +43,6 @@
import org.junit.Rule
import org.junit.Test
-@OptIn(ExperimentalFoundationApi::class)
class BasicTextFieldDrawPhaseToggleTest {
@get:Rule
@@ -182,7 +180,7 @@
* Instead of looking for an exact match of pixel values, this assertion provides the ability to
* judge each pixel individually to whether it fits a predefined filter.
*/
-private inline fun ImageBitmap.assertPixelConsistency(
+internal inline fun ImageBitmap.assertPixelConsistency(
filter: (color: Color) -> Boolean
) {
val pixel = toPixelMap()
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldHandwritingTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldHandwritingTest.kt
index 0e6f520..5cdef70 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldHandwritingTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldHandwritingTest.kt
@@ -16,9 +16,10 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardHelper
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.handwriting.isStylusHandwritingSupported
import androidx.compose.foundation.text.performStylusClick
import androidx.compose.foundation.text.performStylusHandwriting
@@ -34,15 +35,16 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.requestFocus
+import androidx.compose.ui.text.input.KeyboardType
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@LargeTest
@RunWith(AndroidJUnit4::class)
internal class BasicTextFieldHandwritingTest {
@@ -58,6 +60,8 @@
private val imm = FakeInputMethodManager()
+ private val keyboardHelper = KeyboardHelper(rule)
+
@Before
fun setup() {
// Test is only meaningful when stylus handwriting is supported.
@@ -190,6 +194,38 @@
performHandwritingAndExpect(stylusHandwritingStarted = true)
}
+ @Test
+ fun textField_passwordField_notStartStylusHandwriting() {
+ immRule.setFactory { imm }
+ inputMethodInterceptor.setTextFieldTestContent {
+ val state = remember { TextFieldState() }
+ BasicTextField(
+ state = state,
+ modifier = Modifier.fillMaxSize().testTag(Tag),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
+ )
+ }
+
+ performHandwritingAndExpect(stylusHandwritingStarted = false)
+ }
+
+ @Test
+ fun coreTextField_passwordField_attemptStylusHandwritingShowSoftInput() {
+ rule.setContent {
+ keyboardHelper.initialize()
+ val state = remember { TextFieldState() }
+ BasicTextField(
+ state = state,
+ modifier = Modifier.fillMaxSize().testTag(Tag),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
+ )
+ }
+
+ rule.onNodeWithTag(Tag).performStylusHandwriting()
+ keyboardHelper.waitForKeyboardVisibility(true)
+ Truth.assertThat(keyboardHelper.isSoftwareKeyboardShown()).isTrue()
+ }
+
private fun testStylusHandwriting(
stylusHandwritingStarted: Boolean,
interaction: SemanticsNodeInteraction.() -> Unit
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldImeSelectionChangesTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldImeSelectionChangesTest.kt
index 32b32fc..d24cec3 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldImeSelectionChangesTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldImeSelectionChangesTest.kt
@@ -20,7 +20,6 @@
import android.view.KeyEvent.ACTION_DOWN
import android.view.KeyEvent.ACTION_UP
import android.view.View
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
@@ -40,7 +39,6 @@
* This class tests different ways of updating selection in TextFieldState and asserts that
* IME gets updated for all of them.
*/
-@OptIn(ExperimentalFoundationApi::class)
@LargeTest
@RunWith(AndroidJUnit4::class)
internal class BasicTextFieldImeSelectionChangesTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldImmIntegrationTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldImmIntegrationTest.kt
index 578f592..ed77fd8 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldImmIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldImmIntegrationTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
@@ -55,10 +54,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(
- ExperimentalFoundationApi::class,
- ExperimentalTestApi::class,
-)
+@OptIn(ExperimentalTestApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
internal class BasicTextFieldImmIntegrationTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldLayoutPhaseToggleTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldLayoutPhaseToggleTest.kt
new file mode 100644
index 0000000..b7f00c2
--- /dev/null
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldLayoutPhaseToggleTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text.input
+
+import android.os.Build
+import androidx.compose.foundation.background
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.TEST_FONT_FAMILY
+import androidx.compose.foundation.text.matchers.assertThat
+import androidx.compose.foundation.text.selection.fetchTextLayoutResult
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.hasSetTextAction
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.sp
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+
+class BasicTextFieldLayoutPhaseToggleTest {
+
+ @get:Rule
+ val rule = createComposeRule()
+
+ private lateinit var state: TextFieldState
+
+ private val fontSize = 20.sp
+ private val textStyle = TextStyle(
+ fontSize = fontSize,
+ fontFamily = TEST_FONT_FAMILY
+ )
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ fun fontWeightChange_reflectsOnView() {
+ state = TextFieldState("abc")
+ var fontWeight by mutableStateOf(FontWeight.Normal)
+ rule.setContent {
+ BasicTextField(
+ state = state,
+ textStyle = TextStyle(fontWeight = fontWeight),
+ modifier = Modifier.background(Color.White)
+ )
+ }
+
+ val firstBitmap = rule.onNode(hasSetTextAction()).captureToImage().asAndroidBitmap()
+ val firstTextLayoutResult = rule.onNode(hasSetTextAction()).fetchTextLayoutResult()
+
+ assertThat(firstTextLayoutResult.layoutInput.style.fontWeight).isEqualTo(FontWeight.Normal)
+
+ fontWeight = FontWeight.Bold
+
+ val secondBitmap = rule.onNode(hasSetTextAction()).captureToImage().asAndroidBitmap()
+ val secondTextLayoutResult = rule.onNode(hasSetTextAction()).fetchTextLayoutResult()
+
+ assertThat(secondTextLayoutResult.layoutInput.style.fontWeight).isEqualTo(FontWeight.Bold)
+ assertThat(firstBitmap).isNotEqualToBitmap(secondBitmap)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ fun textAlignChange_reflectsOnView() {
+ state = TextFieldState("abc")
+ var textAlign by mutableStateOf(TextAlign.End)
+ rule.setContent {
+ BasicTextField(
+ state = state,
+ textStyle = textStyle.copy(textAlign = textAlign),
+ modifier = Modifier.background(Color.White)
+ )
+ }
+
+ val firstBitmap = rule.onNode(hasSetTextAction()).captureToImage().asAndroidBitmap()
+ val firstTextLayoutResult = rule.onNode(hasSetTextAction()).fetchTextLayoutResult()
+
+ assertThat(firstTextLayoutResult.layoutInput.style.textAlign).isEqualTo(TextAlign.End)
+
+ textAlign = TextAlign.Start
+
+ val secondBitmap = rule.onNode(hasSetTextAction()).captureToImage().asAndroidBitmap()
+ val secondTextLayoutResult = rule.onNode(hasSetTextAction()).fetchTextLayoutResult()
+
+ assertThat(secondTextLayoutResult.layoutInput.style.textAlign).isEqualTo(TextAlign.Start)
+ assertThat(firstBitmap).isNotEqualToBitmap(secondBitmap)
+ }
+}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/DecorationBoxTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/DecorationBoxTest.kt
index 96cb15d..0783b23 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/DecorationBoxTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/DecorationBoxTest.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.text.input
import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -56,7 +55,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@MediumTest
@RunWith(AndroidJUnit4::class)
class DecorationBoxTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDetectorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDetectorTest.kt
index 056ac9c..79686c2 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDetectorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDetectorTest.kt
@@ -50,7 +50,7 @@
private val tag = "detector"
- private var callbackCount = 0;
+ private var callbackCount = 0
@Before
fun setup() {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HeightInLinesModifierTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HeightInLinesModifierTest.kt
index 18636bf..6d0af70 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HeightInLinesModifierTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HeightInLinesModifierTest.kt
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalFoundationApi::class)
-
package androidx.compose.foundation.text.input
import android.content.Context
import android.graphics.Typeface
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.text.BasicTextField
@@ -38,7 +35,6 @@
import androidx.compose.ui.platform.ValueElement
import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.AndroidFont
@@ -64,7 +60,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@MediumTest
@RunWith(AndroidJUnit4::class)
class HeightInLinesModifierTest {
@@ -232,7 +227,7 @@
}
}
- @OptIn(ExperimentalTextApi::class, ExperimentalCoroutinesApi::class)
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
fun asyncFontLoad_changesLineHeight() {
val testDispatcher = UnconfinedTestDispatcher()
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/RememberTextFieldStateTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/RememberTextFieldStateTest.kt
index 2fe532a..4d5e863 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/RememberTextFieldStateTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/RememberTextFieldStateTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.remember
import androidx.compose.ui.test.junit4.StateRestorationTester
import androidx.compose.ui.test.junit4.createComposeRule
@@ -28,7 +27,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class RememberTextFieldStateTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCodepointTransformationTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCodepointTransformationTest.kt
index bc7f1d1..5b556d0 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCodepointTransformationTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCodepointTransformationTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.input.internal.CodepointTransformation
import androidx.compose.foundation.text.input.internal.mask
@@ -50,7 +49,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class, ExperimentalTestApi::class)
+@OptIn(ExperimentalTestApi::class)
@MediumTest
@RunWith(AndroidJUnit4::class)
class TextFieldCodepointTransformationTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCursorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCursorTest.kt
index a982889..31b2464 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCursorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCursorTest.kt
@@ -19,7 +19,6 @@
import android.os.Build
import android.view.DragEvent
import android.view.View
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
@@ -91,7 +90,7 @@
import org.junit.Rule
import org.junit.Test
-@OptIn(ExperimentalFoundationApi::class, ExperimentalTestApi::class)
+@OptIn(ExperimentalTestApi::class)
@LargeTest
class TextFieldCursorTest : FocusedWindowTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldDragAndDropTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldDragAndDropTest.kt
index 544d882..7c185a0 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldDragAndDropTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldDragAndDropTest.kt
@@ -350,8 +350,8 @@
lateinit var receivedContent: TransferableContent
rule.setContentAndTestDragAndDrop(
"Hello World!",
- modifier = Modifier.contentReceiver {
- receivedContent = it
+ modifier = Modifier.contentReceiver { content ->
+ receivedContent = content
receivedContent.consume {
// do not consume text
it.uri != null
@@ -501,7 +501,6 @@
}
}
- @OptIn(ExperimentalFoundationApi::class)
private class DragAndDropTestScope(
val state: TextFieldState,
val fontSize: TextUnit,
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt
index ddbd4e4..bc7ba416 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt
@@ -19,7 +19,6 @@
import android.os.SystemClock
import android.view.InputDevice
import android.view.KeyEvent
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.border
import androidx.compose.foundation.focusable
import androidx.compose.foundation.interaction.FocusInteraction
@@ -76,7 +75,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@LargeTest
@RunWith(AndroidJUnit4::class)
internal class TextFieldFocusTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldOutputTransformationGesturesIntegrationTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldOutputTransformationGesturesIntegrationTest.kt
index aece3ef..5d07337 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldOutputTransformationGesturesIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldOutputTransformationGesturesIntegrationTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.TEST_FONT_FAMILY
import androidx.compose.ui.Modifier
@@ -40,7 +39,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@MediumTest
@RunWith(AndroidJUnit4::class)
class TextFieldOutputTransformationGesturesIntegrationTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldOutputTransformationHardwareKeysIntegrationTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldOutputTransformationHardwareKeysIntegrationTest.kt
index c10b343..4f86493 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldOutputTransformationHardwareKeysIntegrationTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldOutputTransformationHardwareKeysIntegrationTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.selection.fetchTextLayoutResult
import androidx.compose.ui.Modifier
@@ -38,7 +37,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class, ExperimentalTestApi::class)
+@OptIn(ExperimentalTestApi::class)
@MediumTest
@RunWith(AndroidJUnit4::class)
class TextFieldOutputTransformationHardwareKeysIntegrationTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldReceiveContentTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldReceiveContentTest.kt
index 4ffd505..777350e 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldReceiveContentTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldReceiveContentTest.kt
@@ -378,21 +378,21 @@
state = state,
modifier = Modifier
.testTag(tag)
- .contentReceiver {
- transferableContent1 = it
- it.consume {
+ .contentReceiver { content ->
+ transferableContent1 = content
+ content.consume {
it.text.contains("a")
}
}
- .contentReceiver {
- transferableContent2 = it
- it.consume {
+ .contentReceiver { content ->
+ transferableContent2 = content
+ content.consume {
it.text.contains("b")
}
}
- .contentReceiver {
- transferableContent3 = it
- it.consume {
+ .contentReceiver { content ->
+ transferableContent3 = content
+ content.consume {
it.text.contains("c")
}
}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldSingleLineHeightTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldSingleLineHeightTest.kt
index 41e8d11..b589fe0 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldSingleLineHeightTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldSingleLineHeightTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.FocusedWindowTest
import androidx.compose.foundation.text.Handle
@@ -36,7 +35,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@MediumTest
@RunWith(AndroidJUnit4::class)
class TextFieldSingleLineHeightTest : FocusedWindowTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/AndroidTextInputSessionTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/AndroidTextInputSessionTest.kt
index 66bea50..46bd2d9 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/AndroidTextInputSessionTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/AndroidTextInputSessionTest.kt
@@ -19,7 +19,6 @@
import android.text.InputType
import android.view.View
import android.view.inputmethod.EditorInfo
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.content.internal.ReceiveContentConfiguration
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
@@ -53,7 +52,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class AndroidTextInputSessionTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/BasicTextFieldHoverTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/BasicTextFieldHoverTest.kt
index 24e17f0..5d6b02b 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/BasicTextFieldHoverTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/BasicTextFieldHoverTest.kt
@@ -22,7 +22,6 @@
import android.view.PointerIcon.TYPE_HAND
import android.view.PointerIcon.TYPE_TEXT
import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
@@ -48,7 +47,7 @@
import org.junit.runner.RunWith
@MediumTest
-@OptIn(ExperimentalTestApi::class, ExperimentalFoundationApi::class)
+@OptIn(ExperimentalTestApi::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
@RunWith(AndroidJUnit4::class)
class BasicTextFieldHoverTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/EditorInfoTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/EditorInfoTest.kt
index 683499e..2a4c26e4 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/EditorInfoTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/EditorInfoTest.kt
@@ -18,8 +18,13 @@
import android.text.InputType
import android.view.inputmethod.DeleteGesture
+import android.view.inputmethod.DeleteRangeGesture
import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InsertGesture
+import android.view.inputmethod.JoinOrSplitGesture
+import android.view.inputmethod.RemoveSpaceGesture
import android.view.inputmethod.SelectGesture
+import android.view.inputmethod.SelectRangeGesture
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.ImeOptions
@@ -599,8 +604,29 @@
val info = EditorInfo()
info.update(ImeOptions.Default)
- assertThat(info.supportedHandwritingGestures).contains(SelectGesture::class.java)
- assertThat(info.supportedHandwritingGestures).contains(DeleteGesture::class.java)
+ assertThat(info.supportedHandwritingGestures).containsExactly(
+ SelectGesture::class.java,
+ DeleteGesture::class.java,
+ SelectRangeGesture::class.java,
+ DeleteRangeGesture::class.java,
+ JoinOrSplitGesture::class.java,
+ InsertGesture::class.java,
+ RemoveSpaceGesture::class.java,
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = 34)
+ @Test
+ fun supportedStylusHandwritingGesturePreviews() {
+ val info = EditorInfo()
+ info.update(ImeOptions.Default)
+
+ assertThat(info.supportedHandwritingGesturePreviews).containsExactly(
+ SelectGesture::class.java,
+ DeleteGesture::class.java,
+ SelectRangeGesture::class.java,
+ DeleteRangeGesture::class.java,
+ )
}
private fun EditorInfo.update(
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/LegacyTextInputMethodRequestCursorAnchorInfoTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/LegacyTextInputMethodRequestCursorAnchorInfoTest.kt
index 985ee59..1be5916d 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/LegacyTextInputMethodRequestCursorAnchorInfoTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/LegacyTextInputMethodRequestCursorAnchorInfoTest.kt
@@ -22,7 +22,6 @@
import android.view.inputmethod.InputConnection
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.graphics.Matrix
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.MultiParagraph
import androidx.compose.ui.text.TextLayoutInput
@@ -71,7 +70,16 @@
private val fontFamilyMeasureFont =
Font(resId = R.font.sample_font, weight = FontWeight.Normal, style = FontStyle.Normal)
.toFontFamily()
- private var position = Offset(0f, 0f)
+
+ private var textLayoutOffset = Offset.Zero
+
+ // Inner text field bounds in text layout coordinates
+ private val innerTextFieldBounds =
+ Rect(topLeft = Offset(0f, 40f), bottomRight = Offset(90f, 70f))
+
+ // Decoration box bounds in text layout coordinates
+ private val decorationBoxBounds =
+ Rect(topLeft = Offset(-5f, 35f), bottomRight = Offset(95f, 77f))
private lateinit var textInputService: LegacyTextInputMethodRequest
private lateinit var inputMethodManager: InputMethodManager
@@ -85,7 +93,11 @@
inputMethodManager = mock { on { isActive() } doReturn true }
textInputService = LegacyTextInputMethodRequest(
view = View(context),
- localToScreen = { matrix -> matrix.translate(position.x, position.y) },
+ localToScreen = { matrix ->
+ (textLayoutOffset + decorationBoxBounds.topLeft).let {
+ matrix.translate(it.x, it.y)
+ }
+ },
inputMethodManager = inputMethodManager
)
textInputService.startInput(
@@ -109,11 +121,9 @@
TextFieldValue("abc", selection = TextRange(2), composition = TextRange(1, 2))
textInputService.updateState(oldValue = textFieldValue, newValue = textFieldValue)
- position = Offset(1f, 1f)
+ textLayoutOffset = Offset(1f, 1f)
val offsetMapping = OffsetMapping.Identity
val textLayoutResult = getTextLayoutResult(textFieldValue.text)
- val innerTextFieldBounds = Rect.Zero
- val decorationBoxBounds = Rect.Zero
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -125,7 +135,7 @@
inputConnection.requestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE)
// Immediate update
- androidMatrix.setTranslate(position)
+ androidMatrix.setTranslate(textLayoutOffset)
val expected =
builder.build(
textFieldValue,
@@ -138,7 +148,7 @@
verify(inputMethodManager).updateCursorAnchorInfo(expected)
clearInvocations(inputMethodManager)
- position = Offset(2f, 2f)
+ textLayoutOffset = Offset(2f, 2f)
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -162,11 +172,9 @@
// No immediate update until updateTextLayoutResult call
verify(inputMethodManager, never()).updateCursorAnchorInfo(any())
- position = Offset(1f, 1f)
+ textLayoutOffset = Offset(1f, 1f)
val offsetMapping = OffsetMapping.Identity
val textLayoutResult = getTextLayoutResult(textFieldValue.text)
- val innerTextFieldBounds = Rect.Zero
- val decorationBoxBounds = Rect.Zero
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -176,7 +184,7 @@
)
// Immediate update
- androidMatrix.setTranslate(position)
+ androidMatrix.setTranslate(textLayoutOffset)
val expected =
builder.build(
textFieldValue,
@@ -189,7 +197,7 @@
verify(inputMethodManager).updateCursorAnchorInfo(expected)
clearInvocations(inputMethodManager)
- position = Offset(2f, 2f)
+ textLayoutOffset = Offset(2f, 2f)
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -208,11 +216,9 @@
TextFieldValue("abc", selection = TextRange(2), composition = TextRange(1, 2))
textInputService.updateState(oldValue = textFieldValue, newValue = textFieldValue)
- position = Offset(1f, 1f)
+ textLayoutOffset = Offset(1f, 1f)
val offsetMapping = OffsetMapping.Identity
val textLayoutResult = getTextLayoutResult(textFieldValue.text)
- val innerTextFieldBounds = Rect.Zero
- val decorationBoxBounds = Rect.Zero
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -227,7 +233,7 @@
verify(inputMethodManager, never()).updateCursorAnchorInfo(any())
clearInvocations(inputMethodManager)
- position = Offset(2f, 2f)
+ textLayoutOffset = Offset(2f, 2f)
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -237,7 +243,7 @@
)
// Monitoring update
- androidMatrix.setTranslate(position)
+ androidMatrix.setTranslate(textLayoutOffset)
val expected =
builder.build(
textFieldValue,
@@ -257,7 +263,7 @@
verify(inputMethodManager, never()).updateCursorAnchorInfo(any())
clearInvocations(inputMethodManager)
- position = Offset(3f, 3f)
+ textLayoutOffset = Offset(3f, 3f)
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -267,7 +273,7 @@
)
// Monitoring update
- androidMatrix.setTranslate(position)
+ androidMatrix.setTranslate(textLayoutOffset)
val expected2 =
builder.build(
textFieldValue,
@@ -286,11 +292,9 @@
TextFieldValue("abc", selection = TextRange(2), composition = TextRange(1, 2))
textInputService.updateState(oldValue = textFieldValue, newValue = textFieldValue)
- position = Offset(1f, 1f)
+ textLayoutOffset = Offset(1f, 1f)
val offsetMapping = OffsetMapping.Identity
val textLayoutResult = getTextLayoutResult(textFieldValue.text)
- val innerTextFieldBounds = Rect.Zero
- val decorationBoxBounds = Rect.Zero
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -304,7 +308,7 @@
)
// Immediate update
- androidMatrix.setTranslate(position)
+ androidMatrix.setTranslate(textLayoutOffset)
val expected =
builder.build(
textFieldValue,
@@ -317,7 +321,7 @@
verify(inputMethodManager).updateCursorAnchorInfo(expected)
clearInvocations(inputMethodManager)
- position = Offset(2f, 2f)
+ textLayoutOffset = Offset(2f, 2f)
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -327,7 +331,7 @@
)
// Monitoring update
- androidMatrix.setTranslate(position)
+ androidMatrix.setTranslate(textLayoutOffset)
val expected2 =
builder.build(
textFieldValue,
@@ -346,11 +350,9 @@
TextFieldValue("abc", selection = TextRange(2), composition = TextRange(1, 2))
textInputService.updateState(oldValue = textFieldValue, newValue = textFieldValue)
- position = Offset(1f, 1f)
+ textLayoutOffset = Offset(1f, 1f)
val offsetMapping = OffsetMapping.Identity
val textLayoutResult = getTextLayoutResult(textFieldValue.text)
- val innerTextFieldBounds = Rect.Zero
- val decorationBoxBounds = Rect.Zero
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
@@ -372,7 +374,7 @@
// No immediate update
verify(inputMethodManager, never()).updateCursorAnchorInfo(any())
- position = Offset(2f, 2f)
+ textLayoutOffset = Offset(2f, 2f)
textInputService.updateTextLayoutResult(
textFieldValue = textFieldValue,
offsetMapping = offsetMapping,
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCacheTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCacheTest.kt
index 9acdbc4..c143949 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCacheTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCacheTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import androidx.compose.runtime.getValue
@@ -47,7 +46,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@RunWith(AndroidJUnit4::class)
@MediumTest
class TextFieldLayoutStateCacheTest {
@@ -492,6 +490,7 @@
) { old, new ->
Truth.assertThat(old.layoutInput.style.fontSize).isEqualTo(12.sp)
Truth.assertThat(new.layoutInput.style.fontSize).isEqualTo(23.sp)
+ Truth.assertThat(old.multiParagraph).isNotSameInstanceAs(new.multiParagraph)
}
}
@@ -518,6 +517,8 @@
}
) { old, new ->
Truth.assertThat(new).isNotSameInstanceAs(old)
+ Truth.assertThat(old.layoutInput.maxLines).isEqualTo(Int.MAX_VALUE)
+ Truth.assertThat(new.layoutInput.maxLines).isEqualTo(1)
}
}
@@ -719,6 +720,40 @@
}
}
+ @Test
+ fun textLayoutCalculatedInReadOnlySnapshot_returnedFromCacheWhenCalledFromWriteable() {
+ singleLine = true
+ updateNonMeasureInputs()
+ updateMeasureInputs()
+ val initialLayout = cache.value!!
+
+ singleLine = false
+ updateNonMeasureInputs()
+ val snapshot = Snapshot.takeSnapshot()
+
+ lateinit var layoutFromSnapshot: TextLayoutResult
+ snapshot.enter {
+ with(cache.value!!) {
+ layoutFromSnapshot = this
+ Truth.assertThat(initialLayout.layoutInput.maxLines).isEqualTo(1)
+ Truth.assertThat(layoutInput.maxLines).isEqualTo(Int.MAX_VALUE)
+ }
+ }
+
+ val finalLayout = cache.value!!
+
+ Truth.assertThat(initialLayout.multiParagraph)
+ .isNotSameInstanceAs(layoutFromSnapshot.multiParagraph)
+
+ // Even though the initial text layout calculation after TextStyle change was done in a
+ // read-only snapshot, we still expect to get the same MultiParagraph instance when called
+ // with the same measure/non-measure arguments.
+ Truth.assertThat(finalLayout.multiParagraph)
+ .isSameInstanceAs(layoutFromSnapshot.multiParagraph)
+
+ Truth.assertThat(finalLayout.layoutInput).isEqualTo(layoutFromSnapshot.layoutInput)
+ }
+
private fun assertLayoutChange(
change: () -> Unit,
compare: (old: TextLayoutResult, new: TextLayoutResult) -> Unit
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextInputServiceAndroidCursorAnchorInfoTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextInputServiceAndroidCursorAnchorInfoTest.kt
index 5b2a4c6..b84cf4f 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextInputServiceAndroidCursorAnchorInfoTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextInputServiceAndroidCursorAnchorInfoTest.kt
@@ -22,7 +22,6 @@
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.ExtractedText
import android.view.inputmethod.InputConnection
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.input.ComposeInputMethodManagerTestRule
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.runtime.snapshots.ObserverHandle
@@ -43,6 +42,7 @@
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.toSize
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
@@ -56,7 +56,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
internal class TextInputServiceAndroidCursorAnchorInfoTest {
@@ -109,11 +108,25 @@
}
}
- private val windowOffset = Offset(12f, 34f)
+ private val layoutOffset = Offset(98f, 47f)
+ private val coreNodeOffset = Offset(73f, 50f)
+ private val coreNodeSize = IntSize(25, 51)
+ private val decoratorNodeOffset = Offset(84f, 59f)
+ private val decoratorNodeSize = IntSize(19, 66)
private val layoutState = TextLayoutState().apply {
- coreNodeCoordinates = TestLayoutCoordinates(windowOffset = windowOffset, isAttached = true)
+ textLayoutNodeCoordinates =
+ TestLayoutCoordinates(windowOffset = layoutOffset, isAttached = true)
+ coreNodeCoordinates = TestLayoutCoordinates(
+ windowOffset = coreNodeOffset,
+ size = coreNodeSize,
+ isAttached = true
+ )
decoratorNodeCoordinates =
- TestLayoutCoordinates(windowOffset = windowOffset, isAttached = true)
+ TestLayoutCoordinates(
+ windowOffset = decoratorNodeOffset,
+ size = decoratorNodeSize,
+ isAttached = true
+ )
}
@Test
@@ -130,9 +143,12 @@
selection = textFieldState.selection,
composition = textFieldState.composition,
textLayoutResult = layoutState.layoutResult!!,
- matrix = getAndroidMatrix(windowOffset),
- innerTextFieldBounds = Rect.Zero,
- decorationBoxBounds = Rect.Zero,
+ matrix = getAndroidMatrix(layoutOffset),
+ innerTextFieldBounds = Rect(coreNodeOffset - layoutOffset, coreNodeSize.toSize()),
+ decorationBoxBounds = Rect(
+ decoratorNodeOffset - layoutOffset,
+ decoratorNodeSize.toSize()
+ ),
)
Truth.assertThat(reportedCursorAnchorInfos).containsExactly(expectedInfo)
reportedCursorAnchorInfos.clear()
@@ -182,8 +198,9 @@
Truth.assertThat(reportedCursorAnchorInfos).isEmpty()
// Trigger new layout.
- layoutState.coreNodeCoordinates =
- TestLayoutCoordinates(windowOffset = Offset(67f, 89f), isAttached = true)
+ val newLayoutOffset = Offset(67f, 89f)
+ layoutState.textLayoutNodeCoordinates =
+ TestLayoutCoordinates(windowOffset = newLayoutOffset, isAttached = true)
// Monitoring update.
val expectedInfo = builder.build(
@@ -191,9 +208,12 @@
selection = textFieldState.selection,
composition = textFieldState.composition,
textLayoutResult = layoutState.layoutResult!!,
- matrix = getAndroidMatrix(Offset(67f, 89f)),
- innerTextFieldBounds = Rect.Zero,
- decorationBoxBounds = Rect.Zero,
+ matrix = getAndroidMatrix(newLayoutOffset),
+ innerTextFieldBounds = Rect(coreNodeOffset - newLayoutOffset, coreNodeSize.toSize()),
+ decorationBoxBounds = Rect(
+ decoratorNodeOffset - newLayoutOffset,
+ decoratorNodeSize.toSize()
+ ),
)
Truth.assertThat(reportedCursorAnchorInfos).containsExactly(expectedInfo)
}
@@ -213,16 +233,20 @@
selection = textFieldState.selection,
composition = textFieldState.composition,
textLayoutResult = layoutState.layoutResult!!,
- matrix = getAndroidMatrix(windowOffset),
- innerTextFieldBounds = Rect.Zero,
- decorationBoxBounds = Rect.Zero,
+ matrix = getAndroidMatrix(layoutOffset),
+ innerTextFieldBounds = Rect(coreNodeOffset - layoutOffset, coreNodeSize.toSize()),
+ decorationBoxBounds = Rect(
+ decoratorNodeOffset - layoutOffset,
+ decoratorNodeSize.toSize()
+ ),
)
Truth.assertThat(reportedCursorAnchorInfos).containsExactly(expectedInfo)
reportedCursorAnchorInfos.clear()
// Trigger new layout.
- layoutState.coreNodeCoordinates =
- TestLayoutCoordinates(windowOffset = Offset(67f, 89f), isAttached = true)
+ val newLayoutOffset = Offset(67f, 89f)
+ layoutState.textLayoutNodeCoordinates =
+ TestLayoutCoordinates(windowOffset = newLayoutOffset, isAttached = true)
// Monitoring update.
val expectedInfo2 = builder.build(
@@ -230,9 +254,12 @@
selection = textFieldState.selection,
composition = textFieldState.composition,
textLayoutResult = layoutState.layoutResult!!,
- matrix = getAndroidMatrix(Offset(67f, 89f)),
- innerTextFieldBounds = Rect.Zero,
- decorationBoxBounds = Rect.Zero,
+ matrix = getAndroidMatrix(newLayoutOffset),
+ innerTextFieldBounds = Rect(coreNodeOffset - newLayoutOffset, coreNodeSize.toSize()),
+ decorationBoxBounds = Rect(
+ decoratorNodeOffset - newLayoutOffset,
+ decoratorNodeSize.toSize()
+ ),
)
Truth.assertThat(reportedCursorAnchorInfos).containsExactly(expectedInfo2)
}
@@ -252,9 +279,12 @@
selection = textFieldState.selection,
composition = textFieldState.composition,
textLayoutResult = layoutState.layoutResult!!,
- matrix = getAndroidMatrix(windowOffset),
- innerTextFieldBounds = Rect.Zero,
- decorationBoxBounds = Rect.Zero,
+ matrix = getAndroidMatrix(layoutOffset),
+ innerTextFieldBounds = Rect(coreNodeOffset - layoutOffset, coreNodeSize.toSize()),
+ decorationBoxBounds = Rect(
+ decoratorNodeOffset - layoutOffset,
+ decoratorNodeSize.toSize()
+ ),
)
Truth.assertThat(reportedCursorAnchorInfos).containsExactly(expectedInfo)
reportedCursorAnchorInfos.clear()
@@ -335,12 +365,13 @@
override fun localPositionOf(
sourceCoordinates: LayoutCoordinates,
relativeToSource: Offset
- ): Offset = relativeToSource
+ ): Offset = sourceCoordinates.localToWindow(relativeToSource) - windowOffset
override fun localBoundingBoxOf(
sourceCoordinates: LayoutCoordinates,
clipBounds: Boolean
- ): Rect = Rect.Zero
+ ): Rect =
+ Rect(localPositionOf(sourceCoordinates, Offset.Zero), sourceCoordinates.size.toSize())
override fun transformToScreen(matrix: Matrix) {
matrix.translate(windowOffset.x, windowOffset.y, 0f)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldClickToMoveCursorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldClickToMoveCursorTest.kt
index 687eb4c..5e1b92a 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldClickToMoveCursorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldClickToMoveCursorTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal.selection
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -50,7 +49,6 @@
import org.junit.Rule
import org.junit.Test
-@OptIn(ExperimentalFoundationApi::class)
@LargeTest
class TextFieldClickToMoveCursorTest : FocusedWindowTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt
index 1ba1dee..8312bad 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal.selection
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.BasicTextField
@@ -66,7 +65,6 @@
import org.junit.Rule
import org.junit.Test
-@OptIn(ExperimentalFoundationApi::class)
@LargeTest
class TextFieldCursorHandleTest : FocusedWindowTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldInteractionSourcePressTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldInteractionSourcePressTest.kt
index 7fe1012..b5db834 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldInteractionSourcePressTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldInteractionSourcePressTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal.selection
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
@@ -49,7 +48,6 @@
import org.junit.Rule
import org.junit.Test
-@OptIn(ExperimentalFoundationApi::class)
@LargeTest
class TextFieldInteractionSourcePressTest : FocusedWindowTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldLongPressTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldLongPressTest.kt
index 9495032..cd782ed 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldLongPressTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldLongPressTest.kt
@@ -56,6 +56,7 @@
import androidx.compose.ui.unit.sp
import androidx.test.filters.LargeTest
import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.junit.Rule
@@ -392,6 +393,32 @@
assertThat(state.selection).isEqualTo(TextRange(4, 7))
}
+ @Test
+ fun longPress_startingFromEndPadding_draggingUp_selectsFromLastWord_ltr() {
+ val state = TextFieldState("abc def\nghi jkl\nmno pqr")
+ rule.setTextFieldTestContent {
+ BasicTextField(
+ state = state,
+ textStyle = TextStyle(),
+ modifier = Modifier
+ .testTag(TAG)
+ .width(200.dp)
+ )
+ }
+
+ rule.onNodeWithTag(TAG).performTouchInput {
+ longPress(bottomRight)
+ repeat((bottomRight - topRight).y.roundToInt()) {
+ moveBy(Offset(0f, -1f))
+ }
+ up()
+ }
+
+ rule.runOnIdle {
+ assertThat(state.selection).isEqualTo(TextRange(4, 23))
+ }
+ }
+
//region RTL
@Test
@@ -499,6 +526,34 @@
}
@Test
+ fun longPress_startingFromEndPadding_draggingUp_selectsFromLastWord_rtl() {
+ val state = TextFieldState("$rtlText2\n$rtlText2\n$rtlText2")
+ rule.setTextFieldTestContent {
+ CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+ BasicTextField(
+ state = state,
+ textStyle = TextStyle(),
+ modifier = Modifier
+ .testTag(TAG)
+ .width(200.dp)
+ )
+ }
+ }
+
+ rule.onNodeWithTag(TAG).performTouchInput {
+ longPress(bottomLeft)
+ repeat((bottomLeft - topLeft).y.roundToInt()) {
+ moveBy(Offset(0f, -1f))
+ }
+ up()
+ }
+
+ rule.runOnIdle {
+ assertThat(state.selection).isEqualTo(TextRange(4, 23))
+ }
+ }
+
+ @Test
fun longPress_startDraggingToScrollRight_startHandleDoesNotShow_ltr() {
val state = TextFieldState("abc def ghi ".repeat(10))
rule.setTextFieldTestContent {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifierTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifierTest.kt
index 28d2264..e28a36d 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifierTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifierTest.kt
@@ -18,7 +18,6 @@
import android.view.DragEvent
import android.view.View
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -489,7 +488,7 @@
assertThatOffset(actual).equalsWithTolerance(Offset(lineRightX, secondLineCenterY))
}
- @OptIn(ExperimentalTestApi::class, ExperimentalFoundationApi::class)
+ @OptIn(ExperimentalTestApi::class)
private fun checkMagnifierStayAtEndWhenDraggedBeyondScroll(
handle: Handle,
layoutDirection: LayoutDirection = LayoutDirection.Ltr
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionHandlesTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionHandlesTest.kt
index 466b0a6..90cdaa4 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionHandlesTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionHandlesTest.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.text.input.internal.selection
import android.os.Build
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.Orientation
@@ -25,6 +24,7 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@@ -65,6 +65,7 @@
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.test.filters.LargeTest
@@ -75,7 +76,6 @@
import org.junit.Rule
import org.junit.Test
-@OptIn(ExperimentalFoundationApi::class)
@LargeTest
class TextFieldSelectionHandlesTest : FocusedWindowTest {
@@ -183,6 +183,26 @@
}
@Test
+ fun selectionHandles_appear_whenTextAlignedToEnd() {
+ state = TextFieldState("hello", initialSelection = TextRange(0, 5))
+ rule.setTextFieldTestContent {
+ BasicTextField(
+ state,
+ textStyle = TextStyle(
+ fontSize = fontSize,
+ fontFamily = TEST_FONT_FAMILY,
+ textAlign = TextAlign.End,
+ letterSpacing = 1.2.sp,
+ ),
+ modifier = Modifier.testTag(TAG).fillMaxWidth()
+ )
+ }
+
+ focusAndWait()
+ assertHandlesDisplayed()
+ }
+
+ @Test
fun textField_noSelectionHandles_whenWindowLosesFocus() {
state = TextFieldState("hello, world", initialSelection = TextRange(2, 5))
val focusWindow = mutableStateOf(true)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionOnBackTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionOnBackTest.kt
index c7d8baa..2c5062b 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionOnBackTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionOnBackTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal.selection
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.FocusedWindowTest
@@ -48,10 +47,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(
- ExperimentalTestApi::class,
- ExperimentalFoundationApi::class
-)
+@OptIn(ExperimentalTestApi::class)
@LargeTest
@RunWith(AndroidJUnit4::class)
class TextFieldSelectionOnBackTest : FocusedWindowTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldTextToolbarTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldTextToolbarTest.kt
index 1ee1acd..f0da00c 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldTextToolbarTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldTextToolbarTest.kt
@@ -97,9 +97,9 @@
val fontSize = 10.sp
- val fontSizePx = with(rule.density) { fontSize.toPx() }
+ private val fontSizePx = with(rule.density) { fontSize.toPx() }
- val TAG = "BasicTextField"
+ private val TAG = "BasicTextField"
private var enabled by mutableStateOf(true)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtilsKtTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtilsKtTest.kt
index 8863ec7..d4efca6 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtilsKtTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtilsKtTest.kt
@@ -18,17 +18,44 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Constraints.Companion.fitPrioritizingWidth
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
+/**
+ * Constraints are packed see [Constraints] implementation.
+ *
+ * These constants are the largest values that each slot can hold. The following pairings are the
+ * only ones allowed:
+ *
+ * (Big, Tiny), (Tiny, Big)
+ * (Medium, Small), (Small, Medium)
+ *
+ * For more information see [Constraints] implementation
+ */
+internal const val BigConstraintValue = (1 shl 18) - 1
+
@RunWith(AndroidJUnit4::class)
@SmallTest
class LayoutUtilsKtTest {
@Test
+ fun finalConstraints_doesntThrowWhenLarge() {
+ // this used to throw, ensure it doesn't
+ val subject = finalConstraints(
+ /* minWidth != maxWidth */
+ Constraints(0, 500, 0, BigConstraintValue - 1),
+ true /* width matters */,
+ TextOverflow.Ellipsis,
+ (BigConstraintValue - 1).toFloat()
+ )
+ assertThat(subject).isNotNull()
+ }
+
+ @Test
fun finalConstraints_returnsTightWidth() {
val subject = finalConstraints(
Constraints(500, 500, 0, 50),
@@ -99,7 +126,12 @@
while (1 shl b > 0) {
val height = 1 shl b
/* shouldn't crash */
- val constraints = Constraints.fixedCoerceHeightAndWidthForBits(width, height)
+ val constraints = fitPrioritizingWidth(
+ minWidth = width,
+ maxWidth = width,
+ minHeight = height,
+ maxHeight = height
+ )
println("$width $height => $constraints")
b++
}
@@ -107,48 +139,4 @@
a++
}
}
-
- @Test
- fun fixedCoerce_BigToTiny() {
- val subject = Constraints.fixedCoerceHeightAndWidthForBits(
- BigConstraintValue,
- BigConstraintValue
- )
- assertThat(subject).isEqualTo(
- Constraints.fixed(BigConstraintValue - 1, TinyConstraintValue - 1)
- )
- }
-
- @Test
- fun fixdCoerce_MediumToSmall() {
- val subject = Constraints.fixedCoerceHeightAndWidthForBits(
- MediumConstraintValue - 1,
- BigConstraintValue
- )
- assertThat(subject).isEqualTo(
- Constraints.fixed(MediumConstraintValue - 1, SmallConstraintValue - 1)
- )
- }
-
- @Test
- fun fixdCoerce_SmallToMedium() {
- val subject = Constraints.fixedCoerceHeightAndWidthForBits(
- SmallConstraintValue - 1,
- BigConstraintValue
- )
- assertThat(subject).isEqualTo(
- Constraints.fixed(SmallConstraintValue - 1, MediumConstraintValue - 1)
- )
- }
-
- @Test
- fun fixdCoerce_TinyToBig() {
- val subject = Constraints.fixedCoerceHeightAndWidthForBits(
- TinyConstraintValue - 1,
- BigConstraintValue
- )
- assertThat(subject).isEqualTo(
- Constraints.fixed(TinyConstraintValue - 1, BigConstraintValue - 1)
- )
- }
}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt
index 835fbcc..c06b6e5 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegateTest.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.text.selection
import android.os.Build
-import androidx.compose.foundation.text.InternalFoundationTextApi
import androidx.compose.foundation.text.TEST_FONT_FAMILY
import androidx.compose.foundation.text.TextDelegate
import androidx.compose.ui.geometry.Offset
@@ -1860,7 +1859,6 @@
}
}
- @OptIn(InternalFoundationTextApi::class)
private fun simpleTextLayout(
text: String = "",
fontSize: TextUnit = TextUnit.Unspecified,
@@ -1879,7 +1877,6 @@
).layout(constraints, LayoutDirection.Ltr)
}
- @OptIn(InternalFoundationTextApi::class)
private fun constrainedTextLayout(
text: String = "",
fontSize: TextUnit = 20.sp,
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerContextMenuTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerContextMenuTest.kt
index b050f4f..fb0f59d 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerContextMenuTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerContextMenuTest.kt
@@ -117,11 +117,17 @@
expectedClipboardContent = "Text",
)
+ @Test
+ fun contextMenu_onClickSelectAll() = runClickContextMenuItemTest(
+ labelToClick = ContextMenuItemLabels.SELECT_ALL,
+ expectedSelection = TextRange(0, 14),
+ )
+
@Suppress("SameParameterValue")
private fun runClickContextMenuItemTest(
labelToClick: String,
expectedSelection: TextRange,
- expectedClipboardContent: String,
+ expectedClipboardContent: String? = null,
) {
val initialClipboardText = "clip"
@@ -163,7 +169,8 @@
assertThat(selection!!.toTextRange()).isEqualTo(expectedSelection)
val clipboardContent = clipboardManager.getText()
assertThat(clipboardContent).isNotNull()
- assertThat(clipboardContent!!.text).isEqualTo(expectedClipboardContent)
+ assertThat(clipboardContent!!.text)
+ .isEqualTo(expectedClipboardContent ?: initialClipboardText)
}
// endregion Context Menu Item Click Tests
@@ -178,7 +185,7 @@
cutState = ContextMenuItemState.DOES_NOT_EXIST,
copyState = ContextMenuItemState.DISABLED,
pasteState = ContextMenuItemState.DOES_NOT_EXIST,
- selectAllState = ContextMenuItemState.DOES_NOT_EXIST,
+ selectAllState = ContextMenuItemState.ENABLED,
)
}
@@ -192,7 +199,7 @@
cutState = ContextMenuItemState.DOES_NOT_EXIST,
copyState = ContextMenuItemState.ENABLED,
pasteState = ContextMenuItemState.DOES_NOT_EXIST,
- selectAllState = ContextMenuItemState.DOES_NOT_EXIST,
+ selectAllState = ContextMenuItemState.ENABLED,
)
}
@@ -206,7 +213,7 @@
cutState = ContextMenuItemState.DOES_NOT_EXIST,
copyState = ContextMenuItemState.ENABLED,
pasteState = ContextMenuItemState.DOES_NOT_EXIST,
- selectAllState = ContextMenuItemState.DOES_NOT_EXIST,
+ selectAllState = ContextMenuItemState.DISABLED,
)
}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerTest.kt
index 2c1821a..01fcc59 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerTest.kt
@@ -28,7 +28,6 @@
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
@@ -247,7 +246,7 @@
}
@Test
- fun selectionContinues_toBelowText() = with(rule.density) {
+ fun selectionContinues_toBelowText() {
createSelectionContainer {
Column {
BasicText(
@@ -275,7 +274,7 @@
}
@Test
- fun selectionContinues_toAboveText() = with(rule.density) {
+ fun selectionContinues_toAboveText() {
createSelectionContainer {
Column {
BasicText(
@@ -303,7 +302,7 @@
}
@Test
- fun selectionContinues_toNextText_skipsDisableSelection() = with(rule.density) {
+ fun selectionContinues_toNextText_skipsDisableSelection() {
createSelectionContainer {
Column {
BasicText(
@@ -496,7 +495,7 @@
}
@Test
- @OptIn(ExperimentalTestApi::class, ExperimentalComposeUiApi::class)
+ @OptIn(ExperimentalTestApi::class)
fun selection_doesCopy_whenCopyKeyEventSent() {
lateinit var clipboardManager: ClipboardManager
createSelectionContainer {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/TextFieldMagnifierTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/TextFieldMagnifierTest.kt
index 6a89127..41fda96 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/TextFieldMagnifierTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/TextFieldMagnifierTest.kt
@@ -19,7 +19,6 @@
import android.os.Build
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.Handle
-import androidx.compose.foundation.text.InternalFoundationTextApi
import androidx.compose.foundation.text.LegacyTextFieldState
import androidx.compose.foundation.text.TextDelegate
import androidx.compose.foundation.text.TextFieldDelegate
@@ -45,7 +44,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(InternalFoundationTextApi::class)
@MediumTest
@SdkSuppress(minSdkVersion = 28)
@RunWith(AndroidJUnit4::class)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/LazyColumnMultiTextRegressionTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/LazyColumnMultiTextRegressionTest.kt
index 6c4fadc..f4e77b8 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/LazyColumnMultiTextRegressionTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/LazyColumnMultiTextRegressionTest.kt
@@ -168,7 +168,7 @@
assertTextToolbarTopAt(pointerAreaRect.top)
scrollLines(fromLine = 5, toLine = 3)
- assertThat(textToolbarShown).isFalse()
+ assertTextToolbarTopAt(pointerAreaRect.top)
scrollLines(fromLine = 5, toLine = 7)
assertThat(textToolbarShown).isTrue()
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/MultiTextMinTouchBoundsSelectionGesturesTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/MultiTextMinTouchBoundsSelectionGesturesTest.kt
index 73d0276..07c14c6 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/MultiTextMinTouchBoundsSelectionGesturesTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/MultiTextMinTouchBoundsSelectionGesturesTest.kt
@@ -195,7 +195,7 @@
}
}
- fun runTest(block: () -> Unit) {
+ private fun runTest(block: () -> Unit) {
block()
val expectedSelectableId = expectedText.selectableId
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/util/SingleTextSelectionTestUtils.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/util/SingleTextSelectionTestUtils.kt
index 2bd69a7..a28640f 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/util/SingleTextSelectionTestUtils.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/util/SingleTextSelectionTestUtils.kt
@@ -30,7 +30,7 @@
private val Selection.min get() = min(start.offset, end.offset)
private val Selection.max get() = max(start.offset, end.offset)
-internal class SelectionSubject constructor(
+internal class SelectionSubject(
failureMetadata: FailureMetadata?,
private val subject: Selection?,
private val content: String,
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/util/TextFieldSelectionTestUtils.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/util/TextFieldSelectionTestUtils.kt
index 3e5f50f..74b577c 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/util/TextFieldSelectionTestUtils.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/util/TextFieldSelectionTestUtils.kt
@@ -29,7 +29,7 @@
import com.google.common.truth.Subject.Factory
import com.google.common.truth.Truth
-internal class TextFieldValueSubject constructor(
+internal class TextFieldValueSubject(
failureMetadata: FailureMetadata?,
private val subject: TextFieldValue,
private val textContent: String,
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/HeightInLinesModifierTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/HeightInLinesModifierTest.kt
index 1c3deea..6c99ebd 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/HeightInLinesModifierTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/HeightInLinesModifierTest.kt
@@ -33,7 +33,6 @@
import androidx.compose.ui.platform.ValueElement
import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.AndroidFont
@@ -245,7 +244,7 @@
}
}
- @OptIn(ExperimentalTextApi::class, ExperimentalCoroutinesApi::class)
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
fun asyncFontLoad_changesLineHeight() {
val testDispatcher = UnconfinedTestDispatcher()
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldCursorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldCursorTest.kt
index 38a96aa..44db070 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldCursorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldCursorTest.kt
@@ -113,16 +113,16 @@
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- fun textFieldFocused_cursorRendered() = with(rule.density) {
+ fun textFieldFocused_cursorRendered() {
rule.setTextFieldTestContent {
- BasicTextField(
- value = "",
- onValueChange = {},
- textStyle = textStyle,
- modifier = textFieldModifier,
- cursorBrush = SolidColor(cursorColor),
- onTextLayout = onTextLayout
- )
+ BasicTextField(
+ value = "",
+ onValueChange = {},
+ textStyle = textStyle,
+ modifier = textFieldModifier,
+ cursorBrush = SolidColor(cursorColor),
+ onTextLayout = onTextLayout
+ )
}
focusAndWait()
@@ -138,7 +138,7 @@
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- fun textFieldFocused_cursorWithBrush() = with(rule.density) {
+ fun textFieldFocused_cursorWithBrush() {
rule.setTextFieldTestContent {
BasicTextField(
value = "",
@@ -351,16 +351,16 @@
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- fun cursorBlinkingAnimation() = with(rule.density) {
+ fun cursorBlinkingAnimation() {
rule.setTextFieldTestContent {
- BasicTextField(
- value = "",
- onValueChange = {},
- textStyle = textStyle,
- modifier = textFieldModifier,
- cursorBrush = SolidColor(cursorColor),
- onTextLayout = onTextLayout
- )
+ BasicTextField(
+ value = "",
+ onValueChange = {},
+ textStyle = textStyle,
+ modifier = textFieldModifier,
+ cursorBrush = SolidColor(cursorColor),
+ onTextLayout = onTextLayout
+ )
}
focusAndWait()
@@ -390,18 +390,18 @@
@OptIn(ExperimentalTestApi::class)
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- fun cursorBlinkingAnimation_whenSystemDisablesAnimations() = with(rule.density) {
+ fun cursorBlinkingAnimation_whenSystemDisablesAnimations() {
motionDurationScale.scaleFactor = 0f
rule.setTextFieldTestContent {
- BasicTextField(
- value = "",
- onValueChange = {},
- textStyle = textStyle,
- modifier = textFieldModifier,
- cursorBrush = SolidColor(cursorColor),
- onTextLayout = onTextLayout
- )
+ BasicTextField(
+ value = "",
+ onValueChange = {},
+ textStyle = textStyle,
+ modifier = textFieldModifier,
+ cursorBrush = SolidColor(cursorColor),
+ onTextLayout = onTextLayout
+ )
}
focusAndWait()
@@ -429,15 +429,15 @@
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- fun cursorUnsetColor_noCursor() = with(rule.density) {
+ fun cursorUnsetColor_noCursor() {
rule.setTextFieldTestContent {
- BasicTextField(
- value = "",
- onValueChange = {},
- textStyle = textStyle,
- modifier = textFieldModifier,
- cursorBrush = SolidColor(Color.Unspecified)
- )
+ BasicTextField(
+ value = "",
+ onValueChange = {},
+ textStyle = textStyle,
+ modifier = textFieldModifier,
+ cursorBrush = SolidColor(Color.Unspecified)
+ )
}
focusAndWait()
@@ -469,17 +469,17 @@
@Ignore // b/271927667
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- fun cursorNotBlinking_whileTyping() = with(rule.density) {
+ fun cursorNotBlinking_whileTyping() {
rule.setTextFieldTestContent {
- val text = remember { mutableStateOf("test") }
- BasicTextField(
- value = text.value,
- onValueChange = { text.value = it },
- textStyle = textStyle,
- modifier = textFieldModifier,
- cursorBrush = SolidColor(cursorColor),
- onTextLayout = onTextLayout
- )
+ val text = remember { mutableStateOf("test") }
+ BasicTextField(
+ value = text.value,
+ onValueChange = { text.value = it },
+ textStyle = textStyle,
+ modifier = textFieldModifier,
+ cursorBrush = SolidColor(cursorColor),
+ onTextLayout = onTextLayout
+ )
}
focusAndWait()
@@ -506,18 +506,18 @@
@Test
@FlakyTest(bugId = 283292820)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- fun selectionChanges_cursorNotBlinking() = with(rule.density) {
+ fun selectionChanges_cursorNotBlinking() {
rule.mainClock.autoAdvance = false
val textValue = mutableStateOf(TextFieldValue("test", selection = TextRange(2)))
rule.setTextFieldTestContent {
- BasicTextField(
- value = textValue.value,
- onValueChange = { textValue.value = it },
- textStyle = textStyle,
- modifier = textFieldModifier,
- cursorBrush = SolidColor(cursorColor),
- onTextLayout = onTextLayout
- )
+ BasicTextField(
+ value = textValue.value,
+ onValueChange = { textValue.value = it },
+ textStyle = textStyle,
+ modifier = textFieldModifier,
+ cursorBrush = SolidColor(cursorColor),
+ onTextLayout = onTextLayout
+ )
}
focusAndWait()
@@ -545,17 +545,17 @@
@Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- fun brushChanged_doesntResetTimer() {
+ fun brushChanged_doesNotResetTimer() {
var cursorBrush by mutableStateOf(SolidColor(cursorColor))
rule.setTextFieldTestContent {
- BasicTextField(
- value = "",
- onValueChange = {},
- textStyle = textStyle,
- modifier = textFieldModifier,
- cursorBrush = cursorBrush,
- onTextLayout = onTextLayout
- )
+ BasicTextField(
+ value = "",
+ onValueChange = {},
+ textStyle = textStyle,
+ modifier = textFieldModifier,
+ cursorBrush = cursorBrush,
+ onTextLayout = onTextLayout
+ )
}
focusAndWait()
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldDefaultWidthTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldDefaultWidthTest.kt
index bc01efe..783a112 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldDefaultWidthTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldDefaultWidthTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.textfield
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.width
@@ -177,7 +176,6 @@
}
}
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun DefaultWidthTextField(
text: String,
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldSelectionTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldSelectionTest.kt
index 68ec6e1..8987896 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldSelectionTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldSelectionTest.kt
@@ -16,13 +16,18 @@
package androidx.compose.foundation.textfield
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.FocusedWindowTest
import androidx.compose.foundation.text.Handle
+import androidx.compose.foundation.text.TEST_FONT_FAMILY
import androidx.compose.foundation.text.selection.ReducedVisualTransformation
+import androidx.compose.foundation.text.selection.gestures.util.longPress
import androidx.compose.foundation.text.selection.isSelectionHandle
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalTextToolbar
@@ -43,11 +48,14 @@
import androidx.compose.ui.test.swipeRight
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.TransformedText
import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.sp
import com.google.common.truth.Truth.assertThat
import kotlin.math.roundToInt
import org.junit.Ignore
@@ -400,6 +408,31 @@
)
}
+ @Test
+ fun selectionHandles_appear_whenTextAlignedToEnd() {
+ var value by mutableStateOf("hello")
+
+ rule.setTextFieldTestContent {
+ BasicTextField(
+ value = value,
+ onValueChange = { value = it },
+ textStyle = TextStyle(
+ fontFamily = TEST_FONT_FAMILY,
+ textAlign = TextAlign.End,
+ letterSpacing = 1.2.sp,
+ ),
+ modifier = Modifier.testTag(testTag).fillMaxWidth()
+ )
+ }
+
+ rule.onNodeWithTag(testTag).performTouchInput {
+ longPress(centerRight)
+ }
+ rule.waitForIdle()
+
+ assertHandlesDisplayed()
+ }
+
// starts from [0,1] selection
private fun textField_extendsSelection(
text: String,
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Magnifier.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Magnifier.android.kt
index 186bdd2..7d2e1e9 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Magnifier.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Magnifier.android.kt
@@ -20,8 +20,11 @@
import android.view.View
import android.widget.Magnifier
import androidx.annotation.ChecksSdkIntAtLeast
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.neverEqualPolicy
import androidx.compose.runtime.setValue
import androidx.compose.runtime.withFrameMillis
import androidx.compose.ui.Modifier
@@ -39,7 +42,6 @@
import androidx.compose.ui.node.requireDensity
import androidx.compose.ui.node.requireView
import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.SemanticsPropertyKey
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.unit.Density
@@ -48,6 +50,7 @@
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.toSize
+import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
/**
@@ -263,7 +266,7 @@
private var view: View? = null
/**
- * Current density provided by [LocalDensity]. Used as a receiver to callback functions that
+ * Current density provided by [requireDensity]. Used as a receiver to callback functions that
* are expected return pixel targeted offsets.
*/
private var density: Density? = null
@@ -274,9 +277,28 @@
private var magnifier: PlatformMagnifier? = null
/**
- * Anchor Composable's position in root layout.
+ * The latest [LayoutCoordinates] that is reported by [onGloballyPositioned] callback. Using
+ * [neverEqualPolicy] guarantees that every update to this value restarts snapshots aware
+ * listeners since the [LayoutCoordinates] instance itself does not change.
*/
- private var anchorPositionInRoot: Offset by mutableStateOf(Offset.Unspecified)
+ private var layoutCoordinates: LayoutCoordinates? by mutableStateOf(null, neverEqualPolicy())
+
+ /**
+ * Lazily initialized state that keeps track of anchor Composable's position in root layout.
+ * This state should be derived from [layoutCoordinates]. This variable shouldn't be used
+ * directly from the code, only [anchorPositionInRoot] should initialize and read from this.
+ */
+ private var anchorPositionInRootState: State<Offset>? = null
+
+ private val anchorPositionInRoot: Offset
+ get() {
+ if (anchorPositionInRootState == null) {
+ anchorPositionInRootState = derivedStateOf {
+ layoutCoordinates?.positionInRoot() ?: Offset.Unspecified
+ }
+ }
+ return anchorPositionInRootState?.value ?: Offset.Unspecified
+ }
/**
* Position where [sourceCenter] is mapped on root layout. This is passed to platform magnifier
@@ -290,6 +312,8 @@
*/
private var previousSize: IntSize? = null
+ private var drawSignalChannel: Channel<Unit>? = null
+
fun update(
sourceCenter: Density.() -> Offset,
magnifierCenter: (Density.() -> Offset)?,
@@ -305,9 +329,12 @@
val previousZoom = this.zoom
val previousSize = this.size
val previousCornerRadius = this.cornerRadius
+ val previousUseTextDefault = this.useTextDefault
val previousElevation = this.elevation
val previousClippingEnabled = this.clippingEnabled
val previousPlatformMagnifierFactory = this.platformMagnifierFactory
+ val previousView = this.view
+ val previousDensity = this.density
this.sourceCenter = sourceCenter
this.magnifierCenter = magnifierCenter
@@ -320,26 +347,45 @@
this.onSizeChanged = onSizeChanged
this.platformMagnifierFactory = platformMagnifierFactory
- // On platforms >=Q, the zoom level can be updated dynamically on an existing magnifier, so
- // if the zoom changes between recompositions we don't need to recreate the magnifier. On
- // older platforms, the zoom can only be set initially, so we use the zoom itself as a key
- // so the magnifier gets recreated if it changes.
- if (
- magnifier == null ||
- (zoom != previousZoom && !platformMagnifierFactory.canUpdateZoom) ||
- size != previousSize ||
- cornerRadius != previousCornerRadius ||
- elevation != previousElevation ||
- clippingEnabled != previousClippingEnabled ||
- platformMagnifierFactory != previousPlatformMagnifierFactory
- ) {
+ val view = requireView()
+ val density = requireDensity()
+
+ val shouldRecreate = magnifier != null && // only recreate if it was already created
+ // On platforms >=Q, the zoom level can be updated dynamically on an existing magnifier,
+ // so if the zoom changes between recompositions we don't need to recreate the
+ // magnifier. On older platforms, the zoom can only be set initially, so we use the
+ // zoom itself as a key so the magnifier gets recreated if it changes.
+ ((!zoom.equalsIncludingNaN(previousZoom) && !platformMagnifierFactory.canUpdateZoom) ||
+ size != previousSize ||
+ cornerRadius != previousCornerRadius ||
+ elevation != previousElevation ||
+ useTextDefault != previousUseTextDefault ||
+ clippingEnabled != previousClippingEnabled ||
+ platformMagnifierFactory != previousPlatformMagnifierFactory ||
+ view != previousView ||
+ density != previousDensity)
+
+ if (shouldRecreate) {
recreateMagnifier()
}
+
updateMagnifier()
}
override fun onAttach() {
onObservedReadsChanged()
+ drawSignalChannel = Channel()
+ coroutineScope.launch {
+ while (true) {
+ drawSignalChannel?.receive()
+ // don't update the magnifier immediately, actual frame draw happens right after
+ // all draw commands are recorded. Magnifier update should happen in the next frame.
+ if (magnifier != null) {
+ withFrameMillis { }
+ magnifier?.updateContent()
+ }
+ }
+ }
}
override fun onDetach() {
@@ -349,23 +395,14 @@
override fun onObservedReadsChanged() {
observeReads {
- val previousView = view
- val view = requireView().also { this.view = it }
- val previousDensity = density
- val density = requireDensity().also { this.density = it }
-
- if (magnifier == null || view != previousView || density != previousDensity) {
- recreateMagnifier()
- }
-
updateMagnifier()
}
}
private fun recreateMagnifier() {
magnifier?.dismiss()
- val view = view ?: return
- val density = density ?: return
+ val view = (view ?: requireView()).also { view = it }
+ val density = (density ?: requireDensity()).also { density = it }
magnifier = platformMagnifierFactory.create(
view = view,
useTextDefault = useTextDefault,
@@ -380,37 +417,38 @@
}
private fun updateMagnifier() {
- val magnifier = magnifier ?: return
- val density = density ?: return
+ val density = density ?: requireDensity().also { density = it }
val sourceCenterOffset = sourceCenter(density)
- sourceCenterInRoot =
- if (anchorPositionInRoot.isSpecified && sourceCenterOffset.isSpecified) {
- anchorPositionInRoot + sourceCenterOffset
- } else {
- Offset.Unspecified
- }
- // Once the position is set, it's never null again, so we don't need to worry
- // about dismissing the magnifier if this expression changes value.
- if (sourceCenterInRoot.isSpecified) {
- // Calculate magnifier center if it's provided. Only accept if the returned value is
- // specified. Then add [anchorPositionInRoot] for relative positioning.
+ // the order of these checks are important since we don't want to query
+ // `anchorPositionInRoot` if `sourceCenterOffset` is unspecified.
+ if (sourceCenterOffset.isSpecified && anchorPositionInRoot.isSpecified) {
+ sourceCenterInRoot = anchorPositionInRoot + sourceCenterOffset
+ // Calculate magnifier center if it's provided. Only accept if the returned
+ // value is specified. Then add [anchorPositionInRoot] for relative positioning.
val magnifierCenter = magnifierCenter?.invoke(density)
?.takeIf { it.isSpecified }
?.let { anchorPositionInRoot + it }
?: Offset.Unspecified
- magnifier.update(
+ if (magnifier == null) {
+ recreateMagnifier()
+ }
+
+ magnifier?.update(
sourceCenter = sourceCenterInRoot,
magnifierCenter = magnifierCenter,
zoom = zoom
)
updateSizeIfNecessary()
- } else {
- // Can't place the magnifier at an unspecified location, so just hide it.
- magnifier.dismiss()
+ return
}
+
+ // If the flow reaches here, it means that the magnifier could not be placed at a specified
+ // position. We now need to hide it so it doesn't show up at an invalid location.
+ sourceCenterInRoot = Offset.Unspecified
+ magnifier?.dismiss()
}
private fun updateSizeIfNecessary() {
@@ -425,19 +463,14 @@
override fun ContentDrawScope.draw() {
drawContent()
- // don't update the magnifier immediately, actual frame draw happens right after all draw
- // commands are recorded. Magnifier update should happen in the next frame.
- coroutineScope.launch {
- withFrameMillis { }
- magnifier?.updateContent()
- }
+ drawSignalChannel?.trySend(Unit)
}
override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
// The mutable state must store the Offset, not the LocalCoordinates, because the same
// LocalCoordinates instance may be sent to this callback multiple times, not implement
// equals, or be stable, and so won't invalidate the snapshotFlow.
- anchorPositionInRoot = coordinates.positionInRoot()
+ layoutCoordinates = coordinates
}
override fun SemanticsPropertyReceiver.applySemantics() {
@@ -448,3 +481,13 @@
@ChecksSdkIntAtLeast(api = 28)
internal fun isPlatformMagnifierSupported(sdkVersion: Int = Build.VERSION.SDK_INT) =
sdkVersion >= 28
+
+/**
+ * Normally `Float.NaN == Float.NaN` returns false but we use [Float.NaN] to mean Unspecified.
+ * The comparison between two unspecified values should return _equal_ if we are only interested
+ * in state changes.
+ */
+internal fun Float.equalsIncludingNaN(other: Float): Boolean {
+ if (this.isNaN() && other.isNaN()) return true
+ return this == other
+}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.android.kt
index d11fe39..56a5472 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.android.kt
@@ -254,7 +254,7 @@
* Returns whether or not the context menu should be dismissed.
*/
fun item(
- label: String,
+ label: @Composable () -> String,
modifier: Modifier = Modifier,
enabled: Boolean = true,
/**
@@ -272,11 +272,12 @@
*/
onClick: () -> Unit,
) {
- check(label.isNotBlank()) { "Label must not be blank" }
composables += { colors ->
+ val resolvedLabel = label()
+ check(resolvedLabel.isNotBlank()) { "Label must not be blank" }
ContextMenuItem(
modifier = modifier,
- label = label,
+ label = resolvedLabel,
enabled = enabled,
colors = colors,
leadingIcon = leadingIcon,
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.android.kt
index 54abefd..fd93543 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.android.kt
@@ -126,16 +126,19 @@
}
val latestFrameVsyncNs = TimeUnit.MILLISECONDS.toNanos(view.drawingTime)
val nextFrameNs = latestFrameVsyncNs + frameIntervalNs
- val oneOverTimeTaskAllowed = System.nanoTime() > nextFrameNs
- val scope = PrefetchRequestScopeImpl(nextFrameNs, oneOverTimeTaskAllowed)
+ val scope = PrefetchRequestScopeImpl(nextFrameNs)
var scheduleForNextFrame = false
while (prefetchRequests.isNotEmpty() && !scheduleForNextFrame) {
- val request = prefetchRequests[0]
- val hasMoreWorkToDo = with(request) { scope.execute() }
- if (hasMoreWorkToDo) {
- scheduleForNextFrame = true
+ if (scope.availableTimeNanos() > 0) {
+ val request = prefetchRequests[0]
+ val hasMoreWorkToDo = with(request) { scope.execute() }
+ if (hasMoreWorkToDo) {
+ scheduleForNextFrame = true
+ } else {
+ prefetchRequests.removeAt(0)
+ }
} else {
- prefetchRequests.removeAt(0)
+ scheduleForNextFrame = true
}
}
@@ -182,24 +185,10 @@
class PrefetchRequestScopeImpl(
private val nextFrameTimeNs: Long,
- isOneOverTimeTaskAllowed: Boolean
) : PrefetchRequestScope {
- private var canDoOverTimeTask = isOneOverTimeTaskAllowed
-
- override val availableTimeNanos: Long
- get() {
- // This logic is meant to be temporary until we replace the isOneOverTimeTaskAllowed
- // logic with something more general. For now, we assume that a PrefetchRequest
- // impl will check availableTimeNanos once per task and we give it a large amount
- // of time the first time it checks if we allow an overtime task.
- return if (canDoOverTimeTask) {
- canDoOverTimeTask = false
- Long.MAX_VALUE
- } else {
- max(0, nextFrameTimeNs - System.nanoTime())
- }
- }
+ override fun availableTimeNanos() =
+ max(0, nextFrameTimeNs - System.nanoTime())
}
companion object {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/AndroidCursorHandle.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/AndroidCursorHandle.android.kt
index dc589a2..8fe6980 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/AndroidCursorHandle.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/AndroidCursorHandle.android.kt
@@ -22,6 +22,10 @@
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.selection.HandlePopup
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.foundation.text.selection.OffsetProvider
+import androidx.compose.foundation.text.selection.SelectionHandleAnchor
+import androidx.compose.foundation.text.selection.SelectionHandleInfo
+import androidx.compose.foundation.text.selection.SelectionHandleInfoKey
import androidx.compose.foundation.text.selection.createHandleImage
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -31,6 +35,7 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.drawscope.withTransform
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.isSpecified
@@ -41,17 +46,25 @@
@Composable
internal actual fun CursorHandle(
- handlePosition: Offset,
+ offsetProvider: OffsetProvider,
modifier: Modifier,
minTouchTargetSize: DpSize
) {
+ val finalModifier = modifier.semantics {
+ this[SelectionHandleInfoKey] = SelectionHandleInfo(
+ handle = Handle.Cursor,
+ position = offsetProvider.provide(),
+ anchor = SelectionHandleAnchor.Middle,
+ visible = true,
+ )
+ }
HandlePopup(
- positionProvider = { handlePosition },
+ positionProvider = offsetProvider,
handleReferencePoint = Alignment.TopCenter
) {
if (minTouchTargetSize.isSpecified) {
Box(
- modifier = modifier.requiredSizeIn(
+ modifier = finalModifier.requiredSizeIn(
minWidth = minTouchTargetSize.width,
minHeight = minTouchTargetSize.height
),
@@ -60,7 +73,7 @@
DefaultCursorHandle()
}
} else {
- DefaultCursorHandle(modifier)
+ DefaultCursorHandle(finalModifier)
}
}
}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/ContextMenu.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/ContextMenu.android.kt
index 0425d17..8c17b8a 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/ContextMenu.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/ContextMenu.android.kt
@@ -16,6 +16,7 @@
package androidx.compose.foundation.text
+import androidx.compose.foundation.contextmenu.ContextMenuScope
import androidx.compose.foundation.contextmenu.ContextMenuState
import androidx.compose.foundation.contextmenu.close
import androidx.compose.foundation.text.input.internal.selection.TextFieldSelectionState
@@ -88,3 +89,15 @@
@Composable
fun resolvedString(): String = stringResource(stringId)
}
+
+internal inline fun ContextMenuScope.TextItem(
+ state: ContextMenuState,
+ label: TextContextMenuItems,
+ enabled: Boolean,
+ crossinline operation: () -> Unit
+) {
+ item(label = { label.resolvedString() }, enabled = enabled) {
+ operation()
+ state.close()
+ }
+}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoBuilder.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoBuilder.android.kt
index 99d3944..e4168d9 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoBuilder.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoBuilder.android.kt
@@ -28,11 +28,11 @@
* Helper function to build
* [CursorAnchorInfo](https://developer.android.com/reference/android/view/inputmethod/CursorAnchorInfo).
*
- * @param matrix matrix that transforms local coordinates into screen coordinates
- * @param innerTextFieldBounds visible bounds of the text field in local coordinates, or an empty
- * rectangle if the text field is not visible
- * @param decorationBoxBounds visible bounds of the decoration box in local coordinates, or an empty
- * rectangle if the decoration box is not visible
+ * @param matrix matrix that transforms text layout coordinates into screen coordinates
+ * @param innerTextFieldBounds visible bounds of the text field in text layout coordinates, or an
+ * empty rectangle if the text field is not visible
+ * @param decorationBoxBounds visible bounds of the decoration box in text layout coordinates, or an
+ * empty rectangle if the decoration box is not visible
*/
internal fun CursorAnchorInfo.Builder.build(
text: CharSequence,
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoController.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoController.android.kt
index 81b76da..a45685a 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoController.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoController.android.kt
@@ -23,10 +23,9 @@
import android.view.inputmethod.InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS
import android.view.inputmethod.InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER
import android.view.inputmethod.InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.selection.visibleBounds
import androidx.compose.runtime.snapshotFlow
-import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Matrix
import androidx.compose.ui.graphics.setFrom
import kotlinx.coroutines.CoroutineScope
@@ -36,7 +35,6 @@
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
-@OptIn(ExperimentalFoundationApi::class)
internal class CursorAnchorInfoController(
private val textFieldState: TransformedTextFieldState,
private val textLayoutState: TextLayoutState,
@@ -165,6 +163,9 @@
private fun calculateCursorAnchorInfo(): CursorAnchorInfo? {
// State reads
+ val textLayoutCoordinates = textLayoutState.textLayoutNodeCoordinates
+ ?.takeIf { it.isAttached }
+ ?: return null
val coreCoordinates = textLayoutState.coreNodeCoordinates
?.takeIf { it.isAttached }
?: return null
@@ -175,16 +176,15 @@
?: return null
val text = textFieldState.visualText
- // Updates matrix to transform text field local coordinates to screen coordinates.
+ // Updates matrix to transform text layout coordinates to screen coordinates.
matrix.reset()
- coreCoordinates.transformToScreen(matrix)
+ textLayoutCoordinates.transformToScreen(matrix)
androidMatrix.setFrom(matrix)
- val innerTextFieldBounds: Rect = coreCoordinates.visibleBounds()
- val decorationBoxBounds: Rect = coreCoordinates.localBoundingBoxOf(
- decorationBoxCoordinates,
- clipBounds = false
- )
+ val innerTextFieldBounds = coreCoordinates.visibleBounds()
+ .translate(textLayoutCoordinates.localPositionOf(coreCoordinates, Offset.Zero))
+ val decorationBoxBounds = decorationBoxCoordinates.visibleBounds()
+ .translate(textLayoutCoordinates.localPositionOf(decorationBoxCoordinates, Offset.Zero))
return builder.build(
text,
text.selection,
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/EditorInfo.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/EditorInfo.android.kt
index 1bdf683..a53b03d 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/EditorInfo.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/EditorInfo.android.kt
@@ -203,5 +203,11 @@
InsertGesture::class.java,
RemoveSpaceGesture::class.java
)
+ editorInfo.supportedHandwritingGesturePreviews = setOf(
+ SelectGesture::class.java,
+ DeleteGesture::class.java,
+ SelectRangeGesture::class.java,
+ DeleteRangeGesture::class.java
+ )
}
}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/HandwritingGesture.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/HandwritingGesture.android.kt
index d47c722..ba0422e 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/HandwritingGesture.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/HandwritingGesture.android.kt
@@ -421,6 +421,30 @@
}
@DoNotInline
+ internal fun LegacyTextFieldState.previewHandwritingGesture(
+ gesture: PreviewableHandwritingGesture,
+ textFieldSelectionManager: TextFieldSelectionManager?,
+ cancellationSignal: CancellationSignal?
+ ): Boolean {
+ val text = untransformedText ?: return false
+ if (text != layoutResult?.value?.layoutInput?.text) {
+ // The text is transformed or layout is null, handwriting gesture failed.
+ return false
+ }
+ when (gesture) {
+ is SelectGesture -> previewSelectGesture(gesture, textFieldSelectionManager)
+ is DeleteGesture -> previewDeleteGesture(gesture, textFieldSelectionManager)
+ is SelectRangeGesture -> previewSelectRangeGesture(gesture, textFieldSelectionManager)
+ is DeleteRangeGesture -> previewDeleteRangeGesture(gesture, textFieldSelectionManager)
+ else -> return false
+ }
+ cancellationSignal?.setOnCancelListener {
+ textFieldSelectionManager?.clearPreviewHighlight()
+ }
+ return true
+ }
+
+ @DoNotInline
private fun LegacyTextFieldState.performSelectGesture(
gesture: SelectGesture,
textSelectionManager: TextFieldSelectionManager?,
@@ -439,6 +463,20 @@
}
@DoNotInline
+ private fun LegacyTextFieldState.previewSelectGesture(
+ gesture: SelectGesture,
+ textFieldSelectionManager: TextFieldSelectionManager?
+ ) {
+ textFieldSelectionManager?.setSelectionPreviewHighlight(
+ getRangeForScreenRect(
+ gesture.selectionArea.toComposeRect(),
+ gesture.granularity.toTextGranularity(),
+ TextInclusionStrategy.ContainsCenter
+ )
+ )
+ }
+
+ @DoNotInline
private fun LegacyTextFieldState.performDeleteGesture(
gesture: DeleteGesture,
text: AnnotatedString,
@@ -463,6 +501,20 @@
}
@DoNotInline
+ private fun LegacyTextFieldState.previewDeleteGesture(
+ gesture: DeleteGesture,
+ textFieldSelectionManager: TextFieldSelectionManager?
+ ) {
+ textFieldSelectionManager?.setDeletionPreviewHighlight(
+ getRangeForScreenRect(
+ gesture.deletionArea.toComposeRect(),
+ gesture.granularity.toTextGranularity(),
+ TextInclusionStrategy.ContainsCenter
+ )
+ )
+ }
+
+ @DoNotInline
private fun LegacyTextFieldState.performSelectRangeGesture(
gesture: SelectRangeGesture,
textSelectionManager: TextFieldSelectionManager?,
@@ -486,6 +538,21 @@
}
@DoNotInline
+ private fun LegacyTextFieldState.previewSelectRangeGesture(
+ gesture: SelectRangeGesture,
+ textFieldSelectionManager: TextFieldSelectionManager?
+ ) {
+ textFieldSelectionManager?.setSelectionPreviewHighlight(
+ getRangeForScreenRects(
+ gesture.selectionStartArea.toComposeRect(),
+ gesture.selectionEndArea.toComposeRect(),
+ gesture.granularity.toTextGranularity(),
+ TextInclusionStrategy.ContainsCenter
+ )
+ )
+ }
+
+ @DoNotInline
private fun LegacyTextFieldState.performDeleteRangeGesture(
gesture: DeleteRangeGesture,
text: AnnotatedString,
@@ -510,6 +577,21 @@
}
@DoNotInline
+ private fun LegacyTextFieldState.previewDeleteRangeGesture(
+ gesture: DeleteRangeGesture,
+ textFieldSelectionManager: TextFieldSelectionManager?
+ ) {
+ textFieldSelectionManager?.setDeletionPreviewHighlight(
+ getRangeForScreenRects(
+ gesture.deletionStartArea.toComposeRect(),
+ gesture.deletionEndArea.toComposeRect(),
+ gesture.granularity.toTextGranularity(),
+ TextInclusionStrategy.ContainsCenter
+ )
+ )
+ }
+
+ @DoNotInline
private fun LegacyTextFieldState.performJoinOrSplitGesture(
gesture: JoinOrSplitGesture,
text: AnnotatedString,
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoBuilder.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoBuilder.android.kt
index 5702c47..6e0d405 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoBuilder.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoBuilder.android.kt
@@ -37,11 +37,11 @@
* @param textFieldValue the text field's [TextFieldValue]
* @param offsetMapping the offset mapping for the text field's visual transformation
* @param textLayoutResult the text field's [TextLayoutResult]
- * @param matrix matrix that transforms local coordinates into screen coordinates
- * @param innerTextFieldBounds visible bounds of the text field in local coordinates, or an empty
- * rectangle if the text field is not visible
- * @param decorationBoxBounds visible bounds of the decoration box in local coordinates, or an empty
- * rectangle if the decoration box is not visible
+ * @param matrix matrix that transforms text layout coordinates into screen coordinates
+ * @param innerTextFieldBounds visible bounds of the text field in text layout coordinates, or an
+ * empty rectangle if the text field is not visible
+ * @param decorationBoxBounds visible bounds of the decoration box in text layout coordinates, or an
+ * empty rectangle if the decoration box is not visible
* @param includeInsertionMarker whether to include insertion marker info in the CursorAnchorInfo
* @param includeCharacterBounds whether to include character bounds info in the CursorAnchorInfo
* @param includeEditorBounds whether to include editor bounds info in the CursorAnchorInfo
@@ -127,8 +127,8 @@
flags = flags or CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION
if (isRtl) flags = flags or CursorAnchorInfo.FLAG_IS_RTL
- // Sets the location of the text insertion point (zero width cursor) as a rectangle in local
- // coordinates.
+ // Sets the location of the text insertion point (zero width cursor) as a rectangle in text
+ // layout coordinates.
setInsertionMarkerLocation(x, cursorRect.top, cursorRect.bottom, cursorRect.bottom, flags)
return this
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoController.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoController.android.kt
index bda500a..fdc08c8 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoController.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoController.android.kt
@@ -93,10 +93,10 @@
* @param textFieldValue the text field's [TextFieldValue]
* @param offsetMapping the offset mapping for the visual transformation
* @param textLayoutResult the text field's [TextLayoutResult]
- * @param innerTextFieldBounds visible bounds of the text field in local coordinates, or an
- * empty rectangle if the text field is not visible
- * @param decorationBoxBounds visible bounds of the decoration box in local coordinates, or an
- * empty rectangle if the decoration box is not visible
+ * @param innerTextFieldBounds visible bounds of the text field in text layout coordinates,
+ * an empty rectangle if the text field is not visible
+ * @param decorationBoxBounds visible bounds of the decoration box in text layout coordinates,
+ * or an empty rectangle if the decoration box is not visible
*/
fun updateTextLayoutResult(
textFieldValue: TextFieldValue,
@@ -135,8 +135,10 @@
if (!inputMethodManager.isActive()) return
matrix.reset()
- // Updates matrix to transform text field local coordinates to screen coordinates.
+ // Updates matrix to transform decoration box coordinates to screen coordinates.
localToScreen(matrix)
+ // Updates matrix to transform text layout coordinates to screen coordinates.
+ matrix.translate(-decorationBoxBounds!!.left, -decorationBoxBounds!!.top, 0f)
androidMatrix.setFrom(matrix)
inputMethodManager.updateCursorAnchorInfo(
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.android.kt
index 3d32bde..c09b28d 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.android.kt
@@ -46,7 +46,6 @@
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
private const val DEBUG_CLASS = "AndroidLegacyPlatformTextInputServiceAdapter"
@@ -67,7 +66,7 @@
private var job: Job? = null
private var currentRequest: LegacyTextInputMethodRequest? = null
private var backingStylusHandwritingTrigger: MutableSharedFlow<Unit>? = null
- private var stylusHandwritingTrigger: MutableSharedFlow<Unit>? = null
+ private val stylusHandwritingTrigger: MutableSharedFlow<Unit>?
get() {
val finalStylusHandwritingTrigger = backingStylusHandwritingTrigger
if (finalStylusHandwritingTrigger != null) {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/RecordingInputConnection.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/RecordingInputConnection.android.kt
index 4d897e0..34e71cb 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/RecordingInputConnection.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/RecordingInputConnection.android.kt
@@ -18,6 +18,7 @@
import android.os.Build
import android.os.Bundle
+import android.os.CancellationSignal
import android.os.Handler
import android.text.TextUtils
import android.util.Log
@@ -30,10 +31,12 @@
import android.view.inputmethod.HandwritingGesture
import android.view.inputmethod.InputConnection
import android.view.inputmethod.InputContentInfo
+import android.view.inputmethod.PreviewableHandwritingGesture
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.compose.foundation.text.LegacyTextFieldState
import androidx.compose.foundation.text.input.internal.HandwritingGestureApi34.performHandwritingGesture
+import androidx.compose.foundation.text.input.internal.HandwritingGestureApi34.previewHandwritingGesture
import androidx.compose.foundation.text.selection.TextFieldSelectionManager
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.text.input.CommitTextCommand
@@ -426,6 +429,22 @@
}
}
+ override fun previewHandwritingGesture(
+ gesture: PreviewableHandwritingGesture,
+ cancellationSignal: CancellationSignal?
+ ): Boolean {
+ if (DEBUG) { logDebug("previewHandwritingGesture($gesture, $cancellationSignal)") }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ return Api34LegacyPerformHandwritingGestureImpl.previewHandwritingGesture(
+ legacyTextFieldState,
+ textFieldSelectionManager,
+ gesture,
+ cancellationSignal
+ )
+ }
+ return false
+ }
+
// endregion
// region Unsupported callbacks
@@ -533,4 +552,18 @@
consumer.accept(result)
}
}
+
+ @DoNotInline
+ fun previewHandwritingGesture(
+ legacyTextFieldState: LegacyTextFieldState?,
+ textFieldSelectionManager: TextFieldSelectionManager?,
+ gesture: PreviewableHandwritingGesture,
+ cancellationSignal: CancellationSignal?
+ ): Boolean {
+ return legacyTextFieldState?.previewHandwritingGesture(
+ gesture,
+ textFieldSelectionManager,
+ cancellationSignal
+ ) ?: false
+ }
}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnection.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnection.android.kt
index 69dad42..80be4a10 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnection.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnection.android.kt
@@ -555,7 +555,6 @@
}
}
-@OptIn(ExperimentalFoundationApi::class)
private fun TextFieldCharSequence.toExtractedText(): ExtractedText {
val res = ExtractedText()
res.text = this
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.android.kt
index b427583..e770393 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.android.kt
@@ -18,36 +18,16 @@
import androidx.compose.foundation.contextmenu.ContextMenuScope
import androidx.compose.foundation.contextmenu.ContextMenuState
-import androidx.compose.foundation.contextmenu.close
import androidx.compose.foundation.text.TextContextMenuItems
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.foundation.text.TextItem
-@ReadOnlyComposable
-@Composable
internal fun TextFieldSelectionState.contextMenuBuilder(
state: ContextMenuState,
-): ContextMenuScope.() -> Unit {
- val cutString = TextContextMenuItems.Cut.resolvedString()
- val copyString = TextContextMenuItems.Copy.resolvedString()
- val pasteString = TextContextMenuItems.Paste.resolvedString()
- val selectAllString = TextContextMenuItems.SelectAll.resolvedString()
- return {
- item(state, label = cutString, enabled = canCut()) { cut() }
- item(state, label = copyString, enabled = canCopy()) { copy(cancelSelection = false) }
- item(state, label = pasteString, enabled = canPaste()) { paste() }
- item(state, label = selectAllString, enabled = canSelectAll()) { selectAll() }
+): ContextMenuScope.() -> Unit = {
+ TextItem(state, TextContextMenuItems.Cut, enabled = canCut()) { cut() }
+ TextItem(state, TextContextMenuItems.Copy, enabled = canCopy()) {
+ copy(cancelSelection = false)
}
-}
-
-private inline fun ContextMenuScope.item(
- state: ContextMenuState,
- label: String,
- enabled: Boolean,
- crossinline operation: () -> Unit
-) {
- item(label, enabled = enabled) {
- operation()
- state.close()
- }
+ TextItem(state, TextContextMenuItems.Paste, enabled = canPaste()) { paste() }
+ TextItem(state, TextContextMenuItems.SelectAll, enabled = canSelectAll()) { selectAll() }
}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.android.kt
index 19d75d2..0e4100a6 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.android.kt
@@ -19,14 +19,12 @@
import androidx.compose.foundation.PlatformMagnifierFactory
import androidx.compose.foundation.contextmenu.ContextMenuScope
import androidx.compose.foundation.contextmenu.ContextMenuState
-import androidx.compose.foundation.contextmenu.close
import androidx.compose.foundation.isPlatformMagnifierSupported
import androidx.compose.foundation.magnifier
import androidx.compose.foundation.text.KeyCommand
import androidx.compose.foundation.text.TextContextMenuItems
+import androidx.compose.foundation.text.TextItem
import androidx.compose.foundation.text.platformDefaultKeyMapping
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -72,23 +70,19 @@
}
}
-@ReadOnlyComposable
-@Composable
internal fun SelectionManager.contextMenuBuilder(
state: ContextMenuState,
-): ContextMenuScope.() -> Unit {
- val copyString = TextContextMenuItems.Copy.resolvedString()
- return {
- listOf(
- item(
- label = copyString,
- enabled = isNonEmptySelection(),
- onClick = {
- copy()
- state.close()
- },
- ),
- // TODO(b/240143283) Add select all item
- )
- }
+): ContextMenuScope.() -> Unit = {
+ listOf(
+ TextItem(
+ state = state,
+ label = TextContextMenuItems.Copy,
+ enabled = isNonEmptySelection(),
+ ) { copy() },
+ TextItem(
+ state = state,
+ label = TextContextMenuItems.SelectAll,
+ enabled = !isEntireContainerSelected(),
+ ) { selectAll() },
+ )
}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.android.kt
index ec44e34..5aa6139 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.android.kt
@@ -16,16 +16,13 @@
package androidx.compose.foundation.text.selection
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.PlatformMagnifierFactory
import androidx.compose.foundation.contextmenu.ContextMenuScope
import androidx.compose.foundation.contextmenu.ContextMenuState
-import androidx.compose.foundation.contextmenu.close
import androidx.compose.foundation.isPlatformMagnifierSupported
import androidx.compose.foundation.magnifier
import androidx.compose.foundation.text.TextContextMenuItems
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.foundation.text.TextItem
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -42,7 +39,6 @@
// We use composed{} to read a local, but don't provide inspector info because the underlying
// magnifier modifier provides more meaningful inspector info.
-@OptIn(ExperimentalFoundationApi::class)
internal actual fun Modifier.textFieldMagnifier(manager: TextFieldSelectionManager): Modifier {
// Avoid tracking animation state on older Android versions that don't support magnifiers.
if (!isPlatformMagnifierSupported()) {
@@ -72,49 +68,29 @@
}
}
-@ReadOnlyComposable
-@Composable
internal fun TextFieldSelectionManager.contextMenuBuilder(
contextMenuState: ContextMenuState
-): ContextMenuScope.() -> Unit {
- val cutString = TextContextMenuItems.Cut.resolvedString()
- val copyString = TextContextMenuItems.Copy.resolvedString()
- val pasteString = TextContextMenuItems.Paste.resolvedString()
- val selectAllString = TextContextMenuItems.SelectAll.resolvedString()
- return {
- val isPassword = visualTransformation is PasswordVisualTransformation
- val hasSelection = !value.selection.collapsed
- item(
- label = cutString,
- enabled = hasSelection && editable && !isPassword,
- onClick = {
- cut()
- contextMenuState.close()
- },
- )
- item(
- label = copyString,
- enabled = hasSelection && !isPassword,
- onClick = {
- copy(cancelSelection = false)
- contextMenuState.close()
- },
- )
- item(
- label = pasteString,
- enabled = editable && clipboardManager?.hasText() == true,
- onClick = {
- paste()
- contextMenuState.close()
- },
- )
- item(
- label = selectAllString,
- enabled = value.selection.length != value.text.length,
- onClick = {
- selectAll()
- contextMenuState.close()
- },
- )
- }
+): ContextMenuScope.() -> Unit = {
+ val isPassword = visualTransformation is PasswordVisualTransformation
+ val hasSelection = !value.selection.collapsed
+ TextItem(
+ state = contextMenuState,
+ label = TextContextMenuItems.Cut,
+ enabled = hasSelection && editable && !isPassword,
+ ) { cut() }
+ TextItem(
+ state = contextMenuState,
+ label = TextContextMenuItems.Copy,
+ enabled = hasSelection && !isPassword,
+ ) { copy(cancelSelection = false) }
+ TextItem(
+ state = contextMenuState,
+ label = TextContextMenuItems.Paste,
+ enabled = editable && clipboardManager?.hasText() == true,
+ ) { paste() }
+ TextItem(
+ state = contextMenuState,
+ label = TextContextMenuItems.SelectAll,
+ enabled = value.selection.length != value.text.length,
+ ) { selectAll() }
}
diff --git a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionFakes.kt b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionFakes.kt
index 24b1fc8..ca6265d 100644
--- a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionFakes.kt
+++ b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionFakes.kt
@@ -16,6 +16,8 @@
package androidx.compose.foundation.text.selection
+import androidx.collection.LongObjectMap
+import androidx.collection.emptyLongObjectMap
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.LayoutCoordinates
@@ -52,7 +54,7 @@
isStartHandle: Boolean = false,
previousSelection: Selection? = null,
shouldRecomputeSelection: Boolean = true,
- subSelections: Map<Long, Selection> = emptyMap(),
+ subSelections: LongObjectMap<Selection> = emptyLongObjectMap(),
): SelectionLayout {
return getSelectionLayoutFake(
infos = listOf(
@@ -199,7 +201,7 @@
isStartHandle: Boolean = false,
previousSelection: Selection? = null,
shouldRecomputeSelection: Boolean = true,
- subSelections: Map<Long, Selection> = emptyMap(),
+ subSelections: LongObjectMap<Selection> = emptyLongObjectMap(),
): SelectionLayout = FakeSelectionLayout(
size = infos.size,
crossStatus = crossStatus,
@@ -231,9 +233,9 @@
override val previousSelection: Selection?,
private val middleInfos: List<SelectableInfo>,
private val shouldRecomputeSelection: Boolean,
- private val subSelections: Map<Long, Selection>,
+ private val subSelections: LongObjectMap<Selection>,
) : SelectionLayout {
- override fun createSubSelections(selection: Selection): Map<Long, Selection> = subSelections
+ override fun createSubSelections(selection: Selection): LongObjectMap<Selection> = subSelections
override fun forEachMiddleInfo(block: (SelectableInfo) -> Unit) {
middleInfos.forEach(block)
}
@@ -280,7 +282,7 @@
var boundingBoxes: Map<Int, Rect> = emptyMap()
private val selectableKey = 1L
- private val fakeSelectAllSelection: Selection = Selection(
+ var fakeSelectAllSelection: Selection? = Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = 0,
@@ -307,7 +309,7 @@
)
}
- override fun getSelectAllSelection(): Selection {
+ override fun getSelectAllSelection(): Selection? {
return fakeSelectAllSelection
}
diff --git a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionLayoutTest.kt b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionLayoutTest.kt
index 88ad2d4..267c99a 100644
--- a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionLayoutTest.kt
+++ b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionLayoutTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.foundation.text.selection
+import androidx.collection.LongObjectMap
import androidx.compose.foundation.text.selection.Direction.AFTER
import androidx.compose.foundation.text.selection.Direction.BEFORE
import androidx.compose.foundation.text.selection.Direction.ON
@@ -1020,7 +1021,7 @@
fun createSubSelections_singleLayout_validSelection_returnsInputSelection() {
val layout = getSingleSelectionLayoutForTest()
val selection = getSelection()
- val actual = layout.createSubSelections(selection)
+ val actual = layout.createSubSelections(selection).toMap()
assertThat(actual).hasSize(1)
// We don't care about the selectableId since it isn't used anyways
assertThat(actual.toList().single().second).isEqualTo(selection)
@@ -1032,7 +1033,7 @@
appendInfoForTest(selectableId = 1L)
}
val selection = getSelection()
- assertThat(layout.createSubSelections(selection)).containsExactly(1L, selection)
+ assertThat(layout.createSubSelections(selection).toMap()).containsExactly(1L, selection)
}
@Test
@@ -1064,7 +1065,7 @@
appendInfoForTest(selectableId = 2L)
}
val selection = getSelection(startSelectableId = 2L, endSelectableId = 2L)
- assertThat(layout.createSubSelections(selection)).containsExactly(2L, selection)
+ assertThat(layout.createSubSelections(selection).toMap()).containsExactly(2L, selection)
}
@Test
@@ -1082,7 +1083,7 @@
)
}
val selection = getSelection(startSelectableId = 1L, endSelectableId = 2L)
- assertThat(layout.createSubSelections(selection)).containsExactly(
+ assertThat(layout.createSubSelections(selection).toMap()).containsExactly(
1L, getSelection(startSelectableId = 1L, endSelectableId = 1L),
2L, getSelection(startSelectableId = 2L, endSelectableId = 2L),
)
@@ -1108,7 +1109,7 @@
)
}
val selection = getSelection(startSelectableId = 1L, endSelectableId = 3L)
- assertThat(layout.createSubSelections(selection)).containsExactly(
+ assertThat(layout.createSubSelections(selection).toMap()).containsExactly(
1L, getSelection(startSelectableId = 1L, endSelectableId = 1L),
2L, getSelection(startSelectableId = 2L, endSelectableId = 2L),
3L, getSelection(startSelectableId = 3L, endSelectableId = 3L),
@@ -1140,7 +1141,7 @@
)
}
val selection = getSelection(startSelectableId = 1L, endSelectableId = 4L)
- assertThat(layout.createSubSelections(selection)).containsExactly(
+ assertThat(layout.createSubSelections(selection).toMap()).containsExactly(
1L, getSelection(startSelectableId = 1L, endSelectableId = 1L),
2L, getSelection(startSelectableId = 2L, endSelectableId = 2L),
3L, getSelection(startSelectableId = 3L, endSelectableId = 3L),
@@ -1166,7 +1167,7 @@
endOffset = 0,
handlesCrossed = true
)
- assertThat(layout.createSubSelections(selection)).containsExactly(1L, selection)
+ assertThat(layout.createSubSelections(selection).toMap()).containsExactly(1L, selection)
}
@Test
@@ -1194,7 +1195,7 @@
endOffset = 0,
handlesCrossed = true
)
- assertThat(layout.createSubSelections(selection)).containsExactly(
+ assertThat(layout.createSubSelections(selection).toMap()).containsExactly(
1L,
getSelection(
startSelectableId = 1L,
@@ -1246,7 +1247,7 @@
endOffset = 0,
handlesCrossed = true
)
- assertThat(layout.createSubSelections(selection)).containsExactly(
+ assertThat(layout.createSubSelections(selection).toMap()).containsExactly(
1L,
getSelection(
startSelectableId = 1L,
@@ -1313,7 +1314,7 @@
endOffset = 0,
handlesCrossed = true
)
- assertThat(layout.createSubSelections(selection)).containsExactly(
+ assertThat(layout.createSubSelections(selection).toMap()).containsExactly(
1L,
getSelection(
startSelectableId = 1L,
@@ -1604,3 +1605,7 @@
)
}
}
+
+private fun <T> LongObjectMap<T>.toMap(): Map<Long, T> = buildMap {
+ [email protected] { long, obj -> put(long, obj) }
+}
diff --git a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
index a44824d..085643e 100644
--- a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
+++ b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
@@ -16,14 +16,21 @@
package androidx.compose.foundation.text.selection
+import androidx.collection.LongObjectMap
+import androidx.collection.emptyLongObjectMap
+import androidx.collection.longObjectMapOf
+import androidx.collection.mutableLongObjectMapOf
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.style.ResolvedTextDirection
+import androidx.compose.ui.util.fastForEach
import com.google.common.truth.Truth.assertThat
+import kotlin.test.fail
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -90,10 +97,10 @@
selectable.clear()
selectable.selectableId = selectableId
selectionRegistrar.subscribe(selectable)
- selectionRegistrar.subselections = mapOf(
- selectableId to fakeSelection,
- startSelectableId to fakeSelection,
- endSelectableId to fakeSelection
+ selectionRegistrar.subselections = longObjectMapOf(
+ selectableId, fakeSelection,
+ startSelectableId, fakeSelection,
+ endSelectableId, fakeSelection
)
selectionManager.containerLayoutCoordinates = containerLayoutCoordinates
selectionManager.hapticFeedBack = hapticFeedback
@@ -209,14 +216,14 @@
}
@Test
- fun mergeSelections_selectAll() {
+ fun mergeSelections_selectAllInSelectable() {
val anotherSelectableId = 100L
val selectableAnother = mock<Selectable>()
whenever(selectableAnother.selectableId).thenReturn(anotherSelectableId)
selectionRegistrar.subscribe(selectableAnother)
- selectionManager.selectAll(
+ selectionManager.selectAllInSelectable(
selectableId = selectableId,
previousSelection = fakeSelection
)
@@ -249,7 +256,7 @@
handlesCrossed = false
)
selectionManager.selection = selection
- selectionRegistrar.subselections = mapOf(selectableId to selection)
+ selectionRegistrar.subselections = longObjectMapOf(selectableId, selection)
assertThat(selectionManager.isNonEmptySelection()).isTrue()
}
@@ -274,7 +281,7 @@
handlesCrossed = false
)
selectionManager.selection = selection
- selectionRegistrar.subselections = mapOf(selectableId to selection)
+ selectionRegistrar.subselections = longObjectMapOf(selectableId, selection)
assertThat(selectionManager.isNonEmptySelection()).isFalse()
}
@@ -308,8 +315,8 @@
handlesCrossed = true
)
- selectionRegistrar.subselections = mapOf(
- endSelectableId to Selection(
+ selectionRegistrar.subselections = longObjectMapOf(
+ endSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = annotatedString.length,
@@ -322,7 +329,7 @@
),
handlesCrossed = true
),
- middleSelectableId to Selection(
+ middleSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = annotatedString.length,
@@ -335,7 +342,7 @@
),
handlesCrossed = true
),
- startSelectableId to Selection(
+ startSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = startOffset,
@@ -382,8 +389,8 @@
handlesCrossed = false
)
- selectionRegistrar.subselections = mapOf(
- startSelectableId to Selection(
+ selectionRegistrar.subselections = longObjectMapOf(
+ startSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = annotatedString.length,
@@ -396,7 +403,7 @@
),
handlesCrossed = false
),
- middleSelectableId to Selection(
+ middleSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = 0,
@@ -409,7 +416,7 @@
),
handlesCrossed = false
),
- endSelectableId to Selection(
+ endSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = 0,
@@ -456,8 +463,8 @@
handlesCrossed = true
)
- selectionRegistrar.subselections = mapOf(
- startSelectableId to Selection(
+ selectionRegistrar.subselections = longObjectMapOf(
+ startSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = annotatedString.length,
@@ -470,7 +477,7 @@
),
handlesCrossed = true
),
- middleSelectableId to Selection(
+ middleSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = 0,
@@ -483,7 +490,7 @@
),
handlesCrossed = true
),
- endSelectableId to Selection(
+ endSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = 0,
@@ -504,7 +511,7 @@
@Test
fun getSelectedText_selection_null_return_null() {
selectionManager.selection = null
- selectionRegistrar.subselections = emptyMap()
+ selectionRegistrar.subselections = emptyLongObjectMap()
assertThat(selectionManager.getSelectedText()).isNull()
assertThat(selectable.getTextCalledTimes).isEqualTo(0)
@@ -531,7 +538,7 @@
handlesCrossed = false
)
selectionManager.selection = selection
- selectionRegistrar.subselections = mapOf(selectableId to selection)
+ selectionRegistrar.subselections = longObjectMapOf(selectableId, selection)
assertThat(selectionManager.getSelectedText())
.isEqualTo(annotatedString.subSequence(startOffset, endOffset))
@@ -559,7 +566,7 @@
handlesCrossed = true
)
selectionManager.selection = selection
- selectionRegistrar.subselections = mapOf(selectableId to selection)
+ selectionRegistrar.subselections = longObjectMapOf(selectableId, selection)
assertThat(selectionManager.getSelectedText())
.isEqualTo(annotatedString.subSequence(endOffset, startOffset))
@@ -595,8 +602,8 @@
handlesCrossed = false
)
- selectionRegistrar.subselections = mapOf(
- startSelectableId to Selection(
+ selectionRegistrar.subselections = longObjectMapOf(
+ startSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = startOffset,
@@ -609,7 +616,7 @@
),
handlesCrossed = false
),
- middleSelectableId to Selection(
+ middleSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = 0,
@@ -622,7 +629,7 @@
),
handlesCrossed = false
),
- endSelectableId to Selection(
+ endSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = 0,
@@ -676,8 +683,8 @@
handlesCrossed = true
)
- selectionRegistrar.subselections = mapOf(
- endSelectableId to Selection(
+ selectionRegistrar.subselections = longObjectMapOf(
+ endSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = annotatedString.length,
@@ -690,7 +697,7 @@
),
handlesCrossed = true
),
- middleSelectableId to Selection(
+ middleSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = annotatedString.length,
@@ -703,7 +710,7 @@
),
handlesCrossed = true
),
- startSelectableId to Selection(
+ startSelectableId, Selection(
start = Selection.AnchorInfo(
direction = ResolvedTextDirection.Ltr,
offset = startOffset,
@@ -731,7 +738,7 @@
@Test
fun copy_selection_null_not_trigger_clipboardManager() {
selectionManager.selection = null
- selectionRegistrar.subselections = emptyMap()
+ selectionRegistrar.subselections = emptyLongObjectMap()
selectionManager.copy()
@@ -759,7 +766,7 @@
handlesCrossed = true
)
selectionManager.selection = selection
- selectionRegistrar.subselections = mapOf(selectableId to selection)
+ selectionRegistrar.subselections = longObjectMapOf(selectableId, selection)
selectionManager.copy()
@@ -793,7 +800,7 @@
handlesCrossed = true
)
selectionManager.selection = selection
- selectionRegistrar.subselections = mapOf(selectableId to selection)
+ selectionRegistrar.subselections = longObjectMapOf(selectableId, selection)
selectionManager.hasFocus = true
selectionManager.showToolbar = true
@@ -803,7 +810,7 @@
any(),
isNull(),
isNull(),
- isNull()
+ any()
)
}
@@ -828,7 +835,7 @@
handlesCrossed = true
)
selectionManager.selection = selection
- selectionRegistrar.subselections = mapOf(selectableId to selection)
+ selectionRegistrar.subselections = longObjectMapOf(selectableId, selection)
selectionManager.hasFocus = false
selectionManager.showToolbar = true
@@ -865,7 +872,7 @@
selectionManager.onRelease()
- verify(selectionRegistrar).subselections = emptyMap()
+ verify(selectionRegistrar).subselections = emptyLongObjectMap()
assertThat(selection).isNull()
verify(spyLambda, times(1)).invoke(null)
@@ -896,12 +903,12 @@
selectionManager.onSelectionChange = spyLambda
selectionManager.selection = fakeSelection
- selectionRegistrar.subselections = mapOf(
- startSelectableId to fakeSelection
+ selectionRegistrar.subselections = longObjectMapOf(
+ startSelectableId, fakeSelection
)
selectionRegistrar.notifySelectableChange(startSelectableId)
- verify(selectionRegistrar).subselections = emptyMap()
+ verify(selectionRegistrar).subselections = emptyLongObjectMap()
assertThat(selection).isNull()
verify(spyLambda, times(1)).invoke(null)
verify(
@@ -909,4 +916,362 @@
times(1)
).performHapticFeedback(HapticFeedbackType.TextHandleMove)
}
+
+ // region isEntireContainerSelected Tests
+ @Test
+ fun isEntireContainerSelected_noSelectables_returnsTrue() {
+ isEntireContainerSelectedTest(expectedResult = true)
+ }
+
+ @Test
+ fun isEntireContainerSelected_singleEmptySelectable_returnsTrue() {
+ isEntireContainerSelectedTest(
+ expectedResult = true,
+ IsEntireContainerSelectedData(text = "", selection = null),
+ )
+ }
+
+ @Test
+ fun isEntireContainerSelected_multipleEmptySelectables_returnsTrue() {
+ isEntireContainerSelectedTest(
+ expectedResult = true,
+ IsEntireContainerSelectedData(text = "", selection = null),
+ IsEntireContainerSelectedData(text = "", selection = null),
+ IsEntireContainerSelectedData(text = "", selection = null),
+ )
+ }
+
+ @Test
+ fun isEntireContainerSelected_emptySurroundingNonEmpty_fullySelected_returnsTrue() {
+ isEntireContainerSelectedTest(
+ expectedResult = true,
+ IsEntireContainerSelectedData(text = "", selection = null),
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ IsEntireContainerSelectedData(text = "", selection = null),
+ )
+ }
+
+ @Test
+ fun isEntireContainerSelected_nonEmptySurroundingEmpty_fullySelected_returnsTrue() {
+ isEntireContainerSelectedTest(
+ expectedResult = true,
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ IsEntireContainerSelectedData(text = "", selection = TextRange(0, 0)),
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ )
+ }
+
+ @Test
+ fun isEntireContainerSelected_nonEmptyFirstTextNotSelected_returnsFalse() {
+ isEntireContainerSelectedTest(
+ expectedResult = false,
+ IsEntireContainerSelectedData(text = "Text", selection = null),
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ )
+ }
+
+ @Test
+ fun isEntireContainerSelected_nonEmptyLastTextNotSelected_returnsFalse() {
+ isEntireContainerSelectedTest(
+ expectedResult = false,
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ IsEntireContainerSelectedData(text = "Text", selection = null),
+ )
+ }
+
+ @Test
+ fun isEntireContainerSelected_firstTextPartiallySelected_returnsFalse() {
+ isEntireContainerSelectedTest(
+ expectedResult = false,
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(1, 4)),
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ )
+ }
+
+ @Test
+ fun isEntireContainerSelected_lastTextPartiallySelected_returnsFalse() {
+ isEntireContainerSelectedTest(
+ expectedResult = false,
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 4)),
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(0, 3)),
+ )
+ }
+
+ @Test
+ fun isEntireContainerSelected_reversedSelectionFullySelected_returnsTrue() {
+ isEntireContainerSelectedTest(
+ expectedResult = true,
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(4, 0)),
+ )
+ }
+
+ @Test
+ fun isEntireContainerSelected_reversedSelectionPartiallySelected_returnsFalse() {
+ isEntireContainerSelectedTest(
+ expectedResult = false,
+ IsEntireContainerSelectedData(text = "Text", selection = TextRange(3, 0)),
+ )
+ }
+
+ /**
+ * Data necessary to set up a [SelectionManager.isEntireContainerSelected] unit test.
+ *
+ * @param text The text for the [Selectable] to return in [Selectable.getText].
+ * @param selection The selection to be associated with the [SelectionRegistrar.subselections].
+ * Null implies "do not include this selectable in the sub-selection".
+ */
+ private data class IsEntireContainerSelectedData(
+ val text: String,
+ val selection: TextRange?,
+ )
+
+ private fun isEntireContainerSelectedTest(
+ expectedResult: Boolean,
+ vararg selectableStates: IsEntireContainerSelectedData,
+ ) {
+ val selectables = selectableStates.mapIndexed { index, item ->
+ FakeSelectable().apply {
+ selectableId = index + 1L
+ textToReturn = AnnotatedString(item.text)
+ }
+ }
+
+ val registrar = SelectionRegistrarImpl().apply {
+ selectables.fastForEach { subscribe(it) }
+ subselections = selectableStates
+ .withIndex()
+ .filter { it.value.selection != null }
+ .associate { (index, item) ->
+ val id = index + 1L
+ val selection = item.selection
+ id to Selection(
+ start = Selection.AnchorInfo(
+ direction = ResolvedTextDirection.Ltr,
+ offset = selection!!.start,
+ selectableId = id
+ ),
+ end = Selection.AnchorInfo(
+ direction = ResolvedTextDirection.Ltr,
+ offset = selection.end,
+ selectableId = id
+ ),
+ handlesCrossed = selection.reversed
+ )
+ }
+ .toLongObjectMap()
+ }
+
+ val manager = SelectionManager(registrar).apply {
+ containerLayoutCoordinates = MockCoordinates()
+ }
+
+ assertThat(manager.isEntireContainerSelected()).run {
+ if (expectedResult) isTrue() else isFalse()
+ }
+ }
+ // endregion isEntireContainerSelected Tests
+
+ // region selectAll Tests
+ @Test
+ fun selectAll_noSelectables_noSelection() {
+ selectAllTest(
+ expectedSelection = null,
+ expectedSubSelectionRanges = emptyMap(),
+ )
+ }
+
+ @Test
+ fun selectAll_singleUnSelectable_noSelection() {
+ selectAllTest(
+ expectedSelection = null,
+ expectedSubSelectionRanges = emptyMap(),
+ SelectAllData(text = "Text", selection = null),
+ )
+ }
+
+ @Test
+ fun selectAll_singleSelectable_selectedAsExpected() {
+ selectAllTest(
+ expectedSelection = expectedSelection(0, 4),
+ expectedSubSelectionRanges = mapOf(1L to TextRange(0, 4)),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ )
+ }
+
+ @Test
+ fun selectAll_multiSelectable_selectedAsExpected() {
+ selectAllTest(
+ expectedSelection = expectedSelection(
+ startOffset = 0,
+ endOffset = 4,
+ startSelectableId = 1L,
+ endSelectableId = 3L,
+ ),
+ expectedSubSelectionRanges = mapOf(
+ 1L to TextRange(0, 4),
+ 2L to TextRange(0, 4),
+ 3L to TextRange(0, 4),
+ ),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ )
+ }
+
+ @Test
+ fun selectAll_multiSelectable_skipFirst_selectedAsExpected() {
+ selectAllTest(
+ expectedSelection = expectedSelection(
+ startOffset = 0,
+ endOffset = 4,
+ startSelectableId = 2L,
+ endSelectableId = 3L,
+ ),
+ expectedSubSelectionRanges = mapOf(
+ 2L to TextRange(0, 4),
+ 3L to TextRange(0, 4),
+ ),
+ SelectAllData(text = "Text", selection = null),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ )
+ }
+
+ @Test
+ fun selectAll_multiSelectable_skipMiddle_selectedAsExpected() {
+ selectAllTest(
+ expectedSelection = expectedSelection(
+ startOffset = 0,
+ endOffset = 4,
+ startSelectableId = 1L,
+ endSelectableId = 3L,
+ ),
+ expectedSubSelectionRanges = mapOf(
+ 1L to TextRange(0, 4),
+ 3L to TextRange(0, 4),
+ ),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ SelectAllData(text = "Text", selection = null),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ )
+ }
+
+ @Test
+ fun selectAll_multiSelectable_skipLast_selectedAsExpected() {
+ selectAllTest(
+ expectedSelection = expectedSelection(
+ startOffset = 0,
+ endOffset = 4,
+ startSelectableId = 1L,
+ endSelectableId = 2L,
+ ),
+ expectedSubSelectionRanges = mapOf(
+ 1L to TextRange(0, 4),
+ 2L to TextRange(0, 4),
+ ),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ SelectAllData(text = "Text", selection = TextRange(0, 4)),
+ SelectAllData(text = "Text", selection = null),
+ )
+ }
+
+ private fun expectedSelection(
+ startOffset: Int,
+ endOffset: Int,
+ startSelectableId: Long = 1L,
+ endSelectableId: Long = 1L,
+ handlesCrossed: Boolean = false,
+ ): Selection = Selection(
+ start = Selection.AnchorInfo(
+ direction = ResolvedTextDirection.Ltr,
+ offset = startOffset,
+ selectableId = startSelectableId
+ ),
+ end = Selection.AnchorInfo(
+ direction = ResolvedTextDirection.Ltr,
+ offset = endOffset,
+ selectableId = endSelectableId
+ ),
+ handlesCrossed = handlesCrossed
+ )
+
+ /**
+ * Data necessary to set up a [SelectionManager.selectAll] unit test.
+ *
+ * @param text The text for the [FakeSelectable] to return in [Selectable.getText].
+ * @param selection The selection for the [FakeSelectable] to return in
+ * [Selectable.getSelectAllSelection].
+ */
+ private data class SelectAllData(
+ val text: String,
+ val selection: TextRange?,
+ )
+
+ private fun selectAllTest(
+ expectedSelection: Selection?,
+ expectedSubSelectionRanges: Map<Long, TextRange>,
+ vararg selectableStates: SelectAllData,
+ ) {
+ val selectables = selectableStates.mapIndexed { index, item ->
+ val id = index + 1L
+ val range = item.selection
+ FakeSelectable().apply {
+ selectableId = id
+ textToReturn = AnnotatedString(item.text)
+ fakeSelectAllSelection = range?.let {
+ Selection(
+ start = Selection.AnchorInfo(
+ direction = ResolvedTextDirection.Ltr,
+ offset = it.start,
+ selectableId = id
+ ),
+ end = Selection.AnchorInfo(
+ direction = ResolvedTextDirection.Ltr,
+ offset = it.end,
+ selectableId = id
+ ),
+ handlesCrossed = it.reversed
+ )
+ }
+ }
+ }
+
+ val registrar = SelectionRegistrarImpl().apply {
+ selectables.fastForEach { subscribe(it) }
+ }
+
+ val expectedSubSelections = expectedSubSelectionRanges.mapValues { (id, range) ->
+ expectedSelection(
+ startOffset = range.start,
+ endOffset = range.end,
+ startSelectableId = id,
+ endSelectableId = id,
+ handlesCrossed = range.start > range.end
+ )
+ }
+ .toLongObjectMap()
+
+ SelectionManager(registrar).apply {
+ containerLayoutCoordinates = MockCoordinates()
+ onSelectionChange = { newSelection ->
+ if (expectedSelection == null) {
+ fail("Expected no selection update, but received one anyways.")
+ }
+ assertThat(newSelection).isEqualTo(expectedSelection)
+ }
+ selectAll()
+ }
+
+ assertThat(registrar.subselections).isEqualTo(expectedSubSelections)
+ }
+ // endregion selectAll Tests
+
+ private fun <T> Map<Long, T>.toLongObjectMap(): LongObjectMap<T> =
+ mutableLongObjectMapOf<T>().apply {
+ [email protected] { key -> put(key, this@toLongObjectMap[key]!!) }
+ }
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index 2cea5ef..28375ae 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -823,13 +823,12 @@
if ((this.onLongClick == null) != (onLongClick == null)) {
// Adding or removing longClick should cancel any existing press interactions
disposeInteractions()
+ // Adding or removing longClick should add / remove the corresponding property
+ invalidateSemantics()
resetPointerInputHandling = true
}
- if (this.onLongClick !== onLongClick) {
- this.onLongClick = onLongClick
- invalidateSemantics()
- }
+ this.onLongClick = onLongClick
if ((this.onDoubleClick == null) != (onDoubleClick == null)) {
resetPointerInputHandling = true
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
index 5021fa1..5fce395 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
@@ -463,7 +463,7 @@
// Tagging as direct manipulation, such that consumers of this offset can decide whether
// to exclude this offset on their coordinates calculation. Such as whether an
// `approachLayout` will animate it or directly apply the offset without animation.
- withDirectManipulationPlacement {
+ withCurrentFrameOfReferencePlacement {
placeable.placeRelativeWithLayer(xOffset, yOffset)
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
index f95084a..5db29b1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
@@ -455,71 +455,7 @@
}
}
- private val pointerInputNode = delegate(SuspendingPointerInputModifierNode {
- // TODO: conditionally undelegate when aosp/2462416 lands?
- if ([email protected]) return@SuspendingPointerInputModifierNode
- // re-create tracker when pointer input block restarts. This lazily creates the tracker
- // only when it is need.
- val velocityTracker = VelocityTracker()
- coroutineScope {
- try {
- awaitPointerEventScope {
- while (isActive) {
- awaitDownAndSlop(
- _canDrag,
- ::startDragImmediately,
- velocityTracker,
- pointerDirectionConfig
- )?.let {
- /**
- * The gesture crossed the touch slop, events are now relevant
- * and should be propagated
- */
- if (!isListeningForEvents) {
- if (channel == null) {
- channel = Channel(capacity = Channel.UNLIMITED)
- }
- startListeningForEvents()
- }
- var isDragSuccessful = false
- try {
- isDragSuccessful = awaitDrag(
- it.first,
- it.second,
- velocityTracker,
- channel
- ) { event ->
- pointerDirectionConfig.calculateDeltaChange(
- event.positionChangeIgnoreConsumed()
- ) != 0f
- }
- } catch (cancellation: CancellationException) {
- isDragSuccessful = false
- if (!isActive) throw cancellation
- } finally {
- val maximumVelocity = currentValueOf(LocalViewConfiguration)
- .maximumFlingVelocity
- val event = if (isDragSuccessful) {
- val velocity = velocityTracker.calculateVelocity(
- Velocity(maximumVelocity, maximumVelocity)
- )
- velocityTracker.resetTracking()
- DragStopped(velocity)
- } else {
- DragCancelled
- }
- channel?.trySend(event)
- }
- }
- }
- }
- } catch (exception: CancellationException) {
- if (!isActive) {
- throw exception
- }
- }
- }
- })
+ private var pointerInputNode: SuspendingPointerInputModifierNode? = null
override fun onDetach() {
isListeningForEvents = false
@@ -531,11 +467,80 @@
pass: PointerEventPass,
bounds: IntSize
) {
- pointerInputNode.onPointerEvent(pointerEvent, pass, bounds)
+ if (enabled && pointerInputNode == null) {
+ pointerInputNode = delegate(initializePointerInputNode())
+ }
+ pointerInputNode?.onPointerEvent(pointerEvent, pass, bounds)
+ }
+
+ private fun initializePointerInputNode(): SuspendingPointerInputModifierNode {
+ return SuspendingPointerInputModifierNode {
+ // re-create tracker when pointer input block restarts. This lazily creates the tracker
+ // only when it is need.
+ val velocityTracker = VelocityTracker()
+ coroutineScope {
+ try {
+ awaitPointerEventScope {
+ while (isActive) {
+ awaitDownAndSlop(
+ _canDrag,
+ ::startDragImmediately,
+ velocityTracker,
+ pointerDirectionConfig
+ )?.let {
+ /**
+ * The gesture crossed the touch slop, events are now relevant
+ * and should be propagated
+ */
+ if (!isListeningForEvents) {
+ if (channel == null) {
+ channel = Channel(capacity = Channel.UNLIMITED)
+ }
+ startListeningForEvents()
+ }
+ var isDragSuccessful = false
+ try {
+ isDragSuccessful = awaitDrag(
+ it.first,
+ it.second,
+ velocityTracker,
+ channel
+ ) { event ->
+ pointerDirectionConfig.calculateDeltaChange(
+ event.positionChangeIgnoreConsumed()
+ ) != 0f
+ }
+ } catch (cancellation: CancellationException) {
+ isDragSuccessful = false
+ if (!isActive) throw cancellation
+ } finally {
+ val maximumVelocity = currentValueOf(LocalViewConfiguration)
+ .maximumFlingVelocity
+ val event = if (isDragSuccessful) {
+ val velocity = velocityTracker.calculateVelocity(
+ Velocity(maximumVelocity, maximumVelocity)
+ )
+ velocityTracker.resetTracking()
+ DragStopped(velocity)
+ } else {
+ DragCancelled
+ }
+ channel?.trySend(event)
+ }
+ }
+ }
+ }
+ } catch (exception: CancellationException) {
+ if (!isActive) {
+ throw exception
+ }
+ }
+ }
+ }
}
override fun onCancelPointerInput() {
- pointerInputNode.onCancelPointerInput()
+ pointerInputNode?.onCancelPointerInput()
}
private suspend fun CoroutineScope.processDragStart(event: DragStarted) {
@@ -578,11 +583,14 @@
isResetPointerInputHandling: Boolean = false
) {
var resetPointerInputHandling = isResetPointerInputHandling
+
this.canDrag = canDrag
if (this.enabled != enabled) {
this.enabled = enabled
if (!enabled) {
disposeInteractionSource()
+ pointerInputNode?.let { undelegate(it) }
+ pointerInputNode = null
}
resetPointerInputHandling = true
}
@@ -592,7 +600,7 @@
}
if (resetPointerInputHandling) {
- pointerInputNode.resetPointerInputHandler()
+ pointerInputNode?.resetPointerInputHandler()
}
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable2D.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable2D.kt
index c8e26da..f9c9571 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable2D.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable2D.kt
@@ -108,7 +108,7 @@
* Create and remember default implementation of [Draggable2DState] interface that allows to pass a
* simple action that will be invoked when the drag occurs.
*
- * This is the simplest way to set up a [draggable] modifier. When constructing this
+ * This is the simplest way to set up a [draggable2D] modifier. When constructing this
* [Draggable2DState], you must provide a [onDelta] lambda, which will be invoked whenever
* drag happens (by gesture input or a custom [Draggable2DState.drag] call) with the delta in
* pixels.
@@ -145,13 +145,13 @@
* @param onDragStarted callback that will be invoked when drag is about to start at the starting
* position, allowing user to suspend and perform preparation for drag, if desired.This suspend
* function is invoked with the draggable2D scope, allowing for async processing, if desired. Note
- * that the scope used here is the onw provided by the draggable2D node, for long running work that
+ * that the scope used here is the one provided by the draggable2D node, for long-running work that
* needs to outlast the modifier being in the composition you should use a scope that fits the
* lifecycle needed.
* @param onDragStopped callback that will be invoked when drag is finished, allowing the
* user to react on velocity and process it. This suspend function is invoked with the draggable2D
- * scope, allowing for async processing, if desired. Note that the scope used here is the onw
- * provided by the draggable2D scope, for long running work that needs to outlast the modifier being
+ * scope, allowing for async processing, if desired. Note that the scope used here is the one
+ * provided by the draggable2D scope, for long-running work that needs to outlast the modifier being
* in the composition you should use a scope that fits the lifecycle needed.
* @param reverseDirection reverse the direction of the scroll, so top to bottom scroll will
* behave like bottom to top and left to right will behave like right to left.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt
index 9f6aaf7..d32ce49 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt
@@ -16,11 +16,16 @@
package androidx.compose.foundation.gestures.snapping
+import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
import androidx.compose.foundation.lazy.grid.LazyGridLayoutInfo
import androidx.compose.foundation.lazy.grid.LazyGridState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.util.fastForEach
+import kotlin.math.absoluteValue
+import kotlin.math.sign
/**
* A [SnapLayoutInfoProvider] for LazyGrids.
@@ -30,7 +35,7 @@
* This position should be considered with regards to the start edge of the item and the placement
* within the viewport.
*
- * @return A [SnapLayoutInfoProvider] that can be used with [SnapFlingBehavior]
+ * @return A [SnapLayoutInfoProvider] that can be used with [snapFlingBehavior]
*/
fun SnapLayoutInfoProvider(
lazyGridState: LazyGridState,
@@ -39,8 +44,26 @@
private val layoutInfo: LazyGridLayoutInfo
get() = lazyGridState.layoutInfo
- override fun calculateSnappingOffset(
- currentVelocity: Float
+ private val averageItemSize: Int
+ get() {
+ val layoutInfo = layoutInfo
+ return if (layoutInfo.visibleItemsInfo.isEmpty()) {
+ 0
+ } else {
+ val numberOfItems = layoutInfo.visibleItemsInfo.size
+ layoutInfo.visibleItemsInfo.sumOf {
+ it.sizeOnMainAxis(layoutInfo.orientation)
+ } / numberOfItems
+ }
+ }
+
+ override fun calculateApproachOffset(velocity: Float, decayOffset: Float): Float {
+ return (decayOffset.absoluteValue - averageItemSize)
+ .coerceAtLeast(0.0f) * decayOffset.sign
+ }
+
+ override fun calculateSnapOffset(
+ velocity: Float
): Float {
var distanceFromItemBeforeTarget = Float.NEGATIVE_INFINITY
var distanceFromItemAfterTarget = Float.POSITIVE_INFINITY
@@ -70,13 +93,33 @@
}
return calculateFinalOffset(
- with(lazyGridState.density) { calculateFinalSnappingItem(currentVelocity) },
+ with(lazyGridState.density) { calculateFinalSnappingItem(velocity) },
distanceFromItemBeforeTarget,
distanceFromItemAfterTarget
)
}
}
+/**
+ * Create and remember a FlingBehavior for decayed snapping in Lazy Grids. This will snap
+ * the item according to [snapPosition].
+ *
+ * @param lazyGridState The [LazyGridState] from the LazyGrid where this [FlingBehavior] will
+ * be used.
+ * @param snapPosition The desired positioning of the snapped item within the main layout.
+ * This position should be considered with regards to the start edge of the item and the placement
+ * within the viewport.
+ */
+@Composable
+fun rememberSnapFlingBehavior(
+ lazyGridState: LazyGridState,
+ snapPosition: SnapPosition = SnapPosition.Center
+): FlingBehavior {
+ val snappingLayout =
+ remember(lazyGridState) { SnapLayoutInfoProvider(lazyGridState, snapPosition) }
+ return rememberSnapFlingBehavior(snappingLayout)
+}
+
internal val LazyGridLayoutInfo.singleAxisViewportSize: Int
get() = if (orientation == Orientation.Vertical) {
viewportSize.height
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
index 73fc7ad..bd6dd3b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
@@ -25,6 +25,7 @@
import androidx.compose.ui.unit.Density
import androidx.compose.ui.util.fastForEach
import kotlin.math.absoluteValue
+import kotlin.math.sign
/**
* A [SnapLayoutInfoProvider] for LazyLists.
@@ -34,7 +35,7 @@
* This position should be considered with regard to the start edge of the item and the placement
* within the viewport.
*
- * @return A [SnapLayoutInfoProvider] that can be used with [SnapFlingBehavior]
+ * @return A [SnapLayoutInfoProvider] that can be used with [snapFlingBehavior]
*/
fun SnapLayoutInfoProvider(
lazyListState: LazyListState,
@@ -44,7 +45,25 @@
private val layoutInfo: LazyListLayoutInfo
get() = lazyListState.layoutInfo
- override fun calculateSnappingOffset(currentVelocity: Float): Float {
+ private val averageItemSize: Int
+ get() {
+ val layoutInfo = layoutInfo
+ return if (layoutInfo.visibleItemsInfo.isEmpty()) {
+ 0
+ } else {
+ val numberOfItems = layoutInfo.visibleItemsInfo.size
+ layoutInfo.visibleItemsInfo.sumOf {
+ it.size
+ } / numberOfItems
+ }
+ }
+
+ override fun calculateApproachOffset(velocity: Float, decayOffset: Float): Float {
+ return (decayOffset.absoluteValue - averageItemSize)
+ .coerceAtLeast(0.0f) * decayOffset.sign
+ }
+
+ override fun calculateSnapOffset(velocity: Float): Float {
var lowerBoundOffset = Float.NEGATIVE_INFINITY
var upperBoundOffset = Float.POSITIVE_INFINITY
@@ -73,7 +92,7 @@
}
return calculateFinalOffset(
- with(lazyListState.density) { calculateFinalSnappingItem(currentVelocity) },
+ with(lazyListState.density) { calculateFinalSnappingItem(velocity) },
lowerBoundOffset,
upperBoundOffset
)
@@ -82,14 +101,21 @@
/**
* Create and remember a FlingBehavior for decayed snapping in Lazy Lists. This will snap
- * the item's center to the center of the viewport.
+ * the item according to [snapPosition].
*
* @param lazyListState The [LazyListState] from the LazyList where this [FlingBehavior] will
* be used.
+ * @param snapPosition The desired positioning of the snapped item within the main layout.
+ * This position should be considered with regards to the start edge of the item and the placement
+ * within the viewport.
*/
@Composable
-fun rememberSnapFlingBehavior(lazyListState: LazyListState): FlingBehavior {
- val snappingLayout = remember(lazyListState) { SnapLayoutInfoProvider(lazyListState) }
+fun rememberSnapFlingBehavior(
+ lazyListState: LazyListState,
+ snapPosition: SnapPosition = SnapPosition.Center
+): FlingBehavior {
+ val snappingLayout =
+ remember(lazyListState) { SnapLayoutInfoProvider(lazyListState, snapPosition) }
return rememberSnapFlingBehavior(snappingLayout)
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/PagerSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/PagerSnapLayoutInfoProvider.kt
index 4fa1ead..a788266 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/PagerSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/PagerSnapLayoutInfoProvider.kt
@@ -16,8 +16,6 @@
package androidx.compose.foundation.gestures.snapping
-import androidx.compose.animation.core.DecayAnimationSpec
-import androidx.compose.animation.core.calculateTargetValue
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.pager.PagerDebugConfig
@@ -31,11 +29,9 @@
import kotlin.math.absoluteValue
import kotlin.math.sign
-@OptIn(ExperimentalFoundationApi::class)
internal fun SnapLayoutInfoProvider(
pagerState: PagerState,
pagerSnapDistance: PagerSnapDistance,
- decayAnimationSpec: DecayAnimationSpec<Float>,
calculateFinalSnappingBound: (Float, Float, Float) -> Float
): SnapLayoutInfoProvider {
return object : SnapLayoutInfoProvider {
@@ -46,13 +42,13 @@
return this != Float.POSITIVE_INFINITY && this != Float.NEGATIVE_INFINITY
}
- override fun calculateSnappingOffset(currentVelocity: Float): Float {
+ override fun calculateSnapOffset(velocity: Float): Float {
val snapPosition = pagerState.layoutInfo.snapPosition
val (lowerBoundOffset, upperBoundOffset) = searchForSnappingBounds(snapPosition)
val finalDistance =
calculateFinalSnappingBound(
- currentVelocity,
+ velocity,
lowerBoundOffset,
upperBoundOffset
)
@@ -74,15 +70,17 @@
}
}
- override fun calculateApproachOffset(initialVelocity: Float): Float {
- debugLog { "Approach Velocity=$initialVelocity" }
+ override fun calculateApproachOffset(
+ velocity: Float,
+ decayOffset: Float
+ ): Float {
+ debugLog { "Approach Velocity=$velocity" }
val effectivePageSizePx = pagerState.pageSize + pagerState.pageSpacing
// given this velocity, where can I go with a decay animation.
- val animationOffsetPx =
- decayAnimationSpec.calculateTargetValue(0f, initialVelocity)
+ val animationOffsetPx = decayOffset
- val startPage = if (initialVelocity < 0) {
+ val startPage = if (velocity < 0) {
pagerState.firstVisiblePage + 1
} else {
pagerState.firstVisiblePage
@@ -109,7 +107,7 @@
val correctedTargetPage = pagerSnapDistance.calculateTargetPage(
startPage,
targetPage,
- initialVelocity,
+ velocity,
pagerState.pageSize,
pagerState.pageSpacing
).coerceIn(0, pagerState.pageCount)
@@ -131,7 +129,7 @@
return if (flingApproachOffsetPx == 0) {
flingApproachOffsetPx.toFloat()
} else {
- flingApproachOffsetPx * initialVelocity.sign
+ flingApproachOffsetPx * velocity.sign
}.also {
debugLog { "Fling Approach Offset=$it" }
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
index ab8ddfc..d4134b6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
@@ -29,6 +29,7 @@
import androidx.compose.animation.core.copy
import androidx.compose.animation.core.spring
import androidx.compose.animation.rememberSplineBasedDecay
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.DefaultScrollMotionDurationScale
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollScope
@@ -43,13 +44,13 @@
import kotlinx.coroutines.withContext
/**
- * A [TargetedFlingBehavior] that performs snapping of items to a given position.
+ * A [TargetedFlingBehavior] that performs snapping to a given position in a layout.
*
* You can use [SnapLayoutInfoProvider.calculateApproachOffset] to
* indicate that snapping should happen after this offset. If the velocity generated by the
* fling is high enough to get there, we'll use [decayAnimationSpec] to get to that offset
* and then we'll snap to the next bound calculated by
- * [SnapLayoutInfoProvider.calculateSnappingOffset] using [snapAnimationSpec].
+ * [SnapLayoutInfoProvider.calculateSnapOffset] using [snapAnimationSpec].
*
* If the velocity is not high enough, we'll use [snapAnimationSpec] to approach and the same spec
* to snap into place.
@@ -59,11 +60,27 @@
* @sample androidx.compose.foundation.samples.SnapFlingBehaviorCustomizedSample
*
* @param snapLayoutInfoProvider The information about the layout being snapped.
- * @param decayAnimationSpec The animation spec used to approach the target offset. When
+ * @param decayAnimationSpec The animation spec used to approach the target offset when
* the fling velocity is large enough. Large enough means large enough to naturally decay.
* @param snapAnimationSpec The animation spec used to finally snap to the correct bound.
*
*/
+@OptIn(ExperimentalFoundationApi::class)
+@Suppress("Deprecation")
+fun snapFlingBehavior(
+ snapLayoutInfoProvider: SnapLayoutInfoProvider,
+ decayAnimationSpec: DecayAnimationSpec<Float>,
+ snapAnimationSpec: AnimationSpec<Float>
+): TargetedFlingBehavior {
+ return SnapFlingBehavior(snapLayoutInfoProvider, decayAnimationSpec, snapAnimationSpec)
+}
+
+@Deprecated(
+ "Please use the snapFlingBehavior function",
+ replaceWith =
+ ReplaceWith("androidx.compose.foundation.gestures.snapping.snapFlingBehavior")
+)
+@ExperimentalFoundationApi
class SnapFlingBehavior(
private val snapLayoutInfoProvider: SnapLayoutInfoProvider,
private val decayAnimationSpec: DecayAnimationSpec<Float>,
@@ -99,22 +116,21 @@
return if (remainingOffset == 0f) NoVelocity else remainingState.velocity
}
- private fun Float.isValidBound() =
- this != Float.NEGATIVE_INFINITY && this != Float.POSITIVE_INFINITY
-
private suspend fun ScrollScope.fling(
initialVelocity: Float,
onRemainingScrollOffsetUpdate: (Float) -> Unit
): AnimationResult<Float, AnimationVector1D> {
val result = withContext(motionScaleDuration) {
- val initialOffset = snapLayoutInfoProvider.calculateApproachOffset(initialVelocity)
+ val decayOffset =
+ decayAnimationSpec.calculateTargetValue(
+ initialVelocity = initialVelocity,
+ initialValue = 0.0f
+ )
- val finalApproachOffset = resolveFinalApproachOffset(
- initialOffset = initialOffset,
- initialVelocity = initialVelocity
- )
-
- var remainingScrollOffset = finalApproachOffset
+ val initialOffset =
+ snapLayoutInfoProvider.calculateApproachOffset(initialVelocity, decayOffset)
+ var remainingScrollOffset =
+ abs(initialOffset) * sign(initialVelocity) // ensure offset sign is correct
onRemainingScrollOffsetUpdate(remainingScrollOffset) // First Scroll Offset
@@ -127,7 +143,7 @@
}
remainingScrollOffset =
- snapLayoutInfoProvider.calculateSnappingOffset(animationState.velocity)
+ snapLayoutInfoProvider.calculateSnapOffset(animationState.velocity)
debugLog { "Settling Final Bound=$remainingScrollOffset" }
@@ -146,49 +162,6 @@
return result
}
- private fun resolveFinalApproachOffset(initialOffset: Float, initialVelocity: Float): Float {
- return if (initialOffset.isNaN()) {
- debugLog { "Approach Offset Was not Provided" }
- // approach is unspecified, should decay by default
- // acquire the bounds for snapping before/after the current position.
- val nextBound =
- snapLayoutInfoProvider.calculateSnappingOffset(Float.POSITIVE_INFINITY)
- val previousBound =
- snapLayoutInfoProvider.calculateSnappingOffset(Float.NEGATIVE_INFINITY)
-
- // use the bounds to estimate the distance between any two bounds.
- val boundsDistance = if (nextBound.isValidBound() && previousBound.isValidBound()) {
- (nextBound - previousBound)
- } else if (nextBound.isValidBound()) {
- nextBound
- } else if (previousBound.isValidBound()) {
- previousBound
- } else {
- 0.0f
- }
-
- debugLog {
- "NextBound: $nextBound " +
- "PreviousBound=$previousBound " +
- "BoundsDistance=$boundsDistance"
- }
-
- // decay, but leave enough distance to snap.
- val decayOffset = decayAnimationSpec.calculateTargetValue(0.0f, initialVelocity)
- val resultingOffset =
- (decayOffset.absoluteValue - boundsDistance).coerceAtLeast(0.0f)
-
- if (resultingOffset == 0.0f) {
- resultingOffset
- } else {
- resultingOffset * initialVelocity.sign
- }
- } else {
- debugLog { "Approach Offset Was Provided" }
- abs(initialOffset) * sign(initialVelocity) // ensure offset sign is correct
- }
- }
-
private suspend fun ScrollScope.tryApproach(
offset: Float,
velocity: Float,
@@ -246,6 +219,7 @@
return decayOffset.absoluteValue >= offset.absoluteValue
}
+ @Suppress("Deprecation")
override fun equals(other: Any?): Boolean {
return if (other is SnapFlingBehavior) {
other.snapAnimationSpec == this.snapAnimationSpec &&
@@ -277,7 +251,7 @@
highVelocityApproachSpec,
density
) {
- SnapFlingBehavior(
+ snapFlingBehavior(
snapLayoutInfoProvider = snapLayoutInfoProvider,
decayAnimationSpec = highVelocityApproachSpec,
snapAnimationSpec = spring(stiffness = Spring.StiffnessMediumLow)
@@ -290,7 +264,7 @@
*
* In the initial animation we animate up until targetOffset. At this point we will have fulfilled
* the requirement of [SnapLayoutInfoProvider.calculateApproachOffset] and we should snap to the
- * next [SnapLayoutInfoProvider.calculateSnappingOffset].
+ * next [SnapLayoutInfoProvider.calculateSnapOffset].
*
* The second part of the approach is a UX improvement. If the target offset is too far (in here, we
* define too far as over half a step offset away) we continue the approach animation a bit further
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt
index 4285b0f..1e43f0e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt
@@ -17,38 +17,49 @@
package androidx.compose.foundation.gestures.snapping
/**
- * Provides information about the layout that is using a SnapFlingBehavior.
+ * Provides information about the layout that is using a [snapFlingBehavior].
* The provider should give the following information:
- * 1) Snapping offset: The next snap position offset.
- * 2) Approach offset: An optional offset to be consumed before snapping to a defined bound.
+ * 1) Snapping offset: The next snap position offset. This needs to be provided by the layout
+ * where the snapping is happened and will represent the final settling position of this layout.
+ * 2) Approach offset: An offset to be consumed before snapping to a defined bound. If not
+ * overridden this will provide a decayed snapping behavior.
*
* In snapping, the approach offset and the snapping offset can be used to control how a snapping
- * animation will look in a given SnappingLayout. The complete snapping animation can be split
- * into 2 phases: Approach and Snapping. In the Approach phase, we'll use an animation to consume
- * all of the offset provided by [calculateApproachOffset], if [Float.NaN] is provided,
- * we'll naturally decay if possible. In the snapping phase, [SnapFlingBehavior] will use an
- * animation to consume all of the offset provided by [calculateSnappingOffset].
+ * animation will look in a given layout. The complete snapping animation can be split
+ * into 2 phases: approach and snapping.
+ *
+ * Approach: animate to the offset returned by [calculateApproachOffset]. This will use a decay
+ * animation if possible, otherwise the snap animation.
+ * Snapping: once the approach offset is reached, snap to the offset returned by
+ * [calculateSnapOffset] using the snap animation.
*/
interface SnapLayoutInfoProvider {
/**
- * Calculate the distance to navigate before settling into the next snapping bound. If
- * Float.NaN (the default value) is returned and the velocity is high enough to decay,
- * [SnapFlingBehavior] will decay before snapping. If zero is specified, that means there won't
- * be an approach phase and there will only be snapping.
+ * Calculate the distance to navigate before settling into the next snapping bound. By default
+ * this is [decayOffset] a suggested offset given by [snapFlingBehavior] to indicate
+ * where the animation would naturally decay if using [velocity]. Returning a value higher than
+ * [decayOffset] is valid and will force [snapFlingBehavior] to use a target based animation
+ * spec to run the approach phase since we won't be able to naturally decay to the proposed
+ * offset. If a value smaller than or equal to [decayOffset] is returned [snapFlingBehavior]
+ * will run a decay animation until it reaches the returned value. If zero is specified,
+ * that means there won't be an approach phase and there will only be snapping.
*
- * @param initialVelocity The current fling movement velocity. You can use this to calculate a
+ * @param velocity The current fling movement velocity. You can use this to calculate a
* velocity based offset.
+ * @param decayOffset A suggested offset indicating where the animation would
+ * naturally decay to.
*/
- fun calculateApproachOffset(initialVelocity: Float): Float = Float.NaN
+ fun calculateApproachOffset(velocity: Float, decayOffset: Float): Float =
+ decayOffset
/**
* Given a target placement in a layout, the snapping offset is the next snapping position
* this layout can be placed in. The target placement should be in the direction of
- * [currentVelocity].
+ * [velocity].
*
- * @param currentVelocity The current fling movement velocity. This may change throughout the
+ * @param velocity The current fling movement velocity. This may change throughout the
* fling animation.
*/
- fun calculateSnappingOffset(currentVelocity: Float): Float
+ fun calculateSnapOffset(velocity: Float): Float
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt
index 20a8d5f..556bcb0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListAnimateScrollScope.kt
@@ -42,10 +42,11 @@
}
override fun calculateDistanceTo(targetIndex: Int): Float {
- val visibleItem =
- state.layoutInfo.visibleItemsInfo.fastFirstOrNull { it.index == targetIndex }
+ val layoutInfo = state.layoutInfo
+ if (layoutInfo.visibleItemsInfo.isEmpty()) return 0f
+ val visibleItem = layoutInfo.visibleItemsInfo.fastFirstOrNull { it.index == targetIndex }
return if (visibleItem == null) {
- val averageSize = visibleItemsAverageSize
+ val averageSize = calculateVisibleItemsAverageSize(layoutInfo)
val indexesDiff = targetIndex - firstVisibleItemIndex
(averageSize * indexesDiff).toFloat() - firstVisibleItemScrollOffset
} else {
@@ -57,11 +58,9 @@
state.scroll(block = block)
}
- private val visibleItemsAverageSize: Int
- get() {
- val layoutInfo = state.layoutInfo
- val visibleItems = layoutInfo.visibleItemsInfo
- val itemsSum = visibleItems.fastSumBy { it.size }
- return itemsSum / visibleItems.size + layoutInfo.mainAxisItemSpacing
- }
+ private fun calculateVisibleItemsAverageSize(layoutInfo: LazyListLayoutInfo): Int {
+ val visibleItems = layoutInfo.visibleItemsInfo
+ val itemsSum = visibleItems.fastSumBy { it.size }
+ return itemsSum / visibleItems.size + layoutInfo.mainAxisItemSpacing
+ }
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
index 30babe0..5dcee03 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
@@ -81,6 +81,8 @@
consumedScroll = 0,
layoutWidth = layoutWidth,
layoutHeight = layoutHeight,
+ beforeContentPadding = beforeContentPadding,
+ afterContentPadding = afterContentPadding,
positionedItems = mutableListOf(),
keyIndexMap = measuredItemProvider.keyIndexMap,
itemProvider = measuredItemProvider,
@@ -352,6 +354,8 @@
consumedScroll = consumedScroll.toInt(),
layoutWidth = layoutWidth,
layoutHeight = layoutHeight,
+ beforeContentPadding = beforeContentPadding,
+ afterContentPadding = afterContentPadding,
positionedItems = positionedItems,
keyIndexMap = measuredItemProvider.keyIndexMap,
itemProvider = measuredItemProvider,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListPrefetchStrategy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListPrefetchStrategy.kt
index dc29f8c..83840e9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListPrefetchStrategy.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListPrefetchStrategy.kt
@@ -147,21 +147,38 @@
} else {
layoutInfo.visibleItemsInfo.first().index - 1
}
- if (indexToPrefetch != [email protected] &&
- indexToPrefetch in 0 until layoutInfo.totalItemsCount
- ) {
- if (wasScrollingForward != scrollingForward) {
- // the scrolling direction has been changed which means the last prefetched
- // is not going to be reached anytime soon so it is safer to dispose it.
- // if this item is already visible it is safe to call the method anyway
- // as it will be no-op
- currentPrefetchHandle?.cancel()
+ if (indexToPrefetch in 0 until layoutInfo.totalItemsCount) {
+ if (indexToPrefetch != [email protected]) {
+ if (wasScrollingForward != scrollingForward) {
+ // the scrolling direction has been changed which means the last prefetched
+ // is not going to be reached anytime soon so it is safer to dispose it.
+ // if this item is already visible it is safe to call the method anyway
+ // as it will be no-op
+ currentPrefetchHandle?.cancel()
+ }
+ [email protected] = scrollingForward
+ [email protected] = indexToPrefetch
+ currentPrefetchHandle = schedulePrefetch(
+ indexToPrefetch
+ )
}
- [email protected] = scrollingForward
- [email protected] = indexToPrefetch
- currentPrefetchHandle = schedulePrefetch(
- indexToPrefetch
- )
+ if (scrollingForward) {
+ val lastItem = layoutInfo.visibleItemsInfo.last()
+ val spacing = layoutInfo.mainAxisItemSpacing
+ val distanceToPrefetchItem =
+ lastItem.offset + lastItem.size + spacing - layoutInfo.viewportEndOffset
+ // if in the next frame we will get the same delta will we reach the item?
+ if (distanceToPrefetchItem < -delta) {
+ currentPrefetchHandle?.markAsUrgent()
+ }
+ } else {
+ val firstItem = layoutInfo.visibleItemsInfo.first()
+ val distanceToPrefetchItem = layoutInfo.viewportStartOffset - firstItem.offset
+ // if in the next frame we will get the same delta will we reach the item?
+ if (distanceToPrefetchItem < delta) {
+ currentPrefetchHandle?.markAsUrgent()
+ }
+ }
}
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt
index 78e6ac9..5fa07f2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridAnimateScrollScope.kt
@@ -37,27 +37,25 @@
override val itemCount: Int get() = state.layoutInfo.totalItemsCount
- private val visibleItemsAverageSize: Int
- get() = calculateLineAverageMainAxisSize(state.layoutInfo)
-
override fun ScrollScope.snapToItem(index: Int, scrollOffset: Int) {
state.snapToItemIndexInternal(index, scrollOffset, forceRemeasure = true)
}
override fun calculateDistanceTo(targetIndex: Int): Float {
- val visibleItem =
- state.layoutInfo.visibleItemsInfo.fastFirstOrNull { it.index == targetIndex }
+ val layoutInfo = state.layoutInfo
+ if (layoutInfo.visibleItemsInfo.isEmpty()) return 0f
+ val visibleItem = layoutInfo.visibleItemsInfo.fastFirstOrNull { it.index == targetIndex }
return if (visibleItem == null) {
val slotsPerLine = state.slotsPerLine
- val averageLineMainAxisSize = visibleItemsAverageSize
+ val averageLineMainAxisSize = calculateLineAverageMainAxisSize(layoutInfo)
val before = targetIndex < firstVisibleItemIndex
val linesDiff =
(targetIndex - firstVisibleItemIndex + (slotsPerLine - 1) * if (before) -1 else 1) /
slotsPerLine
(averageLineMainAxisSize * linesDiff).toFloat() - firstVisibleItemScrollOffset
} else {
- if (state.layoutInfo.orientation == Orientation.Vertical) {
+ if (layoutInfo.orientation == Orientation.Vertical) {
visibleItem.offset.y
} else {
visibleItem.offset.x
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
index 8953e39..34f7ca9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
@@ -81,6 +81,8 @@
consumedScroll = 0,
layoutWidth = layoutWidth,
layoutHeight = layoutHeight,
+ beforeContentPadding = beforeContentPadding,
+ afterContentPadding = afterContentPadding,
positionedItems = mutableListOf(),
keyIndexMap = measuredItemProvider.keyIndexMap,
itemProvider = measuredItemProvider,
@@ -319,6 +321,8 @@
consumedScroll = consumedScroll.toInt(),
layoutWidth = layoutWidth,
layoutHeight = layoutHeight,
+ beforeContentPadding = beforeContentPadding,
+ afterContentPadding = afterContentPadding,
positionedItems = positionedItems,
keyIndexMap = measuredItemProvider.keyIndexMap,
itemProvider = measuredItemProvider,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
index ab042b2..d12d075 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
@@ -22,6 +22,7 @@
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.gestures.snapping.offsetOnMainAxis
import androidx.compose.foundation.gestures.stopScroll
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -31,6 +32,7 @@
import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
import androidx.compose.foundation.lazy.layout.ObservableScopeInvalidator
+import androidx.compose.foundation.lazy.layout.PrefetchScheduler
import androidx.compose.foundation.lazy.layout.animateScrollToItem
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
@@ -81,19 +83,26 @@
* A state object that can be hoisted to control and observe scrolling.
*
* In most cases, this will be created via [rememberLazyGridState].
- *
- * @param firstVisibleItemIndex the initial value for [LazyGridState.firstVisibleItemIndex]
- * @param firstVisibleItemScrollOffset the initial value for
- * [LazyGridState.firstVisibleItemScrollOffset]
*/
@OptIn(ExperimentalFoundationApi::class)
@Stable
-class LazyGridState constructor(
+class LazyGridState internal constructor(
firstVisibleItemIndex: Int = 0,
- firstVisibleItemScrollOffset: Int = 0
+ firstVisibleItemScrollOffset: Int = 0,
+ prefetchScheduler: PrefetchScheduler?,
) : ScrollableState {
/**
+ * @param firstVisibleItemIndex the initial value for [LazyGridState.firstVisibleItemIndex]
+ * @param firstVisibleItemScrollOffset the initial value for
+ * [LazyGridState.firstVisibleItemScrollOffset]
+ */
+ constructor(
+ firstVisibleItemIndex: Int = 0,
+ firstVisibleItemScrollOffset: Int = 0
+ ) : this(firstVisibleItemIndex, firstVisibleItemScrollOffset, null)
+
+ /**
* The holder class for the current scroll position.
*/
private val scrollPosition =
@@ -413,23 +422,40 @@
}
closestNextItemToPrefetch = info.visibleItemsInfo.first().index - 1
}
- if (lineToPrefetch != this.lineToPrefetch &&
- closestNextItemToPrefetch in 0 until info.totalItemsCount
- ) {
- if (wasScrollingForward != scrollingForward) {
- // the scrolling direction has been changed which means the last prefetched
- // is not going to be reached anytime soon so it is safer to dispose it.
- // if this line is already visible it is safe to call the method anyway
- // as it will be no-op
- currentLinePrefetchHandles.forEach { it.cancel() }
+ if (closestNextItemToPrefetch in 0 until info.totalItemsCount) {
+ if (lineToPrefetch != this.lineToPrefetch) {
+ if (wasScrollingForward != scrollingForward) {
+ // the scrolling direction has been changed which means the last prefetched
+ // is not going to be reached anytime soon so it is safer to dispose it.
+ // if this line is already visible it is safe to call the method anyway
+ // as it will be no-op
+ currentLinePrefetchHandles.forEach { it.cancel() }
+ }
+ this.wasScrollingForward = scrollingForward
+ this.lineToPrefetch = lineToPrefetch
+ currentLinePrefetchHandles.clear()
+ info.prefetchInfoRetriever(lineToPrefetch).fastForEach {
+ currentLinePrefetchHandles.add(
+ prefetchState.schedulePrefetch(it.first, it.second)
+ )
+ }
}
- this.wasScrollingForward = scrollingForward
- this.lineToPrefetch = lineToPrefetch
- currentLinePrefetchHandles.clear()
- info.prefetchInfoRetriever(lineToPrefetch).fastForEach {
- currentLinePrefetchHandles.add(
- prefetchState.schedulePrefetch(it.first, it.second)
- )
+ if (scrollingForward) {
+ val lastItem = info.visibleItemsInfo.last()
+ val distanceToPrefetchItem = lastItem.offsetOnMainAxis(info.orientation) +
+ lastItem.mainAxisSizeWithSpacings - info.viewportEndOffset
+ // if in the next frame we will get the same delta will we reach the item?
+ if (distanceToPrefetchItem < -delta) {
+ currentLinePrefetchHandles.forEach { it.markAsUrgent() }
+ }
+ } else {
+ val firstItem = info.visibleItemsInfo.first()
+ val distanceToPrefetchItem = info.viewportStartOffset -
+ firstItem.offsetOnMainAxis(info.orientation)
+ // if in the next frame we will get the same delta will we reach the item?
+ if (distanceToPrefetchItem < delta) {
+ currentLinePrefetchHandles.forEach { it.markAsUrgent() }
+ }
}
}
}
@@ -454,7 +480,7 @@
}
}
- internal val prefetchState = LazyLayoutPrefetchState()
+ internal val prefetchState = LazyLayoutPrefetchState(prefetchScheduler)
private val numOfItemsToTeleport: Int get() = 100 * slotsPerLine
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemAnimator.kt
index f1adbb1..0d1bb80 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemAnimator.kt
@@ -71,6 +71,8 @@
consumedScroll: Int,
layoutWidth: Int,
layoutHeight: Int,
+ beforeContentPadding: Int,
+ afterContentPadding: Int,
positionedItems: MutableList<T>,
keyIndexMap: LazyLayoutKeyIndexMap,
itemProvider: LazyLayoutMeasuredItemProvider<T>,
@@ -186,8 +188,10 @@
movingInFromEndBound.sortBy { previousKeyToIndexMap.getIndex(it.key) }
movingInFromEndBound.fastForEach { item ->
val accumulatedOffset = accumulatedOffsetPerLane.updateAndReturnOffsetFor(item)
- val mainAxisOffset =
- mainAxisLayoutSize + accumulatedOffset - item.mainAxisSizeWithSpacings
+ // Compensate content padding
+ val contentPadding = beforeContentPadding + afterContentPadding
+ val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset -
+ item.mainAxisSizeWithSpacings + contentPadding
initializeAnimation(item, mainAxisOffset)
startPlacementAnimationsIfNeeded(item)
}
@@ -237,6 +241,7 @@
lane = info.lane,
span = info.span
)
+
item.nonScrollableItem = true
// check if we have any active placement animation on the item
val inProgress =
@@ -244,6 +249,14 @@
if ((!inProgress && newIndex == previousKeyToIndexMap?.getIndex(key))) {
removeInfoForKey(key)
} else {
+ // anytime we compose a new item, and we use it,
+ // we need to update our item info mapping
+ info.updateAnimation(
+ item,
+ coroutineScope,
+ graphicsContext,
+ crossAxisOffset = info.crossAxisOffset
+ )
if (newIndex < firstVisibleIndex) {
movingAwayToStartBound.add(item)
} else {
@@ -285,7 +298,8 @@
positionedItems.last()
.let { it.mainAxisOffset }
else {
- mainAxisLayoutSize - item.mainAxisSizeWithSpacings
+ val contentPadding = beforeContentPadding + afterContentPadding
+ mainAxisLayoutSize - item.mainAxisSizeWithSpacings + contentPadding
} + accumulatedOffset
val itemInfo = keyToItemInfoMap[item.key]!!
@@ -390,19 +404,20 @@
return maxOffset
}
- val minSizeToFitDisappearingItems: IntSize get() {
- var size = IntSize.Zero
- disappearingItems.fastForEach {
- val layer = it.layer
- if (layer != null) {
- size = IntSize(
- width = maxOf(size.width, it.rawOffset.x + layer.size.width),
- height = maxOf(size.height, it.rawOffset.y + layer.size.height)
- )
+ val minSizeToFitDisappearingItems: IntSize
+ get() {
+ var size = IntSize.Zero
+ disappearingItems.fastForEach {
+ val layer = it.layer
+ if (layer != null) {
+ size = IntSize(
+ width = maxOf(size.width, it.rawOffset.x + layer.size.width),
+ height = maxOf(size.height, it.rawOffset.y + layer.size.height)
+ )
+ }
}
+ return size
}
- return size
- }
val modifier: Modifier = DisplayingDisappearingItemsElement(this)
@@ -438,7 +453,8 @@
fun updateAnimation(
positionedItem: T,
coroutineScope: CoroutineScope,
- graphicsContext: GraphicsContext
+ graphicsContext: GraphicsContext,
+ crossAxisOffset: Int = positionedItem.crossAxisOffset
) {
for (i in positionedItem.placeablesCount until animations.size) {
animations[i]?.release()
@@ -447,7 +463,7 @@
animations = animations.copyOf(positionedItem.placeablesCount)
}
constraints = positionedItem.constraints
- crossAxisOffset = positionedItem.crossAxisOffset
+ this.crossAxisOffset = crossAxisOffset
lane = positionedItem.lane
span = positionedItem.span
repeat(positionedItem.placeablesCount) { index ->
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt
index 11c2a13..20d6ca5 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt
@@ -79,6 +79,15 @@
* was precomposed already it will be disposed.
*/
fun cancel()
+
+ /**
+ * Marks this prefetch request as urgent, which is a way to communicate that the requested
+ * item is expected to be needed during the next frame.
+ *
+ * For urgent requests we can proceed with doing the prefetch even if the available time
+ * in the frame is less than we spend on similar prefetch requests on average.
+ */
+ fun markAsUrgent()
}
private inner class NestedPrefetchScopeImpl : NestedPrefetchScope {
@@ -169,6 +178,7 @@
@ExperimentalFoundationApi
private object DummyHandle : PrefetchHandle {
override fun cancel() {}
+ override fun markAsUrgent() {}
}
/**
@@ -212,6 +222,7 @@
private val isComposed get() = precomposeHandle != null
private var hasResolvedNestedPrefetches = false
private var nestedPrefetchController: NestedPrefetchController? = null
+ private var isUrgent = false
private val isValid
get() = !isCanceled &&
@@ -225,13 +236,24 @@
}
}
+ override fun markAsUrgent() {
+ isUrgent = true
+ }
+
+ private fun PrefetchRequestScope.shouldExecute(average: Long): Boolean {
+ val available = availableTimeNanos()
+ // even for urgent request we only do the work if we have time available, as otherwise
+ // it is better to just return early to allow the next frame to start and do the work.
+ return (isUrgent && available > 0) || average < available
+ }
+
override fun PrefetchRequestScope.execute(): Boolean {
if (!isValid) {
return false
}
if (!isComposed) {
- if (prefetchMetrics.averageCompositionTimeNanos < availableTimeNanos) {
+ if (shouldExecute(prefetchMetrics.averageCompositionTimeNanos)) {
prefetchMetrics.recordCompositionTiming {
trace("compose:lazy:prefetch:compose") {
performComposition()
@@ -242,27 +264,35 @@
}
}
- // Nested prefetch logic is best-effort: if nested LazyLayout children are
- // added/removed/updated after we've resolved nested prefetch states here or resolved
- // nestedPrefetchRequests below, those changes won't be taken into account.
- if (!hasResolvedNestedPrefetches) {
- if (availableTimeNanos > 0) {
- trace("compose:lazy:prefetch:resolve-nested") {
- nestedPrefetchController = resolveNestedPrefetchStates()
- hasResolvedNestedPrefetches = true
+ // if the request is urgent we better proceed with the measuring straight away instead
+ // of spending time trying to split the work more via nested prefetch. nested prefetch
+ // is always an estimation and it could potentially do work we will not need in the end,
+ // but the measuring will only do exactly the needed work (including composing nested
+ // lazy layouts)
+ if (!isUrgent) {
+ // Nested prefetch logic is best-effort: if nested LazyLayout children are
+ // added/removed/updated after we've resolved nested prefetch states here or resolved
+ // nestedPrefetchRequests below, those changes won't be taken into account.
+ if (!hasResolvedNestedPrefetches) {
+ if (availableTimeNanos() > 0) {
+ trace("compose:lazy:prefetch:resolve-nested") {
+ nestedPrefetchController = resolveNestedPrefetchStates()
+ hasResolvedNestedPrefetches = true
+ }
+ } else {
+ return true
}
- } else {
+ }
+
+ val hasMoreWork =
+ nestedPrefetchController?.run { executeNestedPrefetches() } ?: false
+ if (hasMoreWork) {
return true
}
}
- val hasMoreWork = nestedPrefetchController?.run { executeNestedPrefetches() } ?: false
- if (hasMoreWork) {
- return true
- }
-
if (!isMeasured && constraints != null) {
- if (prefetchMetrics.averageMeasureTimeNanos < availableTimeNanos) {
+ if (shouldExecute(prefetchMetrics.averageMeasureTimeNanos)) {
prefetchMetrics.recordMeasureTiming {
trace("compose:lazy:prefetch:measure") {
performMeasure(constraints)
@@ -349,7 +379,7 @@
trace("compose:lazy:prefetch:nested") {
while (stateIndex < states.size) {
if (requestsByState[stateIndex] == null) {
- if (availableTimeNanos <= 0) {
+ if (availableTimeNanos() <= 0) {
// When we have time again, we'll resolve nested requests for this
// state
return true
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.kt
index d3497f8..131eb4f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.kt
@@ -75,5 +75,5 @@
* How much time is available to do prefetch work. Implementations of [PrefetchRequest] should
* do their best to fit their work into this time without going over.
*/
- val availableTimeNanos: Long
+ fun availableTimeNanos(): Long
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt
index 9d9853c..3b8621c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridAnimateScrollScope.kt
@@ -44,16 +44,17 @@
}
override fun calculateDistanceTo(targetIndex: Int): Float {
- val visibleItem =
- state.layoutInfo.visibleItemsInfo.fastFirstOrNull { it.index == targetIndex }
+ val layoutInfo = state.layoutInfo
+ if (layoutInfo.visibleItemsInfo.isEmpty()) return 0f
+ val visibleItem = layoutInfo.visibleItemsInfo.fastFirstOrNull { it.index == targetIndex }
return if (visibleItem == null) {
- val averageMainAxisItemSize = visibleItemsAverageSize
+ val averageMainAxisItemSize = calculateVisibleItemsAverageSize(layoutInfo)
val laneCount = state.laneCount
val lineDiff = targetIndex / laneCount - firstVisibleItemIndex / laneCount
averageMainAxisItemSize * lineDiff.toFloat() - firstVisibleItemScrollOffset
} else {
- if (state.layoutInfo.orientation == Orientation.Vertical) {
+ if (layoutInfo.orientation == Orientation.Vertical) {
visibleItem.offset.y
} else {
visibleItem.offset.x
@@ -65,17 +66,15 @@
state.scroll(block = block)
}
- private val visibleItemsAverageSize: Int
- get() {
- val layoutInfo = state.layoutInfo
- val visibleItems = layoutInfo.visibleItemsInfo
- val itemSizeSum = visibleItems.fastSumBy {
- if (layoutInfo.orientation == Orientation.Vertical) {
- it.size.height
- } else {
- it.size.width
- }
+ private fun calculateVisibleItemsAverageSize(layoutInfo: LazyStaggeredGridLayoutInfo): Int {
+ val visibleItems = layoutInfo.visibleItemsInfo
+ val itemSizeSum = visibleItems.fastSumBy {
+ if (layoutInfo.orientation == Orientation.Vertical) {
+ it.size.height
+ } else {
+ it.size.width
}
- return itemSizeSum / visibleItems.size + layoutInfo.mainAxisItemSpacing
}
+ return itemSizeSum / visibleItems.size + layoutInfo.mainAxisItemSpacing
+ }
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
index eeb9286..5928a6e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
@@ -263,6 +263,8 @@
consumedScroll = 0,
layoutWidth = layoutWidth,
layoutHeight = layoutHeight,
+ beforeContentPadding = beforeContentPadding,
+ afterContentPadding = afterContentPadding,
positionedItems = mutableListOf(),
keyIndexMap = measuredItemProvider.keyIndexMap,
itemProvider = measuredItemProvider,
@@ -870,6 +872,8 @@
consumedScroll = consumedScroll.toInt(),
layoutWidth = layoutWidth,
layoutHeight = layoutHeight,
+ beforeContentPadding = beforeContentPadding,
+ afterContentPadding = afterContentPadding,
positionedItems = positionedItems,
keyIndexMap = measuredItemProvider.keyIndexMap,
itemProvider = measuredItemProvider,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
index 09cb286..3a15ecb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
@@ -34,6 +34,7 @@
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle
import androidx.compose.foundation.lazy.layout.ObservableScopeInvalidator
+import androidx.compose.foundation.lazy.layout.PrefetchScheduler
import androidx.compose.foundation.lazy.layout.animateScrollToItem
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridLaneInfo.Companion.FullSpan
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridLaneInfo.Companion.Unset
@@ -82,9 +83,10 @@
* In most cases, it should be created via [rememberLazyStaggeredGridState].
*/
@OptIn(ExperimentalFoundationApi::class)
-class LazyStaggeredGridState private constructor(
+class LazyStaggeredGridState internal constructor(
initialFirstVisibleItems: IntArray,
initialFirstVisibleOffsets: IntArray,
+ prefetchScheduler: PrefetchScheduler?
) : ScrollableState {
/**
* @param initialFirstVisibleItemIndex initial value for [firstVisibleItemIndex]
@@ -95,7 +97,8 @@
initialFirstVisibleItemOffset: Int = 0
) : this(
intArrayOf(initialFirstVisibleItemIndex),
- intArrayOf(initialFirstVisibleItemOffset)
+ intArrayOf(initialFirstVisibleItemOffset),
+ null
)
/**
@@ -178,7 +181,7 @@
internal var prefetchingEnabled: Boolean = true
/** prefetch state used for precomputing items in the direction of scroll */
- internal val prefetchState: LazyLayoutPrefetchState = LazyLayoutPrefetchState()
+ internal val prefetchState: LazyLayoutPrefetchState = LazyLayoutPrefetchState(prefetchScheduler)
/** state controlling the scroll */
private val scrollableState = ScrollableState { -onScroll(-it) }
@@ -584,7 +587,7 @@
)
},
restore = {
- LazyStaggeredGridState(it[0], it[1])
+ LazyStaggeredGridState(it[0], it[1], null)
}
)
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
index a01e002..4247cf7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
@@ -28,8 +28,8 @@
import androidx.compose.foundation.gestures.TargetedFlingBehavior
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
-import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
import androidx.compose.foundation.gestures.snapping.SnapPosition
+import androidx.compose.foundation.gestures.snapping.snapFlingBehavior
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.layout.IntervalList
import androidx.compose.foundation.lazy.layout.LazyLayout
@@ -365,7 +365,7 @@
}
/**
- * Wraps [SnapFlingBehavior] to give out information about target page coming from flings.
+ * Wraps [snapFlingBehavior] to give out information about target page coming from flings.
*/
private class PagerWrapperFlingBehavior(
val originalFlingBehavior: TargetedFlingBehavior,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
index 316e643..9036317 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
@@ -25,10 +25,10 @@
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.TargetedFlingBehavior
-import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
import androidx.compose.foundation.gestures.snapping.SnapPosition
import androidx.compose.foundation.gestures.snapping.calculateFinalSnappingBound
+import androidx.compose.foundation.gestures.snapping.snapFlingBehavior
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@@ -60,7 +60,7 @@
* use a snap animation (provided by [flingBehavior] to scroll pages into a specific position). You
* can use [beyondViewportPageCount] to place more pages before and after the visible pages.
*
- * If you need snapping with pages of different size, you can use a [SnapFlingBehavior] with a
+ * If you need snapping with pages of different size, you can use a [snapFlingBehavior] with a
* [SnapLayoutInfoProvider] adapted to a LazyList.
* @see androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider for the implementation
* of a [SnapLayoutInfoProvider] that uses [androidx.compose.foundation.lazy.LazyListState].
@@ -148,7 +148,7 @@
* use a snap animation (provided by [flingBehavior] to scroll pages into a specific position). You
* can use [beyondViewportPageCount] to place more pages before and after the visible pages.
*
- * If you need snapping with pages of different size, you can use a [SnapFlingBehavior] with a
+ * If you need snapping with pages of different size, you can use a [snapFlingBehavior] with a
* [SnapLayoutInfoProvider] adapted to a LazyList.
* @see androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider for the implementation
* of a [SnapLayoutInfoProvider] that uses [androidx.compose.foundation.lazy.LazyListState].
@@ -235,9 +235,9 @@
object PagerDefaults {
/**
- * A [SnapFlingBehavior] that will snap pages to the start of the layout. One can use the
+ * A [snapFlingBehavior] that will snap pages to the start of the layout. One can use the
* given parameters to control how the snapping animation will happen.
- * @see androidx.compose.foundation.gestures.snapping.SnapFlingBehavior for more information
+ * @see androidx.compose.foundation.gestures.snapping.snapFlingBehavior for more information
* on what which parameter controls in the overall snapping animation.
*
* The animation specs used by the fling behavior will depend on 2 factors:
@@ -313,8 +313,7 @@
val snapLayoutInfoProvider =
SnapLayoutInfoProvider(
state,
- pagerSnapDistance,
- decayAnimationSpec
+ pagerSnapDistance
) { flingVelocity, lowerBound, upperBound ->
calculateFinalSnappingBound(
pagerState = state,
@@ -326,7 +325,7 @@
)
}
- SnapFlingBehavior(
+ snapFlingBehavior(
snapLayoutInfoProvider = snapLayoutInfoProvider,
decayAnimationSpec = decayAnimationSpec,
snapAnimationSpec = snapAnimationSpec
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
index edf220c..a4310c5 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
@@ -36,6 +36,7 @@
import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
import androidx.compose.foundation.lazy.layout.ObservableScopeInvalidator
+import androidx.compose.foundation.lazy.layout.PrefetchScheduler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
@@ -147,18 +148,26 @@
/**
* The state that can be used to control [VerticalPager] and [HorizontalPager]
- * @param currentPage The initial page to be displayed
- * @param currentPageOffsetFraction The offset of the initial page with respect to the start of
- * the layout.
*/
@OptIn(ExperimentalFoundationApi::class)
@Stable
-abstract class PagerState(
+abstract class PagerState internal constructor(
currentPage: Int = 0,
- @FloatRange(from = -0.5, to = 0.5) currentPageOffsetFraction: Float = 0f
+ @FloatRange(from = -0.5, to = 0.5) currentPageOffsetFraction: Float = 0f,
+ prefetchScheduler: PrefetchScheduler? = null
) : ScrollableState {
/**
+ * @param currentPage The initial page to be displayed
+ * @param currentPageOffsetFraction The offset of the initial page with respect to the start of
+ * the layout.
+ */
+ constructor(
+ currentPage: Int = 0,
+ @FloatRange(from = -0.5, to = 0.5) currentPageOffsetFraction: Float = 0f
+ ) : this(currentPage, currentPageOffsetFraction, null)
+
+ /**
* The total amount of pages present in this pager. The source of this data should be
* observable.
*/
@@ -431,7 +440,7 @@
*/
val currentPageOffsetFraction: Float get() = scrollPosition.currentPageOffsetFraction
- internal val prefetchState = LazyLayoutPrefetchState()
+ internal val prefetchState = LazyLayoutPrefetchState(prefetchScheduler)
internal val beyondBoundsInfo = LazyLayoutBeyondBoundsInfo()
@@ -716,21 +725,38 @@
} else {
info.visiblePagesInfo.first().index - info.beyondViewportPageCount - PagesToPrefetch
}
- if (indexToPrefetch != this.indexToPrefetch &&
- indexToPrefetch in 0 until pageCount
- ) {
- if (wasPrefetchingForward != isPrefetchingForward) {
- // the scrolling direction has been changed which means the last prefetched
- // is not going to be reached anytime soon so it is safer to dispose it.
- // if this item is already visible it is safe to call the method anyway
- // as it will be no-op
- currentPrefetchHandle?.cancel()
+ if (indexToPrefetch in 0 until pageCount) {
+ if (indexToPrefetch != this.indexToPrefetch) {
+ if (wasPrefetchingForward != isPrefetchingForward) {
+ // the scrolling direction has been changed which means the last prefetched
+ // is not going to be reached anytime soon so it is safer to dispose it.
+ // if this item is already visible it is safe to call the method anyway
+ // as it will be no-op
+ currentPrefetchHandle?.cancel()
+ }
+ this.wasPrefetchingForward = isPrefetchingForward
+ this.indexToPrefetch = indexToPrefetch
+ currentPrefetchHandle = prefetchState.schedulePrefetch(
+ indexToPrefetch, premeasureConstraints
+ )
}
- this.wasPrefetchingForward = isPrefetchingForward
- this.indexToPrefetch = indexToPrefetch
- currentPrefetchHandle = prefetchState.schedulePrefetch(
- indexToPrefetch, premeasureConstraints
- )
+ if (isPrefetchingForward) {
+ val lastItem = info.visiblePagesInfo.last()
+ val pageSize = info.pageSize + info.pageSpacing
+ val distanceToReachNextItem =
+ lastItem.offset + pageSize - info.viewportEndOffset
+ // if in the next frame we will get the same delta will we reach the item?
+ if (distanceToReachNextItem < delta) {
+ currentPrefetchHandle?.markAsUrgent()
+ }
+ } else {
+ val firstItem = info.visiblePagesInfo.first()
+ val distanceToReachNextItem = info.viewportStartOffset - firstItem.offset
+ // if in the next frame we will get the same delta will we reach the item?
+ if (distanceToReachNextItem < -delta) {
+ currentPrefetchHandle?.markAsUrgent()
+ }
+ }
}
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
index 4fecf94..1b48576 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
@@ -21,7 +21,6 @@
import androidx.compose.foundation.text.modifiers.TextAnnotatedStringElement
import androidx.compose.foundation.text.modifiers.TextAnnotatedStringNode
import androidx.compose.foundation.text.modifiers.TextStringSimpleElement
-import androidx.compose.foundation.text.modifiers.fixedCoerceHeightAndWidthForBits
import androidx.compose.foundation.text.modifiers.hasLinks
import androidx.compose.foundation.text.selection.LocalSelectionRegistrar
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
@@ -53,6 +52,7 @@
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Constraints.Companion.fitPrioritizingWidth
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.util.fastFilter
import androidx.compose.ui.util.fastForEach
@@ -453,9 +453,11 @@
textRangeLayoutMeasureScope.measure()
}
val placeable = measurable.measure(
- Constraints.fixedCoerceHeightAndWidthForBits(
- rangeMeasureResult.width,
- rangeMeasureResult.height
+ fitPrioritizingWidth(
+ minWidth = rangeMeasureResult.width,
+ maxWidth = rangeMeasureResult.width,
+ minHeight = rangeMeasureResult.height,
+ maxHeight = rangeMeasureResult.height
)
)
Pair(placeable, rangeMeasureResult.place)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
index 397036b..00dd988 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
@@ -47,9 +47,6 @@
import androidx.compose.foundation.text.input.internal.TransformedTextFieldState
import androidx.compose.foundation.text.input.internal.selection.TextFieldSelectionState
import androidx.compose.foundation.text.selection.SelectionHandle
-import androidx.compose.foundation.text.selection.SelectionHandleAnchor
-import androidx.compose.foundation.text.selection.SelectionHandleInfo
-import androidx.compose.foundation.text.selection.SelectionHandleInfoKey
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.SideEffect
@@ -71,7 +68,6 @@
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalTextToolbar
import androidx.compose.ui.platform.LocalWindowInfo
-import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.ImeAction
@@ -329,11 +325,9 @@
.scrollable(
state = scrollState,
orientation = orientation,
- // Disable scrolling when textField is disabled, there is no where to scroll, and
- // another dragging gesture is taking place
- enabled = enabled &&
- scrollState.maxValue > 0 &&
- textFieldSelectionState.draggingHandle == null,
+ // Disable scrolling when textField is disabled or another dragging gesture is taking
+ // place
+ enabled = enabled && textFieldSelectionState.draggingHandle == null,
reverseDirection = ScrollableDefaults.reverseDirection(
layoutDirection = layoutDirection,
orientation = orientation,
@@ -416,19 +410,20 @@
@Composable
internal fun TextFieldCursorHandle(selectionState: TextFieldSelectionState) {
- val cursorHandleState = selectionState.cursorHandle
+ // Does not recompose if only position of the handle changes.
+ val cursorHandleState by remember {
+ derivedStateOf {
+ selectionState.getCursorHandleState(includePosition = false)
+ }
+ }
if (cursorHandleState.visible) {
CursorHandle(
- handlePosition = cursorHandleState.position,
+ offsetProvider = {
+ selectionState
+ .getCursorHandleState(includePosition = true)
+ .position
+ },
modifier = Modifier
- .semantics {
- this[SelectionHandleInfoKey] = SelectionHandleInfo(
- handle = Handle.Cursor,
- position = cursorHandleState.position,
- anchor = SelectionHandleAnchor.Middle,
- visible = true,
- )
- }
.pointerInput(selectionState) {
with(selectionState) { cursorHandleGestures() }
},
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index b4654ff..50eccc5 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -32,6 +32,7 @@
import androidx.compose.foundation.text.input.internal.createLegacyPlatformTextInputServiceAdapter
import androidx.compose.foundation.text.input.internal.legacyTextInputAdapter
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.foundation.text.selection.OffsetProvider
import androidx.compose.foundation.text.selection.SelectionHandleAnchor
import androidx.compose.foundation.text.selection.SelectionHandleInfo
import androidx.compose.foundation.text.selection.SelectionHandleInfoKey
@@ -62,7 +63,6 @@
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
@@ -119,6 +119,7 @@
import androidx.compose.ui.text.input.FinishComposingTextCommand
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.ImeOptions
+import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.TextFieldValue
@@ -199,7 +200,7 @@
* innerTextField exactly once.
*/
@Composable
-@OptIn(InternalFoundationTextApi::class, ExperimentalFoundationApi::class)
+@OptIn(ExperimentalFoundationApi::class)
internal fun CoreTextField(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
@@ -405,13 +406,18 @@
)
.pointerHoverIcon(textPointerIcon)
.then(
- if (isStylusHandwritingSupported) {
- Modifier.pointerInput(writeable) {
- if (writeable) {
- detectStylusHandwriting {
- if (!state.hasFocus) {
- focusRequester.requestFocus()
- }
+ if (isStylusHandwritingSupported && writeable) {
+ Modifier.pointerInput(Unit) {
+ detectStylusHandwriting {
+ if (!state.hasFocus) {
+ focusRequester.requestFocus()
+ }
+ // If this is a password field, we can't trigger handwriting.
+ // The expected behavior is 1) request focus 2) show software keyboard.
+ // Note: TextField will show software keyboard automatically when it
+ // gain focus. 3) show a toast message telling that handwriting is not
+ // supported for password fields. TODO(b/335294152)
+ if (imeOptions.keyboardType != KeyboardType.Password) {
// TextInputService is calling LegacyTextInputServiceAdapter under the
// hood. And because it's a public API, startStylusHandwriting is added
// to legacyTextInputServiceAdapter instead.
@@ -419,8 +425,8 @@
// session starts when the editor is not focused, this is handled
// internally by the LegacyTextInputServiceAdapter.
legacyTextInputServiceAdapter.startStylusHandwriting()
- true
}
+ true
}
}
} else {
@@ -434,9 +440,12 @@
TextFieldDelegate.draw(
canvas,
value,
+ state.selectionPreviewHighlightRange,
+ state.deletionPreviewHighlightRange,
offsetMapping,
layoutResult.value,
- state.selectionPaint
+ state.highlightPaint,
+ state.selectionBackgroundColor
)
}
}
@@ -614,7 +623,7 @@
}
}
- val showCursor = enabled && !readOnly && windowInfo.isWindowFocused
+ val showCursor = enabled && !readOnly && windowInfo.isWindowFocused && !state.hasHighlight()
val cursorModifier = Modifier.cursor(state, value, offsetMapping, cursorBrush, showCursor)
DisposableEffect(manager) {
@@ -842,7 +851,6 @@
}
}
-@OptIn(InternalFoundationTextApi::class)
internal class LegacyTextFieldState(
var textDelegate: TextDelegate,
val recomposeScope: RecomposeScope,
@@ -977,6 +985,8 @@
// Text has been changed, enter the HandleState.None and hide the cursor handle.
handleState = HandleState.None
}
+ selectionPreviewHighlightRange = TextRange.Zero
+ deletionPreviewHighlightRange = TextRange.Zero
onValueChangeOriginal(it)
recomposeScope.invalidate()
}
@@ -985,8 +995,16 @@
keyboardActionRunner.runAction(imeAction)
}
- /** The paint used to draw highlight background for selected text. */
- val selectionPaint: Paint = Paint()
+ /** The paint used to draw highlight backgrounds. */
+ val highlightPaint: Paint = Paint()
+ var selectionBackgroundColor = Color.Unspecified
+
+ /** Range of text to be highlighted to display handwriting gesture previews from the IME. */
+ var selectionPreviewHighlightRange: TextRange by mutableStateOf(TextRange.Zero)
+ var deletionPreviewHighlightRange: TextRange by mutableStateOf(TextRange.Zero)
+
+ fun hasHighlight() =
+ !selectionPreviewHighlightRange.collapsed || !deletionPreviewHighlightRange.collapsed
fun update(
untransformedText: AnnotatedString,
@@ -1001,7 +1019,7 @@
selectionBackgroundColor: Color
) {
this.onValueChangeOriginal = onValueChange
- this.selectionPaint.color = selectionBackgroundColor
+ this.selectionBackgroundColor = selectionBackgroundColor
this.keyboardActionRunner.apply {
this.keyboardActions = keyboardActions
this.focusManager = focusManager
@@ -1084,7 +1102,7 @@
* This function is used to handle 2, 3, and 4, and the others are automatically handled by the
* focus system.
*/
-@OptIn(ExperimentalFoundationApi::class, InternalFoundationTextApi::class)
+@OptIn(ExperimentalFoundationApi::class)
internal suspend fun BringIntoViewRequester.bringSelectionEndIntoView(
value: TextFieldValue,
textDelegate: TextDelegate,
@@ -1162,7 +1180,7 @@
val observer = remember(manager) { manager.cursorDragObserver() }
val position = manager.getCursorPosition(LocalDensity.current)
CursorHandle(
- handlePosition = position,
+ offsetProvider = { position },
modifier = Modifier
.pointerInput(observer) {
coroutineScope {
@@ -1190,13 +1208,12 @@
@Composable
internal expect fun CursorHandle(
- handlePosition: Offset,
+ offsetProvider: OffsetProvider,
modifier: Modifier,
minTouchTargetSize: DpSize = DpSize.Unspecified
)
// TODO(b/262648050) Try to find a better API.
-@OptIn(InternalFoundationTextApi::class)
private fun notifyFocusedRect(
state: LegacyTextFieldState,
value: TextFieldValue,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextDelegate.kt
index 7320768..ca3f5a8 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextDelegate.kt
@@ -199,7 +199,12 @@
return MultiParagraph(
intrinsics = nonNullIntrinsics,
- constraints = Constraints(maxWidth = width, maxHeight = constraints.maxHeight),
+ constraints = Constraints.fitPrioritizingWidth(
+ minWidth = 0,
+ maxWidth = width,
+ minHeight = 0,
+ maxHeight = constraints.maxHeight
+ ),
// This is a fallback behavior for ellipsis. Native
maxLines = finalMaxLines,
ellipsis = overflow == TextOverflow.Ellipsis
@@ -302,7 +307,6 @@
* Returns the [TextDelegate] passed as a [current] param if the input didn't change
* otherwise creates a new [TextDelegate].
*/
-@OptIn(InternalFoundationTextApi::class)
internal fun updateTextDelegate(
current: TextDelegate,
text: AnnotatedString,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
index baba89e..eabeb8e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
@@ -23,7 +23,9 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
+import androidx.compose.ui.graphics.isUnspecified
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.findRootCoordinates
import androidx.compose.ui.text.AnnotatedString
@@ -87,7 +89,6 @@
return IntSize(paragraph.minIntrinsicWidth.ceilToIntPx(), paragraph.height.ceilToIntPx())
}
-@OptIn(InternalFoundationTextApi::class)
internal class TextFieldDelegate {
companion object {
/**
@@ -113,28 +114,75 @@
*
* @param canvas The target canvas.
* @param value The editor state
+ * @param selectionPreviewHighlightRange Range to be highlighted to preview a handwriting
+ * selection gesture
+ * @param deletionPreviewHighlightRange Range to be highlighted to preview a handwriting
+ * deletion gesture
* @param offsetMapping The offset map
- * @param selectionPaint The selection paint
+ * @param textLayoutResult The text layout result
+ * @param highlightPaint Paint used to draw highlight backgrounds
+ * @param selectionBackgroundColor The selection highlight background color
*/
@JvmStatic
internal fun draw(
canvas: Canvas,
value: TextFieldValue,
+ selectionPreviewHighlightRange: TextRange,
+ deletionPreviewHighlightRange: TextRange,
offsetMapping: OffsetMapping,
textLayoutResult: TextLayoutResult,
- selectionPaint: Paint
+ highlightPaint: Paint,
+ selectionBackgroundColor: Color
) {
- if (!value.selection.collapsed) {
- val start = offsetMapping.originalToTransformed(value.selection.min)
- val end = offsetMapping.originalToTransformed(value.selection.max)
- if (start != end) {
- val selectionPath = textLayoutResult.getPathForRange(start, end)
- canvas.drawPath(selectionPath, selectionPaint)
- }
+ if (!selectionPreviewHighlightRange.collapsed) {
+ highlightPaint.color = selectionBackgroundColor
+ drawHighlight(
+ canvas,
+ selectionPreviewHighlightRange,
+ offsetMapping,
+ textLayoutResult,
+ highlightPaint
+ )
+ } else if (!deletionPreviewHighlightRange.collapsed) {
+ val textColor =
+ textLayoutResult.layoutInput.style.color.takeUnless { it.isUnspecified }
+ ?: Color.Black
+ highlightPaint.color = textColor.copy(alpha = textColor.alpha * 0.2f)
+ drawHighlight(
+ canvas,
+ deletionPreviewHighlightRange,
+ offsetMapping,
+ textLayoutResult,
+ highlightPaint
+ )
+ } else if (!value.selection.collapsed) {
+ highlightPaint.color = selectionBackgroundColor
+ drawHighlight(
+ canvas,
+ value.selection,
+ offsetMapping,
+ textLayoutResult,
+ highlightPaint
+ )
}
TextPainter.paint(canvas, textLayoutResult)
}
+ private fun drawHighlight(
+ canvas: Canvas,
+ range: TextRange,
+ offsetMapping: OffsetMapping,
+ textLayoutResult: TextLayoutResult,
+ paint: Paint
+ ) {
+ val start = offsetMapping.originalToTransformed(range.min)
+ val end = offsetMapping.originalToTransformed(range.max)
+ if (start != end) {
+ val selectionPath = textLayoutResult.getPathForRange(start, end)
+ canvas.drawPath(selectionPath, paint)
+ }
+ }
+
/**
* Notify system that focused input area.
*
@@ -354,8 +402,6 @@
* @param compositionRange An input state
* @param transformed A transformed text
* @return The transformed text with composition decoration.
- *
- * @suppress
*/
fun applyCompositionDecoration(
compositionRange: TextRange,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLayoutHelper.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLayoutHelper.kt
index cdc33f2..62593ab 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLayoutHelper.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLayoutHelper.kt
@@ -39,7 +39,6 @@
* @param layoutDirection a layout direction to be used for computing text layout.
* @param constraints a constraint to be used for computing text layout.
*/
-@OptIn(InternalFoundationTextApi::class)
internal fun TextLayoutResult.canReuse(
text: AnnotatedString,
style: TextStyle,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLinkScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLinkScope.kt
index 2703b2d..14af807 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLinkScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextLinkScope.kt
@@ -201,7 +201,7 @@
uriHandler: UriHandler
) {
when (link) {
- is LinkAnnotation.Url -> link.linkInteractionListener?.onClicked(link) ?: try {
+ is LinkAnnotation.Url -> link.linkInteractionListener?.onClick(link) ?: try {
uriHandler.openUri(link.url)
} catch (_: IllegalArgumentException) {
// we choose to silently fail when the uri can't be opened to avoid crashes
@@ -209,7 +209,7 @@
// handlers themselves and therefore I suspect are less likely to test them
// manually.
}
- is LinkAnnotation.Clickable -> link.linkInteractionListener?.onClicked(link)
+ is LinkAnnotation.Clickable -> link.linkInteractionListener?.onClick(link)
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/InputTransformation.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/InputTransformation.kt
index 7d4662b..ac18498 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/InputTransformation.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/InputTransformation.kt
@@ -140,7 +140,6 @@
// endregion
// region Transformation implementations
-@OptIn(ExperimentalFoundationApi::class)
private class FilterChain(
private val first: InputTransformation,
private val second: InputTransformation,
@@ -184,7 +183,6 @@
}
}
-@OptIn(ExperimentalFoundationApi::class)
private data class InputTransformationByValue(
val transformation: (
old: CharSequence,
@@ -231,7 +229,6 @@
}
// This is a very naive implementation for now, not intended to be production-ready.
-@OptIn(ExperimentalFoundationApi::class)
private data class MaxLengthFilter(
private val maxLength: Int
) : InputTransformation {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextUndoManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextUndoManager.kt
index 4ae8b38..09914bd 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextUndoManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextUndoManager.kt
@@ -37,7 +37,6 @@
* important task is to keep a separate staging area for incoming text edit operations to possibly
* merge them before committing as a single undoable action.
*/
-@OptIn(ExperimentalFoundationApi::class)
internal class TextUndoManager(
initialStagingUndo: TextUndoOperation? = null,
private val undoManager: UndoManager<TextUndoOperation> = UndoManager(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointTransformation.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointTransformation.kt
index cc5170b..ff2b499 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointTransformation.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointTransformation.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.appendCodePointX
import androidx.compose.foundation.text.input.TextFieldCharSequence
import androidx.compose.runtime.Stable
@@ -50,7 +49,6 @@
internal fun CodepointTransformation.Companion.mask(character: Char): CodepointTransformation =
MaskCodepointTransformation(character)
-@OptIn(ExperimentalFoundationApi::class)
private data class MaskCodepointTransformation(val character: Char) : CodepointTransformation {
override fun transform(codepointIndex: Int, codepoint: Int): Int {
return character.code
@@ -62,7 +60,6 @@
* carriage returns(\r) to zero-width no-break space (U+FEFF). This transformation forces any
* content to appear as single line.
*/
-@OptIn(ExperimentalFoundationApi::class)
internal object SingleLineCodepointTransformation : CodepointTransformation {
private const val LINE_FEED = '\n'.code
@@ -82,7 +79,6 @@
}
}
-@OptIn(ExperimentalFoundationApi::class)
internal fun TextFieldCharSequence.toVisualText(
codepointTransformation: CodepointTransformation,
offsetMappingCalculator: OffsetMappingCalculator
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldCoreModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldCoreModifier.kt
index 8376dd9..dcfb62f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldCoreModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldCoreModifier.kt
@@ -521,7 +521,7 @@
val cursorAlphaValue = cursorAnimation.cursorAlpha
if (cursorAlphaValue == 0f || !showCursor) return
- val cursorRect = textFieldSelectionState.cursorRect
+ val cursorRect = textFieldSelectionState.getCursorRect()
drawLine(
cursorBrush,
@@ -555,7 +555,9 @@
textFieldState.visualText
// Only animate the cursor when its window is actually focused. This also
// disables the cursor animation when the screen is off.
- val isWindowFocused = currentValueOf(LocalWindowInfo).isWindowFocused
+ // TODO: b/335668644, snapshotFlow is invoking this block even after the coroutine
+ // has been cancelled, and currentCoroutineContext().isActive is false
+ val isWindowFocused = isAttached && currentValueOf(LocalWindowInfo).isWindowFocused
((if (isWindowFocused) 1 else 2) * sign).also { sign *= -1 }
}.collectLatest { isWindowFocused ->
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDecoratorModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDecoratorModifier.kt
index 3e45a08..eeddf40 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDecoratorModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDecoratorModifier.kt
@@ -89,6 +89,7 @@
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.ImeOptions
+import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.IntSize
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -111,7 +112,6 @@
*
* This modifier handles input events (both key and pointer), semantics, and focus.
*/
-@OptIn(ExperimentalFoundationApi::class)
internal data class TextFieldDecoratorModifier(
private val textFieldState: TransformedTextFieldState,
private val textLayoutState: TextLayoutState,
@@ -225,28 +225,34 @@
detectTextFieldLongPressAndAfterDrag(requestFocus)
}
}
- // Note: when editable changes (enabled or readOnly changes), this pointerInputModifier
- // is reset. And we don't need to worry about cancel or launch the stylus handwriting
- // detecting job.
+ // Note: when editable changes (enabled or readOnly changes) or keyboard type changes,
+ // this pointerInputModifier is reset. And we don't need to worry about cancel or launch
+ // the stylus handwriting detecting job.
if (isStylusHandwritingSupported && editable) {
launch(start = CoroutineStart.UNDISPATCHED) {
detectStylusHandwriting {
if (!isFocused) {
requestFocus()
}
-
- // Send the handwriting start signal to platform.
- // The editor should send the signal when it is focused or is about
- // to gain focus, Here are more details:
- // 1) if the editor already has an active input session, the
- // platform handwriting service should already listen to this flow
- // and it'll start handwriting right away.
- //
- // 2) if the editor is not focused, but it'll be focused and
- // create a new input session, one handwriting signal will be
- // replayed when the platform collect this flow. And the platform
- // should trigger handwriting accordingly.
- stylusHandwritingTrigger?.tryEmit(Unit)
+ // If this is a password field, we can't trigger handwriting.
+ // The expected behavior is 1) request focus 2) show software keyboard.
+ // Note: TextField will show software keyboard automatically when it
+ // gain focus. 3) show a toast message telling that handwriting is not
+ // supported for password fields. TODO(b/335294152)
+ if (keyboardOptions.keyboardType != KeyboardType.Password) {
+ // Send the handwriting start signal to platform.
+ // The editor should send the signal when it is focused or is about
+ // to gain focus, Here are more details:
+ // 1) if the editor already has an active input session, the
+ // platform handwriting service should already listen to this flow
+ // and it'll start handwriting right away.
+ //
+ // 2) if the editor is not focused, but it'll be focused and
+ // create a new input session, one handwriting signal will be
+ // replayed when the platform collect this flow. And the platform
+ // should trigger handwriting accordingly.
+ stylusHandwritingTrigger?.tryEmit(Unit)
+ }
return@detectStylusHandwriting true
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.kt
index 082ff9d..0bf1698 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.DeadKeyCombiner
import androidx.compose.foundation.text.KeyCommand
import androidx.compose.foundation.text.appendCodePointX
@@ -52,7 +51,6 @@
* This class is left abstract to make sure that each platform extends from it. Platforms can
* decide to extend or completely override KeyEvent actions defined here.
*/
-@OptIn(ExperimentalFoundationApi::class)
internal abstract class TextFieldKeyEventHandler {
private val preparedSelectionState = TextFieldPreparedSelectionState()
private val deadKeyCombiner = DeadKeyCombiner()
@@ -259,9 +257,8 @@
*/
private fun TextLayoutState.getVisibleTextLayoutHeight(): Float {
return textLayoutNodeCoordinates?.takeIf { it.isAttached }?.let { textLayoutCoordinates ->
- decoratorNodeCoordinates?.takeIf { it.isAttached }?.let { decoratorCoordinates ->
- decoratorCoordinates.localBoundingBoxOf(textLayoutCoordinates)
- }
+ decoratorNodeCoordinates?.takeIf { it.isAttached }
+ ?.localBoundingBoxOf(textLayoutCoordinates)
}?.size?.height ?: Float.NaN
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.kt
index 547eeac..e8a573a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.kt
@@ -16,8 +16,6 @@
package androidx.compose.foundation.text.input.internal
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.text.InternalFoundationTextApi
import androidx.compose.foundation.text.TextDelegate
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.internal.TextFieldLayoutStateCache.MeasureInputs
@@ -32,9 +30,9 @@
import androidx.compose.runtime.snapshots.StateRecord
import androidx.compose.runtime.snapshots.withCurrent
import androidx.compose.runtime.snapshots.writable
-import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutInput
import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextMeasurer
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.Constraints
@@ -58,7 +56,6 @@
* in an instance of a dedicated class. This means for each type of update, only one state object
* is needed.
*/
-@OptIn(ExperimentalFoundationApi::class, InternalFoundationTextApi::class)
internal class TextFieldLayoutStateCache : State<TextLayoutResult?>, StateObject {
private var nonMeasureInputs: NonMeasureInputs? by mutableStateOf(
value = null,
@@ -165,12 +162,18 @@
cachedRecord.constraints == measureInputs.constraints &&
cachedRecord.fontFamilyResolver == measureInputs.fontFamilyResolver
) {
+ val isLayoutAffectingSame = cachedRecord.textStyle
+ ?.hasSameLayoutAffectingAttributes(nonMeasureInputs.textStyle) ?: false
+
+ val isDrawAffectingSame = cachedRecord.textStyle
+ ?.hasSameDrawAffectingAttributes(nonMeasureInputs.textStyle) ?: false
+
// Fast path: None of the inputs changed.
- if (cachedRecord.textStyle == nonMeasureInputs.textStyle) return cachedResult
+ if (isLayoutAffectingSame && isDrawAffectingSame) {
+ return cachedResult
+ }
// Slightly slower than fast path: Layout did not change but TextLayoutInput did
- if (cachedRecord.textStyle
- ?.hasSameDrawAffectingAttributes(nonMeasureInputs.textStyle) == true
- ) {
+ if (isLayoutAffectingSame) {
return cachedResult.copy(
layoutInput = TextLayoutInput(
cachedResult.layoutInput.text,
@@ -189,10 +192,13 @@
}
// Slow path: Some input changed, need to re-layout.
- return computeLayout(visualText, nonMeasureInputs, measureInputs, cachedResult)
+ return computeLayout(visualText, nonMeasureInputs, measureInputs)
.also { newResult ->
- // TODO(b/294403840) TextDelegate does its own caching and may return the same
- // TextLayoutResult object. We should inline that so we don't check twice.
+ // Although the snapshot-aware cache is only updated when the current snapshot
+ // is writable, we still would like to cache the results of text layout
+ // computation since it's very likely that a follow-up access to the text layout
+ // result will use the same measure and non-measure inputs. Therefore, we use
+ // a `TextMeasurer` with a cache size of 1 to compute the text layout result.
if (newResult != cachedResult) {
updateCacheIfWritable {
this.visualText = visualText
@@ -219,31 +225,45 @@
}
}
+ private var textMeasurer: TextMeasurer? = null
+
+ /**
+ * Returns a [TextMeasurer] instance, either initialized from [measureInputs] or the one
+ * previously created. If a cached [TextMeasurer] is returned and [measureInputs] do not match
+ * the attributes of previous instance, make sure to call [TextMeasurer.measure] with the
+ * up-to-date parameters. [TextMeasurer] will override its fallback values for
+ * [FontFamily.Resolver], [Density], and [LayoutDirection] when these are passed explicitly
+ * to the [TextMeasurer.measure] function.
+ */
+ private fun obtainTextMeasurer(measureInputs: MeasureInputs): TextMeasurer {
+ return textMeasurer ?: TextMeasurer(
+ defaultFontFamilyResolver = measureInputs.fontFamilyResolver,
+ defaultDensity = measureInputs.density,
+ defaultLayoutDirection = measureInputs.layoutDirection,
+ cacheSize = 1
+ ).also { textMeasurer = it }
+ }
+
private fun computeLayout(
visualText: CharSequence,
nonMeasureInputs: NonMeasureInputs,
- measureInputs: MeasureInputs,
- prevResult: TextLayoutResult?
+ measureInputs: MeasureInputs
): TextLayoutResult {
- // TODO(b/294403840) Don't use TextDelegate – it is not designed for this use case,
- // optimized for re-use which we don't take advantage of here, and does its own caching
- // checks. Maybe we can use MultiParagraphLayoutCache like BasicText?
+ // TODO(b/294403840) Don't use TextMeasurer – it is not designed for this use case,
+ // optimized for re-use which we don't take a great advantage of here, and does its own
+ // caching checks. Maybe we can use MultiParagraphLayoutCache like BasicText?
- // We have to always create a new TextDelegate since it contains internal state that is
- // not snapshot-aware.
- val textDelegate = TextDelegate(
- text = AnnotatedString(visualText.toString()),
+ val textMeasurer = obtainTextMeasurer(measureInputs)
+
+ return textMeasurer.measure(
+ text = visualText.toString(),
style = nonMeasureInputs.textStyle,
+ softWrap = nonMeasureInputs.softWrap,
+ maxLines = if (nonMeasureInputs.singleLine) 1 else Int.MAX_VALUE,
+ constraints = measureInputs.constraints,
+ layoutDirection = measureInputs.layoutDirection,
density = measureInputs.density,
fontFamilyResolver = measureInputs.fontFamilyResolver,
- softWrap = nonMeasureInputs.softWrap,
- placeholders = emptyList()
- )
-
- return textDelegate.layout(
- layoutDirection = measureInputs.layoutDirection,
- constraints = measureInputs.constraints,
- prevResult = prevResult
)
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextLayoutState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextLayoutState.kt
index feace9f..0104ada 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextLayoutState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextLayoutState.kt
@@ -218,9 +218,8 @@
*/
internal fun TextLayoutState.fromTextLayoutToCore(offset: Offset): Offset {
return textLayoutNodeCoordinates?.takeIf { it.isAttached }?.let { textLayoutNodeCoordinates ->
- coreNodeCoordinates?.takeIf { it.isAttached }?.let { coreNodeCoordinates ->
- coreNodeCoordinates.localPositionOf(textLayoutNodeCoordinates, offset)
- }
+ coreNodeCoordinates?.takeIf { it.isAttached }
+ ?.localPositionOf(textLayoutNodeCoordinates, offset)
} ?: offset
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifier.kt
index 17ee351..fe8166b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifier.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal.selection
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.Handle
import androidx.compose.foundation.text.input.internal.TextLayoutState
import androidx.compose.foundation.text.input.internal.TransformedTextFieldState
@@ -62,7 +61,6 @@
visible: Boolean
): TextFieldMagnifierNode
-@OptIn(ExperimentalFoundationApi::class)
internal fun calculateSelectionMagnifierCenterAndroid(
textFieldState: TransformedTextFieldState,
selectionState: TextFieldSelectionState,
@@ -100,7 +98,14 @@
// Hide the magnifier when dragged too far (outside the horizontal bounds of how big the
// magnifier actually is). See
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/widget/Editor.java;l=5228-5231;drc=2fdb6bd709be078b72f011334362456bb758922c
- if ((dragX - centerX).absoluteValue > magnifierSize.width / 2) {
+ // Also check whether magnifierSize is calculated. A platform magnifier instance is not
+ // created until it's requested for the first time. So the size will only be calculated after we
+ // return a specified offset from this function.
+ // It is very unlikely that this behavior would cause a flicker since magnifier immediately
+ // shows up where the pointer is being dragged. The pointer needs to drag further than the half
+ // of magnifier's width to hide by the following logic.
+ if (magnifierSize != IntSize.Zero &&
+ (dragX - centerX).absoluteValue > magnifierSize.width / 2) {
return Offset.Unspecified
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.kt
index 7a90afa..46181b9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.kt
@@ -54,13 +54,11 @@
import androidx.compose.foundation.text.selection.getTextFieldSelectionLayout
import androidx.compose.foundation.text.selection.isPrecisePointer
import androidx.compose.foundation.text.selection.visibleBounds
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.snapshots.Snapshot
-import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.isSpecified
@@ -203,7 +201,7 @@
* Whether to show the TextToolbar according to current selection state. This is not the final
* decider for showing the toolbar. Please refer to [observeTextToolbarVisibility] docs.
*/
- private var textToolbarState by mutableStateOf(TextToolbarState.None)
+ private var textToolbarState by mutableStateOf(None)
/**
* Access helper for text layout node coordinates that checks attached state.
@@ -235,8 +233,11 @@
/**
* State of the cursor handle that includes its visibility and position.
+ *
+ * Pass [includePosition] as false to omit the position from the result. This helps create a
+ * derived state which does not invalidate according position changes.
*/
- val cursorHandle by derivedStateOf {
+ internal fun getCursorHandleState(includePosition: Boolean): TextFieldHandleState {
// For cursor handle to be visible, [showCursorHandle] must be true and the selection
// must be collapsed.
// Also, cursor handle should be in visible bounds of the TextField. However, if
@@ -249,34 +250,33 @@
text.selection.collapsed &&
text.shouldShowSelection() &&
text.isNotEmpty() &&
- (draggingHandle == Handle.Cursor || cursorHandleInBounds)
+ (draggingHandle == Handle.Cursor || isCursorHandleInVisibleBounds())
- if (!visible) return@derivedStateOf TextFieldHandleState.Hidden
+ if (!visible) return TextFieldHandleState.Hidden
// text direction is useless for cursor handle, any value is fine.
- TextFieldHandleState(
+ return TextFieldHandleState(
visible = true,
- position = cursorRect.bottomCenter,
+ position = if (includePosition) getCursorRect().bottomCenter else Offset.Unspecified,
direction = ResolvedTextDirection.Ltr,
handlesCrossed = false
)
}
/**
- * Whether currently cursor handle is in visible bounds. This derived state does not react to
- * selection changes immediately because every selection change is processed in layout phase
- * by auto-scroll behavior. Only after giving auto-scroll time to process the cursor movement,
- * and possibly scroll the cursor back into view, we can say that whether cursor is in visible
- * bounds or not. This is guaranteed to happen after scroll since new [textLayoutCoordinates]
- * are reported after the layout phase end.
+ * Whether currently cursor handle is in visible bounds. This function does not register a
+ * state read because every selection change is processed in layout phase by the auto-scroll
+ * behavior.
+ *
+ * We can say that whether cursor is in visible bounds or not only after giving auto-scroll
+ * time to process the cursor movement, and possibly scroll the cursor back into view. This is
+ * guaranteed to happen after scroll since new [textLayoutCoordinates] are reported after the
+ * layout phase ends.
*/
- private val cursorHandleInBounds by derivedStateOf(policy = structuralEqualityPolicy()) {
- val position = Snapshot.withoutReadObservation { cursorRect.bottomCenter }
+ private fun isCursorHandleInVisibleBounds(): Boolean {
+ val position = Snapshot.withoutReadObservation { getCursorRect().bottomCenter }
- textLayoutCoordinates
- ?.visibleBounds()
- ?.containsInclusive(position)
- ?: false
+ return textLayoutCoordinates?.visibleBounds()?.containsInclusive(position) ?: false
}
/**
@@ -285,10 +285,10 @@
* Returns [Rect.Zero] if text layout has not been calculated yet or the selection is not
* collapsed (no cursor to locate).
*/
- val cursorRect: Rect by derivedStateOf {
- val layoutResult = textLayoutState.layoutResult ?: return@derivedStateOf Rect.Zero
+ fun getCursorRect(): Rect {
+ val layoutResult = textLayoutState.layoutResult ?: return Rect.Zero
val value = textFieldState.visualText
- if (!value.selection.collapsed) return@derivedStateOf Rect.Zero
+ if (!value.selection.collapsed) return Rect.Zero
val cursorRect = layoutResult.getCursorRect(value.selection.start)
val cursorWidth = with(density) { DefaultCursorThickness.toPx() }
@@ -308,7 +308,7 @@
.coerceAtMost(layoutResult.size.width - cursorWidth / 2)
.coerceAtLeast(cursorWidth / 2)
- Rect(
+ return Rect(
left = coercedCursorCenterX - cursorWidth / 2,
right = coercedCursorCenterX + cursorWidth / 2,
top = cursorRect.top,
@@ -350,15 +350,16 @@
}
launch(start = CoroutineStart.UNDISPATCHED) {
detectTapGestures(onTap = {
- textToolbarState = if (textToolbarState == TextToolbarState.Cursor) {
- TextToolbarState.None
+ textToolbarState = if (textToolbarState == Cursor) {
+ None
} else {
- TextToolbarState.Cursor
+ Cursor
}
})
}
}
}
+
/**
* Gesture detector for dragging the selection handles to change the selection in TextField.
*/
@@ -403,7 +404,7 @@
}
} finally {
showCursorHandle = false
- if (textToolbarState != TextToolbarState.None) {
+ if (textToolbarState != None) {
hideTextToolbar()
}
}
@@ -452,7 +453,7 @@
}
// do not show any TextToolbar.
- updateTextToolbarState(TextToolbarState.None)
+ updateTextToolbarState(None)
val coercedOffset = textLayoutState.coercedInVisibleBoundsOfInputText(offset)
@@ -467,7 +468,7 @@
showCursorHandle = false
// go into selection mode.
- updateTextToolbarState(TextToolbarState.Selection)
+ updateTextToolbarState(Selection)
val index = textLayoutState.getOffsetForPosition(offset)
val newSelection = updateSelection(
@@ -612,7 +613,7 @@
detectDragGestures(
onDragStart = {
// mark start drag point
- cursorDragStart = getAdjustedCoordinates(cursorRect.bottomCenter)
+ cursorDragStart = getAdjustedCoordinates(getCursorRect().bottomCenter)
cursorDragDelta = Offset.Zero
isInTouchMode = true
markStartContentVisibleOffset()
@@ -683,7 +684,7 @@
hapticFeedBack?.performHapticFeedback(HapticFeedbackType.TextHandleMove)
textFieldState.placeCursorBeforeCharAt(offset)
showCursorHandle = true
- updateTextToolbarState(TextToolbarState.Cursor)
+ updateTextToolbarState(Cursor)
} else {
if (textFieldState.visualText.isEmpty()) return@onDragStart
val offset = textLayoutState.getOffsetForPosition(dragStartOffset)
@@ -700,7 +701,7 @@
adjustment = SelectionAdjustment.CharacterWithWordAccelerate,
)
textFieldState.selectCharsIn(newSelection)
- updateTextToolbarState(TextToolbarState.Selection)
+ updateTextToolbarState(Selection)
// For touch, set the begin offset to the adjusted selection.
// When char based selection is used, we want to ensure we snap the
// beginning offset to the start word boundary of the first selected word.
@@ -769,6 +770,14 @@
allowPreviousSelectionCollapsed = false,
)
+ // When drag starts from the end padding, we eventually need to update the start
+ // point once a selection is initiated. Otherwise, startOffset is always calculated
+ // from dragBeginPosition which can refer to different positions on text if
+ // TextField starts scrolling.
+ if (dragBeginOffsetInText == -1 && !newSelection.collapsed) {
+ dragBeginOffsetInText = newSelection.start
+ }
+
// Although we support reversed selection, reversing the selection after it's
// initiated via long press has a visual glitch that's hard to get rid of. When
// handles (start/end) switch places after the selection reverts, draw happens a
@@ -778,14 +787,6 @@
newSelection = newSelection.reverse()
}
- // When drag starts from the end padding, we eventually need to update the start
- // point once a selection is initiated. Otherwise, startOffset is always calculated
- // from dragBeginPosition which can refer to different positions on text if
- // TextField starts scrolling.
- if (dragBeginOffsetInText == -1 && !newSelection.collapsed) {
- dragBeginOffsetInText = newSelection.start
- }
-
// if the new selection is not equal to previous selection, consider updating the
// acting handle. Otherwise, acting handle should remain the same.
if (newSelection != prevSelection) {
@@ -916,7 +917,7 @@
.collect {
showCursorHandle = false
// hide the toolbar any time text content changes.
- updateTextToolbarState(TextToolbarState.None)
+ updateTextToolbarState(None)
}
}
@@ -938,8 +939,8 @@
snapshotFlow {
val isCollapsed = textFieldState.visualText.selection.collapsed
val textToolbarStateVisible =
- isCollapsed && textToolbarState == TextToolbarState.Cursor ||
- !isCollapsed && textToolbarState == TextToolbarState.Selection
+ isCollapsed && textToolbarState == Cursor ||
+ !isCollapsed && textToolbarState == Selection
val textToolbarVisible =
// toolbar is requested specifically for the current selection state
@@ -954,8 +955,8 @@
if (!textToolbarVisible) {
Rect.Zero
} else {
- // contentRect is calculated in root coordinates. VisibleBounds are in parent
- // coordinates. Convert visibleBounds to root before checking the overlap.
+ // contentRect is calculated in root coordinates. VisibleBounds are in
+ // textLayoutCoordinates. Convert visibleBounds to root before checking the overlap.
val visibleBounds = textLayoutCoordinates?.visibleBounds()
if (visibleBounds != null) {
val visibleBoundsTopLeftInRoot =
@@ -993,6 +994,7 @@
// contentRect is defined in text layout node coordinates, so it needs to be realigned to
// the root container.
if (text.selection.collapsed) {
+ val cursorRect = getCursorRect()
val topLeft = textLayoutCoordinates?.localToRoot(cursorRect.topLeft) ?: Offset.Zero
return Rect(topLeft, cursorRect.size)
}
@@ -1048,10 +1050,7 @@
val position = getHandlePosition(isStartHandle)
val visible = draggingHandle == handle ||
- (textLayoutCoordinates
- ?.visibleBounds()
- ?.containsInclusive(position)
- ?: false)
+ (textLayoutCoordinates?.visibleBounds()?.containsInclusive(position) ?: false)
if (!visible) return TextFieldHandleState.Hidden
@@ -1066,8 +1065,7 @@
// to coerce handle's position to visible bounds to not let it jitter while scrolling the
// TextField as the selection is expanding.
val coercedPosition = if (includePosition) {
- textLayoutCoordinates?.visibleBounds()?.let { position.coerceIn(it) }
- ?: position
+ textLayoutCoordinates?.visibleBounds()?.let { position.coerceIn(it) } ?: position
} else {
Offset.Unspecified
}
@@ -1266,10 +1264,10 @@
// TODO(halilibo): Add a new TextToolbar option "paste as plain text".
textToolbar?.showMenu(
rect = contentRect,
- onCopyRequested = menuItem(canCopy(), TextToolbarState.None) { copy() },
- onPasteRequested = menuItem(canPaste(), TextToolbarState.None) { paste() },
- onCutRequested = menuItem(canCut(), TextToolbarState.None) { cut() },
- onSelectAllRequested = menuItem(canSelectAll(), TextToolbarState.Selection) {
+ onCopyRequested = menuItem(canCopy(), None) { copy() },
+ onPasteRequested = menuItem(canPaste(), None) { paste() },
+ onCutRequested = menuItem(canCut(), None) { cut() },
+ onSelectAllRequested = menuItem(canSelectAll(), Selection) {
selectAll()
},
)
@@ -1292,7 +1290,7 @@
}
showCursorHandle = false
- updateTextToolbarState(TextToolbarState.None)
+ updateTextToolbarState(None)
}
private fun hideTextToolbar() {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextPreparedSelection.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextPreparedSelection.kt
index 79579dc..4357482 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextPreparedSelection.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextPreparedSelection.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.text.input.internal.selection
import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.findFollowingBreak
import androidx.compose.foundation.text.findParagraphEnd
import androidx.compose.foundation.text.findParagraphStart
@@ -83,7 +82,6 @@
* between successive [TextFieldPreparedSelection]s, e.g. original X position of the cursor while
* moving the cursor up/down.
*/
-@OptIn(ExperimentalFoundationApi::class)
internal class TextFieldPreparedSelection(
private val state: TransformedTextFieldState,
private val textLayoutResult: TextLayoutResult?,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/undo/TextUndoOperation.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/undo/TextUndoOperation.kt
index 5b7b1a6..c1733d8 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/undo/TextUndoOperation.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/undo/TextUndoOperation.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal.undo
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.timeNowMillis
import androidx.compose.runtime.saveable.Saver
@@ -112,7 +111,6 @@
/**
* Apply a given [TextUndoOperation] in reverse to undo this [TextFieldState].
*/
-@OptIn(ExperimentalFoundationApi::class)
internal fun TextFieldState.undo(op: TextUndoOperation) {
editWithNoSideEffects {
replace(op.index, op.index + op.postText.length, op.preText)
@@ -123,7 +121,6 @@
/**
* Apply a given [TextUndoOperation] in forward direction to redo this [TextFieldState].
*/
-@OptIn(ExperimentalFoundationApi::class)
internal fun TextFieldState.redo(op: TextUndoOperation) {
editWithNoSideEffects {
replace(op.index, op.index + op.preText.length, op.postText)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt
index ed89579..201baaf 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt
@@ -29,7 +29,9 @@
overflow: TextOverflow,
maxIntrinsicWidth: Float
): Constraints = Constraints(
+ minWidth = 0,
maxWidth = finalMaxWidth(constraints, softWrap, overflow, maxIntrinsicWidth),
+ minHeight = 0,
maxHeight = constraints.maxHeight
)
@@ -86,48 +88,3 @@
val overwriteMaxLines = !softWrap && overflow == TextOverflow.Ellipsis
return if (overwriteMaxLines) 1 else maxLinesIn.coerceAtLeast(1)
}
-
-/**
- * Constraints are packed see [Constraints] implementation.
- *
- * These constants are the largest values that each slot can hold. The following pairings are the
- * only ones allowed:
- *
- * (Big, Tiny), (Tiny, Big)
- * (Medium, Small), (Small, Medium)
- *
- * For more information see [Constraints] implementation
- */
-internal const val BigConstraintValue = (1 shl 18) - 1
-internal const val MediumConstraintValue = (1 shl 16) - 1
-internal const val SmallConstraintValue = (1 shl 15) - 1
-internal const val TinyConstraintValue = (1 shl 13) - 1
-
-/**
- * Make constraints that never throw from being too large. Prefer to keep accurate width information
- * first, then constrain height based on the size of width.
- *
- * This will return a Constraint with the same or smaller dimensions than the passed (width, height)
- *
- * see b/312294386 for more details
- *
- * This particular logic is text specific, so not generalizing.
- *
- * @param width desired width (has priority)
- * @param height desired height (uses the remaining bits after width)
- *
- * @return a safe Constraint that never throws for running out of bits
- */
-internal fun Constraints.Companion.fixedCoerceHeightAndWidthForBits(
- width: Int,
- height: Int
-): Constraints {
- val safeWidth = minOf(width, BigConstraintValue - 1)
- val safeHeight = when {
- safeWidth < TinyConstraintValue -> minOf(height, BigConstraintValue - 1)
- safeWidth < SmallConstraintValue -> minOf(height, MediumConstraintValue - 1)
- safeWidth < MediumConstraintValue -> minOf(height, SmallConstraintValue - 1)
- else -> minOf(height, TinyConstraintValue - 1)
- }
- return fixed(safeWidth, safeHeight)
-}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
index 6496137..79212ae 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
@@ -60,6 +60,7 @@
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Constraints.Companion.fitPrioritizingWidth
import androidx.compose.ui.unit.Density
import androidx.compose.ui.util.fastRoundToInt
@@ -442,9 +443,11 @@
// then allow children to measure _inside_ our final box, with the above placeholders
val placeable = measurable.measure(
- Constraints.fixedCoerceHeightAndWidthForBits(
- width = textLayoutResult.size.width,
- height = textLayoutResult.size.height
+ fitPrioritizingWidth(
+ minWidth = textLayoutResult.size.width,
+ maxWidth = textLayoutResult.size.width,
+ minHeight = textLayoutResult.size.height,
+ maxHeight = textLayoutResult.size.height
)
)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
index 8ce81b6..5dbf89d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
@@ -55,6 +55,7 @@
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Constraints.Companion.fitPrioritizingWidth
import androidx.compose.ui.unit.Density
import androidx.compose.ui.util.fastRoundToInt
@@ -75,6 +76,10 @@
private var minLines: Int = DefaultMinLines,
private var overrideColor: ColorProducer? = null
) : Modifier.Node(), LayoutModifierNode, DrawModifierNode, SemanticsModifierNode {
+ @Suppress("PrimitiveInCollection") // Map required for use in public API.
+ // Usages of this collection are so few that the gains of using
+ // MutableObjectIntMap<AlignmentLine> and then converting to a Map<AlignmentLine, Int>
+ // as needed for the public API is not worth the performance benefit.
private var baselineCache: MutableMap<AlignmentLine, Int>? = null
private var _layoutCache: ParagraphLayoutCache? = null
@@ -348,6 +353,8 @@
if (didChangeLayout) {
invalidateLayer()
+ // Map<AlignmentLine, Int> required for use in public API `layout` below
+ @Suppress("PrimitiveInCollection")
var cache = baselineCache
if (cache == null) {
cache = LinkedHashMap(2)
@@ -359,9 +366,11 @@
// then allow children to measure _inside_ our final box, with the above placeholders
val placeable = measurable.measure(
- Constraints.fixedCoerceHeightAndWidthForBits(
- layoutSize.width,
- layoutSize.height
+ fitPrioritizingWidth(
+ minWidth = layoutSize.width,
+ maxWidth = layoutSize.width,
+ minHeight = layoutSize.height,
+ maxHeight = layoutSize.height
)
)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
index 8b8b02b..3becb0e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
@@ -37,6 +37,12 @@
/**
* Enables text selection for its direct or indirect children.
*
+ * Use of a lazy layout, such as [LazyRow][androidx.compose.foundation.lazy.LazyRow] or
+ * [LazyColumn][androidx.compose.foundation.lazy.LazyColumn], within a [SelectionContainer]
+ * has undefined behavior on text items that aren't composed. For example, texts that aren't
+ * composed will not be included in copy operations and select all will not expand the
+ * selection to include them.
+ *
* @sample androidx.compose.foundation.samples.SelectionSample
*/
@Composable
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt
index 948d11b..7796aa0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionLayout.kt
@@ -16,6 +16,13 @@
package androidx.compose.foundation.text.selection
+import androidx.collection.LongIntMap
+import androidx.collection.LongObjectMap
+import androidx.collection.MutableLongIntMap
+import androidx.collection.MutableLongObjectMap
+import androidx.collection.longObjectMapOf
+import androidx.collection.mutableLongIntMapOf
+import androidx.collection.mutableLongObjectMapOf
import androidx.compose.foundation.text.selection.Direction.AFTER
import androidx.compose.foundation.text.selection.Direction.BEFORE
import androidx.compose.foundation.text.selection.Direction.ON
@@ -125,11 +132,11 @@
*
* @param selection The selection to turn into subSelections
*/
- fun createSubSelections(selection: Selection): Map<Long, Selection>
+ fun createSubSelections(selection: Selection): LongObjectMap<Selection>
}
private class MultiSelectionLayout(
- val selectableIdToInfoListIndex: Map<Long, Int>,
+ val selectableIdToInfoListIndex: LongIntMap,
val infoList: List<SelectableInfo>,
override val startSlot: Int,
override val endSlot: Int,
@@ -206,7 +213,7 @@
return false
}
- override fun createSubSelections(selection: Selection): Map<Long, Selection> =
+ override fun createSubSelections(selection: Selection): LongObjectMap<Selection> =
// Selection is within one selectable, we can return a singleton map of this selection.
if (selection.start.selectableId == selection.end.selectableId) {
// this check, if not passed, leads to exceptions when selection
@@ -217,8 +224,8 @@
) {
"unexpectedly miss-crossed selection: $selection"
}
- mapOf(selection.start.selectableId to selection)
- } else buildMap {
+ longObjectMapOf(selection.start.selectableId, selection)
+ } else mutableLongObjectMapOf<Selection>().apply {
val minAnchor = with(selection) { if (handlesCrossed) end else start }
createAndPutSubSelection(selection, firstInfo, minAnchor.offset, firstInfo.textLength)
@@ -230,7 +237,7 @@
createAndPutSubSelection(selection, lastInfo, minOffset = 0, maxAnchor.offset)
}
- private fun MutableMap<Long, Selection>.createAndPutSubSelection(
+ private fun MutableLongObjectMap<Selection>.createAndPutSubSelection(
selection: Selection,
info: SelectableInfo,
minOffset: Int,
@@ -288,10 +295,11 @@
return (slot - slotAdjustment) / 2
}
- private fun getInfoListIndexBySelectableId(id: Long): Int =
- requireNotNull(selectableIdToInfoListIndex[id]) {
- "Invalid selectableId: $id"
- }
+ private fun getInfoListIndexBySelectableId(id: Long): Int = try {
+ selectableIdToInfoListIndex[id]
+ } catch (e: NoSuchElementException) {
+ throw IllegalStateException("Invalid selectableId: $id", e)
+ }
}
/**
@@ -332,14 +340,14 @@
isStartHandle != other.isStartHandle ||
info.shouldRecomputeSelection(other.info)
- override fun createSubSelections(selection: Selection): Map<Long, Selection> {
+ override fun createSubSelections(selection: Selection): LongObjectMap<Selection> {
check(
(selection.handlesCrossed && selection.start.offset >= selection.end.offset) ||
(!selection.handlesCrossed && selection.start.offset <= selection.end.offset)
) {
"unexpectedly miss-crossed selection: $selection"
}
- return mapOf(info.selectableId to selection)
+ return longObjectMapOf(info.selectableId, selection)
}
override fun toString(): String =
@@ -431,7 +439,7 @@
val previousSelection: Selection?,
val selectableIdOrderingComparator: Comparator<Long>
) {
- private val selectableIdToInfoListIndex: MutableMap<Long, Int> = mutableMapOf()
+ private val selectableIdToInfoListIndex: MutableLongIntMap = mutableLongIntMapOf()
private val infoList: MutableList<SelectableInfo> = mutableListOf()
private var startSlot: Int = UNASSIGNED_SLOT
private var endSlot: Int = UNASSIGNED_SLOT
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
index c01b952..465fc46 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
@@ -17,6 +17,10 @@
package androidx.compose.foundation.text.selection
import androidx.annotation.VisibleForTesting
+import androidx.collection.LongObjectMap
+import androidx.collection.emptyLongObjectMap
+import androidx.collection.mutableLongIntMapOf
+import androidx.collection.mutableLongObjectMapOf
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.waitForUpOrCancellation
@@ -54,6 +58,7 @@
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastFold
import androidx.compose.ui.util.fastForEach
@@ -273,7 +278,7 @@
selectionRegistrar.onSelectionUpdateSelectAll =
{ isInTouchMode, selectableId ->
- val (newSelection, newSubselection) = selectAll(
+ val (newSelection, newSubselection) = selectAllInSelectable(
selectableId = selectableId,
previousSelection = selection,
)
@@ -405,11 +410,11 @@
return coordinates
}
- internal fun selectAll(
+ internal fun selectAllInSelectable(
selectableId: Long,
previousSelection: Selection?
- ): Pair<Selection?, Map<Long, Selection>> {
- val subselections = mutableMapOf<Long, Selection>()
+ ): Pair<Selection?, LongObjectMap<Selection>> {
+ val subselections = mutableLongObjectMapOf<Selection>()
val newSelection = selectionRegistrar.sort(requireContainerCoordinates())
.fastFold(null) { mergedSelection: Selection?, selectable: Selectable ->
val selection = if (selectable.selectableId == selectableId)
@@ -424,6 +429,70 @@
}
/**
+ * Returns whether the selection encompasses the entire container.
+ */
+ internal fun isEntireContainerSelected(): Boolean {
+ val selectables = selectionRegistrar.sort(requireContainerCoordinates())
+
+ // If there are no selectables, then an empty selection spans the entire container.
+ if (selectables.isEmpty()) return true
+
+ // Since some text exists, we must make sure that every selectable is fully selected.
+ return selectables.fastAll {
+ val text = it.getText()
+ if (text.isEmpty()) return@fastAll true // empty text is inherently fully selected
+
+ // If a non-empty selectable isn't included in the sub-selections,
+ // then some text in the container is not selected.
+ val subSelection = selectionRegistrar.subselections[it.selectableId]
+ ?: return@fastAll false
+
+ val selectionStart = subSelection.start.offset
+ val selectionEnd = subSelection.end.offset
+
+ // The selection could be reversed,
+ // so just verify that the difference between the two offsets matches the text length
+ (selectionStart - selectionEnd).absoluteValue == text.length
+ }
+ }
+
+ /**
+ * Creates and sets a selection spanning the entire container.
+ */
+ internal fun selectAll() {
+ val selectables = selectionRegistrar.sort(requireContainerCoordinates())
+ if (selectables.isEmpty()) return
+
+ var firstSubSelection: Selection? = null
+ var lastSubSelection: Selection? = null
+ val newSubSelections = mutableLongObjectMapOf<Selection>().apply {
+ selectables.fastForEach { selectable ->
+ val subSelection = selectable.getSelectAllSelection() ?: return@fastForEach
+ if (firstSubSelection == null) firstSubSelection = subSelection
+ lastSubSelection = subSelection
+ put(selectable.selectableId, subSelection)
+ }
+ }
+
+ if (newSubSelections.isEmpty()) return
+
+ // first/last sub selections are implied to be non-null from here on out
+ val newSelection = if (firstSubSelection === lastSubSelection) {
+ firstSubSelection
+ } else {
+ Selection(
+ start = firstSubSelection!!.start,
+ end = lastSubSelection!!.end,
+ handlesCrossed = false,
+ )
+ }
+
+ selectionRegistrar.subselections = newSubSelections
+ onSelectionChange(newSelection)
+ previousSelectionLayout = null
+ }
+
+ /**
* Returns whether the start and end anchors are equal.
*
* It is possible that this returns true, but the selection is still empty because it has
@@ -517,9 +586,13 @@
}
val textToolbar = textToolbar ?: return
- if (showToolbar && isInTouchMode && isNonEmptySelection()) {
+ if (showToolbar && isInTouchMode) {
val rect = getContentRect() ?: return
- textToolbar.showMenu(rect = rect, onCopyRequested = ::toolbarCopy)
+ textToolbar.showMenu(
+ rect = rect,
+ onCopyRequested = if (isNonEmptySelection()) ::toolbarCopy else null,
+ onSelectAllRequested = if (isEntireContainerSelected()) null else ::selectAll,
+ )
} else if (textToolbar.status == TextToolbarStatus.Shown) {
textToolbar.hide()
}
@@ -558,7 +631,7 @@
// This is for PressGestureDetector to cancel the selection.
fun onRelease() {
- selectionRegistrar.subselections = emptyMap()
+ selectionRegistrar.subselections = emptyLongObjectMap()
showToolbar = false
if (selection != null) {
onSelectionChange(null)
@@ -785,7 +858,7 @@
val containerCoordinates = requireContainerCoordinates()
val sortedSelectables = selectionRegistrar.sort(containerCoordinates)
- val idToIndexMap = mutableMapOf<Long, Int>()
+ val idToIndexMap = mutableLongIntMapOf()
sortedSelectables.fastForEachIndexed { index, selectable ->
idToIndexMap[selectable.selectableId] = index
}
@@ -961,7 +1034,14 @@
// Hide the magnifier when dragged too far (outside the horizontal bounds of how big the
// magnifier actually is). See
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/widget/Editor.java;l=5228-5231;drc=2fdb6bd709be078b72f011334362456bb758922c
- if ((dragX - textConstrainedX).absoluteValue > magnifierSize.width / 2) {
+ // Also check whether magnifierSize is calculated. A platform magnifier instance is not
+ // created until it's requested for the first time. So the size will only be calculated after we
+ // return a specified offset from this function.
+ // It is very unlikely that this behavior would cause a flicker since magnifier immediately
+ // shows up where the pointer is being dragged. The pointer needs to drag further than the half
+ // of magnifier's width to hide by the following logic.
+ if (magnifierSize != IntSize.Zero &&
+ (dragX - textConstrainedX).absoluteValue > magnifierSize.width / 2) {
return Offset.Unspecified
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt
index 4aa9753..80caabf 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrar.kt
@@ -16,6 +16,7 @@
package androidx.compose.foundation.text.selection
+import androidx.collection.LongObjectMap
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.geometry.Offset
@@ -31,7 +32,7 @@
* [MutableState]. And any composable reading this field will be recomposed once its value
* changed.
*/
- val subselections: Map<Long, Selection>
+ val subselections: LongObjectMap<Selection>
/**
* Subscribe to SelectionContainer selection changes.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt
index dce40ef..72b77d8 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImpl.kt
@@ -16,6 +16,9 @@
package androidx.compose.foundation.text.selection
+import androidx.collection.LongObjectMap
+import androidx.collection.emptyLongObjectMap
+import androidx.collection.mutableLongObjectMapOf
import androidx.compose.foundation.AtomicLong
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -53,12 +56,12 @@
internal val selectables: List<Selectable>
get() = _selectables
- private val _selectableMap = mutableMapOf<Long, Selectable>()
+ private val _selectableMap = mutableLongObjectMapOf<Selectable>()
/**
* A map from selectable keys to subscribed selectables.
*/
- internal val selectableMap: Map<Long, Selectable>
+ internal val selectableMap: LongObjectMap<Selectable>
get() = _selectableMap
/**
@@ -109,7 +112,7 @@
*/
internal var afterSelectableUnsubscribe: ((Long) -> Unit)? = null
- override var subselections: Map<Long, Selection> by mutableStateOf(emptyMap())
+ override var subselections: LongObjectMap<Selection> by mutableStateOf(emptyLongObjectMap())
override fun subscribe(selectable: Selectable): Selectable {
require(selectable.selectableId != SelectionRegistrar.InvalidSelectableId) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
index 8fad264..a32cf68 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
@@ -19,7 +19,6 @@
import androidx.compose.foundation.text.DefaultCursorThickness
import androidx.compose.foundation.text.Handle
import androidx.compose.foundation.text.HandleState
-import androidx.compose.foundation.text.InternalFoundationTextApi
import androidx.compose.foundation.text.LegacyTextFieldState
import androidx.compose.foundation.text.TextDragObserver
import androidx.compose.foundation.text.UndoManager
@@ -90,7 +89,6 @@
* The current transformed text from the [LegacyTextFieldState].
* The original text can be found in [value].
*/
- @OptIn(InternalFoundationTextApi::class)
internal val transformedText get() = state?.textDelegate?.text
/**
@@ -578,6 +576,23 @@
updateFloatingToolbar(show = false)
}
+ internal fun setSelectionPreviewHighlight(range: TextRange) {
+ state?.selectionPreviewHighlightRange = range
+ state?.deletionPreviewHighlightRange = TextRange.Zero
+ if (!range.collapsed) exitSelectionMode()
+ }
+
+ internal fun setDeletionPreviewHighlight(range: TextRange) {
+ state?.deletionPreviewHighlightRange = range
+ state?.selectionPreviewHighlightRange = TextRange.Zero
+ if (!range.collapsed) exitSelectionMode()
+ }
+
+ internal fun clearPreviewHighlight() {
+ state?.deletionPreviewHighlightRange = TextRange.Zero
+ state?.selectionPreviewHighlightRange = TextRange.Zero
+ }
+
/**
* The method for copying text.
*
@@ -793,7 +808,6 @@
* line, and the bottom is the bottom of the last selected line. The left is the leftmost
* handle's horizontal coordinates, and the right is the rightmost handle's coordinates.
*/
- @OptIn(InternalFoundationTextApi::class)
private fun getContentRect(): Rect {
// if it's stale layout, return empty Rect
state?.takeIf { !it.isLayoutResultStale }?.let {
@@ -1002,7 +1016,6 @@
/**
* @return the location of the magnifier relative to the inner text field coordinates
*/
-@OptIn(InternalFoundationTextApi::class)
internal fun calculateSelectionMagnifierCenterAndroid(
manager: TextFieldSelectionManager,
magnifierSize: IntSize
@@ -1040,7 +1053,14 @@
// Hide the magnifier when dragged too far (outside the horizontal bounds of how big the
// magnifier actually is). See
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/widget/Editor.java;l=5228-5231;drc=2fdb6bd709be078b72f011334362456bb758922c
- if ((dragX - centerX).absoluteValue > magnifierSize.width / 2) {
+ // Also check whether magnifierSize is calculated. A platform magnifier instance is not
+ // created until it's requested for the first time. So the size will only be calculated after we
+ // return a specified offset from this function.
+ // It is very unlikely that this behavior would cause a flicker since magnifier immediately
+ // shows up where the pointer is being dragged. The pointer needs to drag further than the half
+ // of magnifier's width to hide by the following logic.
+ if (magnifierSize != IntSize.Zero &&
+ (dragX - centerX).absoluteValue > magnifierSize.width / 2) {
return Offset.Unspecified
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionDelegate.kt
index 78d47d7..d05e481 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionDelegate.kt
@@ -42,8 +42,12 @@
// This happens if maxLines is set but the offset is on a line >= maxLines.
if (line >= textLayoutResult.lineCount) return Offset.Unspecified
- val x = textLayoutResult.getHorizontalPosition(offset, isStart, areHandlesCrossed)
- val y = textLayoutResult.getLineBottom(line)
+ val x = textLayoutResult
+ .getHorizontalPosition(offset, isStart, areHandlesCrossed)
+ .coerceIn(0f, textLayoutResult.size.width.toFloat())
+ val y = textLayoutResult
+ .getLineBottom(line)
+ .coerceIn(0f, textLayoutResult.size.height.toFloat())
return Offset(x, y)
}
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/DesktopCursorHandle.desktop.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/DesktopCursorHandle.desktop.kt
index 5262813..ef305ed 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/DesktopCursorHandle.desktop.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/DesktopCursorHandle.desktop.kt
@@ -16,15 +16,15 @@
package androidx.compose.foundation.text
+import androidx.compose.foundation.text.selection.OffsetProvider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.DpSize
@Composable
@Suppress("UNUSED_PARAMETER")
internal actual fun CursorHandle(
- handlePosition: Offset,
+ offsetProvider: OffsetProvider,
modifier: Modifier,
minTouchTargetSize: DpSize
) {
diff --git a/compose/integration-tests/demos/build.gradle b/compose/integration-tests/demos/build.gradle
index 1bfb611..5b3f0fa 100644
--- a/compose/integration-tests/demos/build.gradle
+++ b/compose/integration-tests/demos/build.gradle
@@ -31,6 +31,7 @@
implementation(project(":compose:foundation:foundation-layout"))
implementation(project(":compose:integration-tests:demos:common"))
implementation(project(":compose:material:material"))
+ implementation(project(":compose:material:material-icons-extended"))
implementation(project(":compose:material3:material3"))
implementation(project(":compose:runtime:runtime"))
implementation(project(":compose:ui:ui"))
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/AccessibilityNodeInspector.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/AccessibilityNodeInspector.kt
new file mode 100644
index 0000000..4c04bf5
--- /dev/null
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/AccessibilityNodeInspector.kt
@@ -0,0 +1,1377 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.demos
+
+import android.graphics.Matrix
+import android.graphics.Rect
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityNodeInfo
+import androidx.annotation.RequiresApi
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.expandVertically
+import androidx.compose.animation.shrinkVertically
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Arrangement.spacedBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.text.selection.SelectionContainer
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.AlertDialog
+import androidx.compose.material.Button
+import androidx.compose.material.Divider
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.darkColors
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.filled.ArrowDropDown
+import androidx.compose.material.icons.filled.Info
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collection.mutableVectorOf
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.graphics.ClipOp
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.graphics.drawscope.clipRect
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.toComposeIntRect
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
+import androidx.compose.ui.input.pointer.changedToUp
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.RootForTest
+import androidx.compose.ui.node.requireLayoutCoordinates
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTag
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.LinkAnnotation
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.text.withLink
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.offset
+import androidx.compose.ui.unit.round
+import androidx.compose.ui.unit.toOffset
+import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.util.fastFirstOrNull
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogProperties
+import androidx.compose.ui.window.Popup
+import androidx.compose.ui.window.PopupPositionProvider
+import androidx.compose.ui.window.PopupProperties
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import androidx.core.view.children
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+private const val InspectorButtonTestTag =
+ "androidx.compose.foundation.demos.AccessibilityNodeInspectorButton"
+
+/** The key used to read Compose testTag semantics properties from accessibility nodes' extras. */
+private const val TestTagExtrasKey = "androidx.compose.ui.semantics.testTag"
+
+private const val LogTag = "A11yNodeInspector"
+
+private val UnsupportedMessage =
+ "This tool is not supported on this device. AccessibilityNodeInfo objects are not readable " +
+ "by code in the same process without an accessibility service before API 34.\n\n" +
+ "This device is running API ${Build.VERSION.SDK_INT}."
+
+private const val UsageMessage =
+ "Drag anywhere to explore accessibility nodes.\n\n" +
+ "Release to view the node's properties and print the information to logcat " +
+ "(tagged \"$LogTag\").\n\n" +
+ "Go back to close inspector."
+
+/**
+ * A composable that, when touched or dragged, will immediately show an overlay on the current
+ * window that allows the user to interactively explore accessibility nodes and view their
+ * properties.
+ */
+@Composable
+fun AccessibilityNodeInspectorButton(
+ modifier: Modifier = Modifier,
+ content: @Composable () -> Unit
+) {
+ var active by remember { mutableStateOf(false) }
+ val state = rememberAccessibilityNodeInspectorState()
+ Box(
+ propagateMinConstraints = true,
+ modifier = modifier
+ // This node needs to have the same gesture modifier as the dedicated inspector overlay
+ // since when the button is dragged initially, the pointer events will all still be sent
+ // to the button, and not the overlay, even though the overlay will immediately be
+ // shown. Because node coordinates are all communicated in screen space, it doesn't
+ // actually matter which window accepts the pointer events.
+ .then(NodeSelectionGestureModifier(state, onDragStarted = { active = true }))
+ // Tag the button so the inspector can detect when the button itself is selected and
+ // show a help message.
+ .semantics(mergeDescendants = true) {
+ testTag = InspectorButtonTestTag
+ }
+ ) {
+ content()
+
+ if (active) {
+ if (Build.VERSION.SDK_INT >= 34) {
+ AccessibilityNodeInspector(
+ state = state,
+ onDismissRequest = { active = false }
+ )
+ } else {
+ AlertDialog(
+ onDismissRequest = { active = false },
+ title = { Text("Accessibility Node Inspector") },
+ text = { Text(UnsupportedMessage) },
+ buttons = {
+ Button(
+ onClick = { active = false },
+ modifier = Modifier
+ .padding(16.dp)
+ .fillMaxWidth()
+ ) {
+ Text("DISMISS")
+ }
+ }
+ )
+ }
+ }
+ }
+}
+
+/**
+ * Returns true if this [NodeInfo] or any of its ancestors represents an
+ * [AccessibilityNodeInspectorButton].
+ */
+private val NodeInfo.isInspectorButton: Boolean
+ get() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ visitSelfAndAncestors {
+ val testTag = AccessibilityNodeInfoHelper.readExtraData(
+ it.nodeInfo.unwrap(),
+ TestTagExtrasKey
+ )
+ if (testTag == InspectorButtonTestTag) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+// region Selection UI
+
+/**
+ * A popup that overlays another window and allows exploring its accessibility nodes by touch.
+ */
+@Composable
+private fun AccessibilityNodeInspector(
+ state: AccessibilityNodeInspectorState,
+ onDismissRequest: () -> Unit,
+) {
+ if (state.isReady) {
+ Popup(
+ popupPositionProvider = state,
+ properties = PopupProperties(
+ focusable = true,
+ excludeFromSystemGesture = false,
+ ),
+ onDismissRequest = onDismissRequest
+ ) {
+ Box(
+ propagateMinConstraints = true,
+ modifier = Modifier
+ .width { state.inspectorWindowSize.width }
+ .height { state.inspectorWindowSize.height }
+ ) {
+ // Selection UI and input handling.
+ Box(
+ Modifier
+ .then(NodeSelectionGestureModifier(state))
+ .then(DrawSelectionOverlayModifier(state))
+ )
+
+ state.nodeUnderInspection?.let {
+ if (it.isInspectorButton) {
+ // Don't use Surface here, it breaks touch input.
+ Text(
+ UsageMessage,
+ modifier = Modifier
+ .wrapContentSize()
+ .padding(16.dp)
+ .background(MaterialTheme.colors.surface)
+ .padding(16.dp)
+ )
+ } else {
+ InspectorNodeDetailsDialog(
+ leafNode = it,
+ onNodeClick = state::inspectNode,
+ onBack = onDismissRequest,
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * A modifier that draws the current selection of an [AccessibilityNodeInspectorState] in an
+ * [AccessibilityNodeInspector].
+ */
+private data class DrawSelectionOverlayModifier(
+ val state: AccessibilityNodeInspectorState
+) : ModifierNodeElement<DrawSelectionOverlayModifierNode>() {
+ override fun create(): DrawSelectionOverlayModifierNode =
+ DrawSelectionOverlayModifierNode(state)
+
+ override fun update(node: DrawSelectionOverlayModifierNode) {
+ check(node.state === state) { "Cannot change state" }
+ }
+
+ override fun InspectorInfo.inspectableProperties() {}
+}
+
+private class DrawSelectionOverlayModifierNode(
+ val state: AccessibilityNodeInspectorState
+) : Modifier.Node(), DrawModifierNode {
+ override fun ContentDrawScope.draw() {
+ val coords = requireLayoutCoordinates()
+ state.nodesUnderCursor.let { nodes ->
+ if (nodes.isNotEmpty()) {
+ val layerAlpha = 0.8f / nodes.size
+ nodes.fastForEach { node ->
+ val bounds = coords.screenToLocal(node.boundsInScreen)
+ clipRect(
+ left = bounds.left.toFloat(),
+ top = bounds.top.toFloat(),
+ right = bounds.right.toFloat(),
+ bottom = bounds.bottom.toFloat(),
+ clipOp = ClipOp.Difference
+ ) {
+ drawRect(Color.Black.copy(alpha = layerAlpha))
+ }
+ }
+ }
+ }
+
+ state.highlightedNode?.let { node ->
+ val lastBounds = coords.screenToLocal(node.boundsInScreen)
+ drawRect(
+ Color.Green,
+ style = Stroke(1.dp.toPx()),
+ topLeft = lastBounds.topLeft.toOffset(),
+ size = lastBounds.size.toSize()
+ )
+ }
+
+ state.selectionOffset.takeIf { it.isSpecified }?.let { screenOffset ->
+ val localOffset = coords.screenToLocal(screenOffset)
+ drawLine(
+ Color.Red,
+ start = Offset(0f, localOffset.y),
+ end = Offset(size.width, localOffset.y)
+ )
+ drawLine(
+ Color.Red,
+ start = Offset(localOffset.x, 0f),
+ end = Offset(localOffset.x, size.height)
+ )
+ }
+ }
+
+ private fun LayoutCoordinates.screenToLocal(rect: IntRect): IntRect {
+ return IntRect(
+ topLeft = screenToLocal(rect.topLeft.toOffset()).round(),
+ bottomRight = screenToLocal(rect.bottomRight.toOffset()).round(),
+ )
+ }
+}
+
+/**
+ * A modifier that accepts pointer input to select accessibility nodes in an
+ * [AccessibilityNodeInspectorState].
+ */
+private data class NodeSelectionGestureModifier(
+ val state: AccessibilityNodeInspectorState,
+ val onDragStarted: (() -> Unit)? = null,
+) : ModifierNodeElement<NodeSelectionGestureModifierNode>() {
+ override fun create(): NodeSelectionGestureModifierNode =
+ NodeSelectionGestureModifierNode(state, onDragStarted)
+
+ override fun update(node: NodeSelectionGestureModifierNode) {
+ check(node.state === state) { "Cannot change state" }
+ node.onDragStarted = onDragStarted
+ }
+
+ override fun InspectorInfo.inspectableProperties() {}
+}
+
+private class NodeSelectionGestureModifierNode(
+ val state: AccessibilityNodeInspectorState,
+ var onDragStarted: (() -> Unit)?,
+) : DelegatingNode() {
+
+ private val pass = PointerEventPass.Initial
+
+ @Suppress("unused")
+ private val inputNode = delegate(SuspendingPointerInputModifierNode {
+ // Detect drag gestures but without slop.
+ val layoutCoords = requireLayoutCoordinates()
+ awaitEachGesture {
+ try {
+ val firstChange = awaitFirstDown(pass = pass)
+ state.setNodeCursor(firstChange.position, layoutCoords)
+ onDragStarted?.invoke()
+ firstChange.consume()
+
+ while (true) {
+ val event = awaitPointerEvent(pass = pass)
+ event.changes.fastFirstOrNull { it.id == firstChange.id }?.let { change ->
+ if (change.changedToUp()) {
+ return@awaitEachGesture
+ } else {
+ state.setNodeCursor(change.position, layoutCoords)
+ }
+ }
+ }
+ } finally {
+ state.inspectNodeUnderCursor()
+ }
+ }
+ })
+}
+
+// endregion
+
+// region Details UI
+
+/**
+ * A dialog that shows all the properties of [leafNode] and all its ancestors and allows exploring
+ * them interactively.
+ */
+@Composable
+private fun InspectorNodeDetailsDialog(
+ leafNode: NodeInfo,
+ onNodeClick: (NodeInfo) -> Unit,
+ onBack: () -> Unit,
+) {
+ Dialog(
+ properties = DialogProperties(usePlatformDefaultWidth = false),
+ onDismissRequest = onBack
+ ) {
+ InspectorNodeDetails(
+ leafNode = leafNode,
+ onNodeClick = onNodeClick,
+ onBack = onBack
+ )
+ }
+}
+
+@Composable
+private fun InspectorNodeDetails(
+ leafNode: NodeInfo,
+ onNodeClick: (NodeInfo) -> Unit,
+ onBack: () -> Unit
+) {
+ MaterialTheme(colors = if (isSystemInDarkTheme()) darkColors() else lightColors()) {
+ val peekInteractionSource = remember { MutableInteractionSource() }
+ val peeking by peekInteractionSource.collectIsPressedAsState()
+ Surface(
+ modifier = Modifier
+ .padding(16.dp)
+ .alpha(if (peeking) 0f else 1f),
+ elevation = 4.dp
+ ) {
+ Column {
+ TopAppBar(
+ title = { NodeHeader(leafNode) },
+ navigationIcon = {
+ IconButton(onClick = onBack) {
+ Icon(
+ Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = null
+ )
+ }
+ },
+ actions = {
+ IconButton(onClick = {}, interactionSource = peekInteractionSource) {
+ Icon(Icons.Filled.Info, contentDescription = null)
+ }
+ }
+ )
+
+ NodeProperties(
+ node = leafNode,
+ onNodeClick = onNodeClick,
+ modifier = Modifier
+ .verticalScroll(rememberScrollState())
+ .padding(16.dp)
+ )
+ }
+ }
+ }
+}
+
+private fun NodeInfo.selfAndAncestorsToList() =
+ buildList { visitSelfAndAncestors(::add) }.asReversed()
+
+@Composable
+private fun NodeHeader(node: NodeInfo) {
+ Column {
+ val (nodeClassPackage, nodeClassName) = node.nodeInfo.parseClassPackageAndName()
+ Text(nodeClassName, fontWeight = FontWeight.Medium)
+ Text(
+ nodeClassPackage,
+ style = MaterialTheme.typography.caption,
+ modifier = Modifier.alpha(0.5f),
+ overflow = TextOverflow.Ellipsis,
+ softWrap = false,
+ )
+ }
+}
+
+@Composable
+private fun NodeProperties(
+ node: NodeInfo,
+ onNodeClick: (NodeInfo) -> Unit,
+ modifier: Modifier
+) {
+ SelectionContainer {
+ Column(modifier = modifier, verticalArrangement = spacedBy(8.dp)) {
+ NodeAncestorLinks(node, onNodeClick)
+
+ val properties = node.getProperties()
+ .mapValues { (_, v) ->
+ // Turn references to other nodes into links that actually open those nodes
+ // in the inspector.
+ if (v is AccessibilityNodeInfoCompat) {
+ nodeLinkRepresentation(
+ node = v,
+ onClick = { onNodeClick(v.toNodeInfo()) }
+ )
+ } else {
+ PropertyValueRepresentation(v)
+ }
+ }
+ .toList()
+ KeyValueView(elements = properties)
+ }
+ }
+}
+
+@Composable
+private fun NodeAncestorLinks(node: NodeInfo, onNodeClick: (NodeInfo) -> Unit) {
+ val ancestors = remember(node) { node.selfAndAncestorsToList().dropLast(1) }
+ if (ancestors.isNotEmpty()) {
+ val ancestorLinks = remember(ancestors) {
+ buildAnnotatedString {
+ ancestors.fastForEachIndexed { index, ancestorNode ->
+ withLink(LinkAnnotation.Clickable("ancestor") { onNodeClick(ancestorNode) }) {
+ append(ancestorNode.nodeInfo.parseClassPackageAndName().second)
+ }
+
+ if (index < ancestors.size - 1) {
+ append(" > ")
+ }
+ }
+ }
+ }
+ Text(ancestorLinks)
+ }
+}
+
+private fun nodeLinkRepresentation(
+ node: AccessibilityNodeInfoCompat,
+ onClick: () -> Unit
+) = PropertyValueRepresentation(
+ buildAnnotatedString {
+ withLink(LinkAnnotation.Clickable("node") { onClick() }) {
+ append(node.className)
+ }
+ }
+)
+
+/**
+ * Shows a table of keys and their values. Values are rendered using [PropertyValueRepresentation].
+ */
+@Composable
+private fun KeyValueView(elements: List<Pair<String, PropertyValueRepresentation>>) {
+ Column(verticalArrangement = spacedBy(8.dp)) {
+ elements.forEach { (name, valueRepresentation) ->
+ KeyValueRow(name, valueRepresentation)
+ }
+ }
+}
+
+/**
+ * A row inside a [KeyValueView] that shows a single key and its value. The value will be shown
+ * beside the row, if there's space, otherwise it will be placed below it.
+ */
+@Composable
+private fun KeyValueRow(name: String, valueRepresentation: PropertyValueRepresentation) {
+ KeyValueRowLayout(
+ contentPadding = 8.dp,
+ keyContent = {
+ Text(
+ name,
+ fontWeight = FontWeight.Medium,
+ style = MaterialTheme.typography.caption,
+ modifier = Modifier.alpha(0.5f)
+ )
+ },
+ valueContent = {
+ if (valueRepresentation.customRenderer != null) {
+ valueRepresentation.customRenderer.invoke()
+ } else {
+ Text(
+ valueRepresentation.text,
+ fontFamily = FontFamily.Monospace,
+ modifier = Modifier.horizontalScroll(rememberScrollState())
+ )
+ }
+ }
+ )
+}
+
+/**
+ * Places [keyContent] and [valueContent] on the same line if they both fit with [contentPadding]
+ * spacing, otherwise places [valueContent] below [keyContent] and indents it by [contentPadding].
+ * If [valueContent] wraps and fills all available space, a thin line is drawn in the margin to help
+ * visually track the nesting level.
+ */
+@Composable
+private inline fun KeyValueRowLayout(
+ contentPadding: Dp,
+ keyContent: @Composable RowScope.() -> Unit,
+ valueContent: @Composable RowScope.() -> Unit,
+) {
+ var nestingIndicator: Pair<Offset, Offset>? by remember { mutableStateOf(null) }
+
+ Layout(
+ modifier = Modifier.drawBehind {
+ nestingIndicator?.let { (start, end) ->
+ drawLine(
+ start = start,
+ end = end,
+ color = Color.Gray,
+ alpha = 0.3f,
+ strokeWidth = 1.dp.toPx(),
+ )
+ }
+ },
+ content = {
+ Row(content = keyContent)
+ Row(content = valueContent)
+ },
+ measurePolicy = { measurables, constraints ->
+ val contentPaddingPx = contentPadding.roundToPx()
+ val (keyMeasurable, valueMeasurable) = measurables
+ val keyConstraints = constraints.copy(minWidth = 0, minHeight = 0)
+ // contentPadding will either act as the spacing between items if they fit on the same
+ // line, or indent if content wraps, so inset the constraints either way.
+ val valueConstraints = constraints.copy(minWidth = 0, minHeight = 0)
+ .offset(horizontal = -contentPaddingPx)
+ val keyPlaceable = keyMeasurable.measure(keyConstraints)
+ val valuePlaceable = valueMeasurable.measure(valueConstraints)
+ val wrap =
+ keyPlaceable.width + contentPaddingPx + valuePlaceable.width > constraints.maxWidth
+
+ val totalWidth = constraints.maxWidth
+ val totalHeight = if (wrap) {
+ keyPlaceable.height + valuePlaceable.height
+ } else {
+ maxOf(keyPlaceable.height, valuePlaceable.height)
+ }
+
+ // Only draw the nesting indicator if the value filled its max width, which indicates it
+ // will probably be taller, and harder to track the start edge visually.
+ nestingIndicator = if (wrap && valuePlaceable.width == valueConstraints.maxWidth) {
+ Pair(
+ Offset(contentPaddingPx / 2f, keyPlaceable.height.toFloat()),
+ Offset(contentPaddingPx / 2f, totalHeight.toFloat())
+ )
+ } else {
+ null
+ }
+
+ layout(totalWidth, totalHeight) {
+ val valueX = totalWidth - valuePlaceable.width
+ if (wrap) {
+ // Arrange vertically.
+ keyPlaceable.placeRelative(0, 0)
+ valuePlaceable.placeRelative(valueX, keyPlaceable.height)
+ } else {
+ // Arrange horizontally.
+ val keyY = Alignment.CenterVertically.align(
+ size = keyPlaceable.height,
+ space = totalHeight
+ )
+ keyPlaceable.placeRelative(0, keyY)
+
+ val valueY = Alignment.CenterVertically.align(
+ size = valuePlaceable.height,
+ space = totalHeight
+ )
+ valuePlaceable.placeRelative(valueX, valueY)
+ }
+ }
+ }
+ )
+}
+
+/**
+ * A representation of an arbitrary value as a potentially-styled [AnnotatedString], and optionally
+ * also as a completely custom composable. To create an instance for standard types, call the
+ * [PropertyValueRepresentation] function.
+ */
+private data class PropertyValueRepresentation(
+ val text: AnnotatedString,
+ val customRenderer: (@Composable () -> Unit)? = null
+)
+
+private val ValueTypeTextStyle = TextStyle(fontFamily = FontFamily.Monospace)
+
+/**
+ * Creates a [PropertyValueRepresentation] appropriate for certain well-known types. For other types
+ * returns a representation that is just the result of the value's [toString].
+ */
+private fun PropertyValueRepresentation(value: Any?): PropertyValueRepresentation =
+ when (value) {
+ is CharSequence -> PropertyValueRepresentation(value.toFormattedDebugString())
+
+ is Iterable<*> -> {
+ val valueType = value.javaClass.canonicalName ?: value.javaClass.name
+ // No isEmpty on iterable.
+ if (!value.iterator().hasNext()) {
+ PropertyValueRepresentation(AnnotatedString("$valueType()"))
+ } else {
+ PropertyValueRepresentation(AnnotatedString(value.toString())) {
+ Column {
+ Text(valueType, style = ValueTypeTextStyle)
+ KeyValueView(value.mapIndexed { index, element ->
+ Pair("[$index]", PropertyValueRepresentation(element))
+ })
+ }
+ }
+ }
+ }
+
+ is Map<*, *> -> {
+ val valueType = value.javaClass.canonicalName ?: value.javaClass.name
+ if (value.isEmpty()) {
+ PropertyValueRepresentation(AnnotatedString("$valueType()"))
+ } else {
+ PropertyValueRepresentation(AnnotatedString(value.toString())) {
+ Column {
+ Text(valueType, style = ValueTypeTextStyle)
+ KeyValueView(value.entries.map { (key, value) ->
+ Pair(key.toString(), PropertyValueRepresentation(value))
+ })
+ }
+ }
+ }
+ }
+
+ is Bundle -> {
+ if (value.isEmpty) {
+ PropertyValueRepresentation(
+ AnnotatedString(
+ "empty Bundle",
+ SpanStyle(fontStyle = FontStyle.Italic)
+ )
+ )
+ } else {
+ PropertyValueRepresentation(AnnotatedString(value.toString())) {
+ KeyValueView(value.keySet().map { key ->
+ @Suppress("DEPRECATION")
+ val rawValue = value.get(key)
+ Pair(key, PropertyValueRepresentation(rawValue))
+ })
+ }
+ }
+ }
+
+ else -> PropertyValueRepresentation(AnnotatedString(value.toString()))
+ }
+
+/**
+ * Returns the package and simple name parts of a FQCN by splitting at the last '.' character.
+ */
+private fun AccessibilityNodeInfoCompat.parseClassPackageAndName(): Pair<String, String> {
+ val separatorIndex = className.indexOfLast { it == '.' }
+ return Pair(
+ className.substring(0, separatorIndex),
+ className.substring(separatorIndex + 1)
+ )
+}
+
+/**
+ * A column of expandable headers. Only one header can be expanded at a time. To create an item
+ * call [AccordionScope.item] in [content].
+ */
+@Composable
+private fun Accordion(
+ selectedIndex: Int,
+ onSelectIndex: (Int) -> Unit,
+ modifier: Modifier = Modifier,
+ content: AccordionScope.() -> Unit
+) {
+ Column(modifier) {
+ // Don't rebuild the items every time the selection changes.
+ val items by remember(content) { derivedStateOf { buildAccordionItems(content) } }
+ val isSelectedIndexValid = selectedIndex in items.indices
+ items.fastForEachIndexed { index, item ->
+ val isItemSelected = index == selectedIndex
+ AccordionItemView(
+ item = item,
+ headerHeight = 40.dp,
+ isExpanded = isItemSelected,
+ shrinkHeader = !isItemSelected && isSelectedIndexValid,
+ onHeaderClick = {
+ onSelectIndex(if (selectedIndex == index) -1 else index)
+ },
+ )
+ if (index < items.size - 1) {
+ Divider()
+ }
+ }
+ }
+}
+
+/**
+ * An item header and optionally-visible content inside an [Accordion]. Only intended to be called
+ * by [Accordion] itself.
+ */
+@Composable
+private fun AccordionItemView(
+ item: AccordionItem,
+ headerHeight: Dp,
+ isExpanded: Boolean,
+ shrinkHeader: Boolean,
+ onHeaderClick: () -> Unit
+) {
+ // Shrink collapsed headers to give more space to the expanded body.
+ val headerScale by animateFloatAsState(if (shrinkHeader) 0.8f else 1f, label = "headerScale")
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .height { (headerHeight * headerScale).roundToPx() }
+ .fillMaxWidth()
+ .selectable(selected = isExpanded, onClick = onHeaderClick)
+ .graphicsLayer {
+ scaleX = headerScale
+ scaleY = headerScale
+ transformOrigin = TransformOrigin(0f, 0.5f)
+ },
+ ) {
+ val iconRotation by animateFloatAsState(
+ if (isExpanded) 0f else -90f,
+ label = "iconRotation"
+ )
+ Icon(
+ Icons.Filled.ArrowDropDown,
+ contentDescription = null,
+ modifier = Modifier.graphicsLayer {
+ rotationZ = iconRotation
+ }
+ )
+ item.header()
+ }
+ AnimatedVisibility(
+ visible = isExpanded,
+ enter = expandVertically(expandFrom = Alignment.Top),
+ exit = shrinkVertically(shrinkTowards = Alignment.Top),
+ ) {
+ item.content()
+ }
+}
+
+private interface AccordionScope {
+ /**
+ * Creates an accordion item with a [header] that is always visible, and a [body] that is only
+ * visible when the item is expanded.
+ */
+ fun item(
+ header: @Composable () -> Unit,
+ body: @Composable () -> Unit
+ )
+}
+
+private data class AccordionItem(
+ val header: @Composable () -> Unit,
+ val content: @Composable () -> Unit
+)
+
+private fun buildAccordionItems(content: AccordionScope.() -> Unit): List<AccordionItem> {
+ return buildList {
+ content(object : AccordionScope {
+ override fun item(
+ header: @Composable () -> Unit,
+ body: @Composable () -> Unit
+ ) {
+ add(AccordionItem(header, body))
+ }
+ })
+ }
+}
+
+/**
+ * Sets [key] to [value] in this map if [value] is not [unspecifiedValue] (null by default).
+ */
+private fun MutableMap<String, Any?>.setIfSpecified(
+ key: String,
+ value: Any?,
+ unspecifiedValue: Any? = null
+) {
+ if (value != unspecifiedValue) {
+ set(key, value)
+ }
+}
+
+/**
+ * Sets [key] to [value] in this map if [value] is not [unspecifiedValue] (false by default).
+ */
+private fun MutableMap<String, Any?>.setIfSpecified(
+ key: String,
+ value: Boolean,
+ unspecifiedValue: Boolean = false
+) {
+ if (value != unspecifiedValue) {
+ set(key, value)
+ }
+}
+
+/**
+ * Sets [key] to [value] in this map if [value] is not [unspecifiedValue] (0 by default).
+ */
+private fun MutableMap<String, Any?>.setIfSpecified(
+ key: String,
+ value: Int,
+ unspecifiedValue: Int = 0
+) {
+ if (value != unspecifiedValue) {
+ set(key, value)
+ }
+}
+
+/**
+ * Returns an [AnnotatedString] that makes this [CharSequence] value easier to read for debugging.
+ * Wraps the value in stylized quote marks so empty strings are more clear, and replaces invisible
+ * control characters (e.g. `'\n'`) with their stylized literal escape sequences.
+ */
+private fun CharSequence.toFormattedDebugString(): AnnotatedString = buildAnnotatedString {
+ val quoteStyle = SpanStyle(
+ color = Color.Gray,
+ fontWeight = FontWeight.Bold
+ )
+ val specialStyle = SpanStyle(
+ color = Color.Red,
+ fontWeight = FontWeight.Bold,
+ )
+
+ withStyle(quoteStyle) { append('"') }
+
+ [email protected] { c ->
+ var formattedChar: String? = null
+ when (c) {
+ '\n' -> formattedChar = "\\n"
+ '\r' -> formattedChar = "\\r"
+ '\t' -> formattedChar = "\\t"
+ '\b' -> formattedChar = "\\b"
+ }
+ if (formattedChar != null) {
+ withStyle(specialStyle) {
+ append(formattedChar)
+ }
+ } else {
+ append(c)
+ }
+ }
+
+ withStyle(quoteStyle) { append('"') }
+}
+
+// endregion
+
+/**
+ * Like the standard [Modifier.width] modifier but the width is only calculated at measure time.
+ */
+private fun Modifier.width(calculateWidth: Density.() -> Int): Modifier =
+ layout { measurable, constraints ->
+ val calculatedWidth = calculateWidth()
+ val childConstraints = constraints.copy(
+ minWidth = calculatedWidth,
+ maxWidth = calculatedWidth
+ )
+ val placeable = measurable.measure(childConstraints)
+ layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
+ }
+
+/**
+ * Like the standard [Modifier.height] modifier but the height is only calculated at measure time.
+ */
+private fun Modifier.height(calculateHeight: Density.() -> Int): Modifier =
+ layout { measurable, constraints ->
+ val calculatedHeight = calculateHeight()
+ val childConstraints = constraints.copy(
+ minHeight = calculatedHeight,
+ maxHeight = calculatedHeight
+ )
+ val placeable = measurable.measure(childConstraints)
+ layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
+ }
+
+// region Accessibility node access
+
+/**
+ * Creates and remembers an [AccessibilityNodeInspectorState] for inspecting the nodes in the window
+ * hosting this composition.
+ */
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+private fun rememberAccessibilityNodeInspectorState(): AccessibilityNodeInspectorState {
+ val hostView = LocalView.current
+ val state = remember(hostView) { AccessibilityNodeInspectorState(hostView = hostView) }
+ LaunchedEffect(state) { state.runWhileDisplayed() }
+
+ DisposableEffect(hostView) {
+ val testRoot = hostView as RootForTest
+ onDispose {
+ testRoot.forceAccessibilityForTesting(false)
+ }
+ }
+ return state
+}
+
+/** State holder for an [AccessibilityNodeInspectorButton]. */
+private class AccessibilityNodeInspectorState(
+ private val hostView: View
+) : PopupPositionProvider,
+ View.OnLayoutChangeListener {
+
+ var inspectorWindowSize: IntSize by mutableStateOf(calculateInspectorWindowSize())
+ private set
+
+ private val service: InspectableTreeProvider =
+ if (Build.VERSION.SDK_INT >= 34) {
+ AccessibilityTreeInspectorApi34(hostView.rootView)
+ } else {
+ NoopTreeProvider
+ }
+
+ val isReady: Boolean by derivedStateOf {
+ inspectorWindowSize.width > 0 && inspectorWindowSize.height > 0
+ }
+
+ var selectionOffset: Offset by mutableStateOf(Offset.Unspecified)
+ private set
+
+ /**
+ * All the nodes that pass the hit test after a call to [setNodeCursor], or if a node is
+ * programmatically selected via [inspectNode] then that node and all its ancestors.
+ */
+ var nodesUnderCursor: List<NodeInfo> by mutableStateOf(emptyList())
+ private set
+
+ /**
+ * The node to highlight – during selection, this will be the node that will be opened in the
+ * inspector when the gesture is finished.
+ */
+ var highlightedNode: NodeInfo? by mutableStateOf(null)
+ private set
+
+ /**
+ * If non-null, the node being shown in the inspector.
+ */
+ var nodeUnderInspection: NodeInfo? by mutableStateOf(null)
+ private set
+
+ /**
+ * Temporarily select the node at [localOffset] in the window being inspected. This should be
+ * called while the user is dragging.
+ */
+ fun setNodeCursor(localOffset: Offset, layoutCoordinates: LayoutCoordinates) {
+ hideInspector()
+ val screenOffset = layoutCoordinates.localToScreen(localOffset)
+ selectionOffset = screenOffset
+ nodesUnderCursor = service.findNodesAt(screenOffset)
+ highlightedNode = nodesUnderCursor.lastOrNull()
+ }
+
+ /**
+ * Opens the node under the selection cursor in the inspector and dumps it to logcat.
+ */
+ fun inspectNodeUnderCursor() {
+ selectionOffset = Offset.Unspecified
+ nodeUnderInspection = highlightedNode?.also {
+ it.dumpToLog(tag = LogTag)
+ }
+ }
+
+ /**
+ * Highlights the given node in the selection popup, dumps it to logcat, and opens it in the
+ * inspector.
+ */
+ fun inspectNode(node: NodeInfo?) {
+ highlightedNode = node
+ nodesUnderCursor = node?.selfAndAncestorsToList() ?: emptyList()
+ nodeUnderInspection = node
+ node?.also {
+ it.dumpToLog(tag = LogTag)
+ }
+ }
+
+ /**
+ * Hides the inspector dialog to allow the user to select a different node.
+ */
+ fun hideInspector() {
+ nodeUnderInspection = null
+ }
+
+ /**
+ * Runs any coroutine effects the state holder requires while it's connected to some UI.
+ */
+ suspend fun runWhileDisplayed() {
+ service.initialize()
+
+ coroutineScope {
+ // Update the overlay window size when the target window is resized.
+ launch {
+ hostView.addOnLayoutChangeListener(this@AccessibilityNodeInspectorState)
+ try {
+ awaitCancellation()
+ } finally {
+ hostView.removeOnLayoutChangeListener(this@AccessibilityNodeInspectorState)
+ }
+ }
+ }
+ }
+
+ override fun onLayoutChange(
+ v: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ inspectorWindowSize = calculateInspectorWindowSize()
+ }
+
+ override fun calculatePosition(
+ anchorBounds: IntRect,
+ windowSize: IntSize,
+ layoutDirection: LayoutDirection,
+ popupContentSize: IntSize
+ ): IntOffset = IntOffset.Zero
+
+ private fun calculateInspectorWindowSize(): IntSize {
+ return Rect().also {
+ hostView.getWindowVisibleDisplayFrame(it)
+ }.let { IntSize(it.width(), it.height()) }
+ }
+}
+
+private data class NodeInfo(
+ val nodeInfo: AccessibilityNodeInfoCompat,
+ val boundsInScreen: IntRect,
+)
+
+/** Returns a map with all the inspectable properties of this [NodeInfo]. */
+private fun NodeInfo.getProperties(): Map<String, Any?> = buildMap {
+ val node = nodeInfo
+ // Don't render className, it's in the title.
+ setIfSpecified("packageName", node.packageName)
+ setIfSpecified("boundsInScreen", Rect().also(node::getBoundsInScreen))
+ setIfSpecified("boundsInWindow", Rect().also(node::getBoundsInWindow))
+ setIfSpecified("viewIdResourceName", node.viewIdResourceName)
+ setIfSpecified("uniqueId", node.uniqueId)
+ setIfSpecified("text", node.text)
+ setIfSpecified("textSelectionStart", node.textSelectionStart, unspecifiedValue = -1)
+ setIfSpecified("textSelectionEnd", node.textSelectionEnd, unspecifiedValue = -1)
+ setIfSpecified("contentDescription", node.contentDescription)
+ setIfSpecified("collectionInfo", node.collectionInfo)
+ setIfSpecified("collectionItemInfo", node.collectionItemInfo)
+ setIfSpecified("containerTitle", node.containerTitle)
+ setIfSpecified("childCount", node.childCount)
+ setIfSpecified("drawingOrder", node.drawingOrder)
+ setIfSpecified("error", node.error)
+ setIfSpecified("hintText", node.hintText)
+ setIfSpecified("inputType", node.inputType)
+ setIfSpecified("isAccessibilityDataSensitive", node.isAccessibilityDataSensitive)
+ setIfSpecified("isAccessibilityFocused", node.isAccessibilityFocused)
+ setIfSpecified("isCheckable", node.isCheckable)
+ setIfSpecified("isChecked", node.isChecked)
+ setIfSpecified("isClickable", node.isClickable)
+ setIfSpecified("isLongClickable", node.isLongClickable)
+ setIfSpecified("isContextClickable", node.isContextClickable)
+ setIfSpecified("isContentInvalid", node.isContentInvalid)
+ setIfSpecified("isDismissable", node.isDismissable)
+ setIfSpecified("isEditable", node.isEditable)
+ setIfSpecified("isEnabled", node.isEnabled, unspecifiedValue = true)
+ setIfSpecified("isFocusable", node.isFocusable)
+ setIfSpecified("isFocused", node.isFocused)
+ setIfSpecified("isGranularScrollingSupported", node.isGranularScrollingSupported)
+ setIfSpecified("isHeading", node.isHeading)
+ set("isImportantForAccessibility", node.isImportantForAccessibility)
+ setIfSpecified("isMultiLine", node.isMultiLine)
+ setIfSpecified("isPassword", node.isPassword)
+ setIfSpecified("isScreenReaderFocusable", node.isScreenReaderFocusable)
+ setIfSpecified("isScrollable", node.isScrollable)
+ setIfSpecified("isSelected", node.isSelected)
+ setIfSpecified("isShowingHintText", node.isShowingHintText)
+ setIfSpecified("isTextEntryKey", node.isTextEntryKey)
+ setIfSpecified("isTextSelectable", node.isTextSelectable)
+ setIfSpecified("isVisibleToUser", node.isVisibleToUser, unspecifiedValue = true)
+ setIfSpecified("labelFor", node.labelFor)
+ setIfSpecified("labeledBy", node.labeledBy)
+ setIfSpecified("liveRegion", node.liveRegion)
+ setIfSpecified("maxTextLength", node.maxTextLength, unspecifiedValue = -1)
+ setIfSpecified("movementGranularities", node.movementGranularities)
+ setIfSpecified("paneTitle", node.paneTitle)
+ setIfSpecified("rangeInfo", node.rangeInfo)
+ setIfSpecified("roleDescription", node.roleDescription)
+ setIfSpecified("stateDescription", node.stateDescription)
+ setIfSpecified("tooltipText", node.tooltipText)
+ setIfSpecified("touchDelegateInfo", node.touchDelegateInfo)
+ setIfSpecified("windowId", node.windowId, unspecifiedValue = -1)
+ setIfSpecified("canOpenPopup", node.canOpenPopup())
+ setIfSpecified(
+ "hasRequestInitialAccessibilityFocus",
+ node.hasRequestInitialAccessibilityFocus()
+ )
+ setIfSpecified("extras", node.extrasWithoutExtraData)
+ setIfSpecified("extraRenderingInfo", node.extraRenderingInfo)
+
+ if (Build.VERSION.SDK_INT >= 26 && node.availableExtraData.isNotEmpty()) {
+ val extraData = mutableMapOf<String, Any?>()
+ node.availableExtraData.forEach { key ->
+ extraData[key] = AccessibilityNodeInfoHelper.readExtraData(node.unwrap(), key)
+ }
+ setIfSpecified("extraData (from availableExtraData)", extraData)
+ }
+
+ setIfSpecified("traversalBefore", node.traversalBefore)
+ setIfSpecified("traversalAfter", node.traversalAfter)
+}
+
+/**
+ * Returns the extras bundle, but without any keys from
+ * [AccessibilityNodeInfoCompat.getAvailableExtraData], since those are reported separately.
+ */
+private val AccessibilityNodeInfoCompat.extrasWithoutExtraData: Bundle
+ get() {
+ val extras = Bundle(extras)
+ availableExtraData.forEach {
+ extras.remove(it)
+ }
+ return extras
+ }
+
+/** Class verification helper for reading extras data from an [AccessibilityNodeInfo]. */
+@RequiresApi(26)
+private object AccessibilityNodeInfoHelper {
+ fun readExtraData(
+ node: AccessibilityNodeInfo,
+ key: String
+ ): Any? {
+ if (key in node.availableExtraData && node.refreshWithExtraData(key, Bundle())) {
+ @Suppress("DEPRECATION")
+ return node.extras.get(key)
+ } else {
+ return null
+ }
+ }
+}
+
+private interface InspectableTreeProvider {
+ fun initialize() {}
+ fun findNodesAt(screenOffset: Offset): List<NodeInfo>
+}
+
+private object NoopTreeProvider : InspectableTreeProvider {
+ override fun findNodesAt(screenOffset: Offset): List<NodeInfo> = emptyList()
+}
+
+@RequiresApi(34)
+private class AccessibilityTreeInspectorApi34(
+ private val rootView: View
+) : InspectableTreeProvider {
+
+ private val matrixCache = Matrix()
+
+ @OptIn(ExperimentalComposeUiApi::class)
+ override fun initialize() {
+ // This will call setQueryableFromApp process, which enables accessibility on the platform,
+ // which allows us to tell compose views to force accessibility support. This is required
+ // for certain fields, such as traversal before/after, to be populated.
+ rootView.createNodeInfo()
+ rootView.visitViewAndChildren { view ->
+ (view as? RootForTest)?.forceAccessibilityForTesting(true)
+ true
+ }
+ }
+
+ override fun findNodesAt(screenOffset: Offset): List<NodeInfo> {
+ rootView.transformMatrixToLocal(matrixCache)
+
+ val nodes = mutableListOf<NodeInfo>()
+ val rootInfo = rootView.createNodeInfo()
+ rootInfo.visitNodeAndChildren { node ->
+ if (node.hitTest(screenOffset)) {
+ nodes += node
+ true
+ } else {
+ false
+ }
+ }
+ return nodes
+ }
+
+ private fun NodeInfo.hitTest(screenOffset: Offset): Boolean {
+ return boundsInScreen.contains(screenOffset.round())
+ }
+
+ private inline fun View.visitViewAndChildren(visitor: (View) -> Boolean) {
+ val queue = mutableVectorOf(this)
+ while (queue.isNotEmpty()) {
+ val current = queue.removeAt(queue.lastIndex)
+ val visitChildren = visitor(current)
+ if (visitChildren && current is ViewGroup) {
+ for (child in current.children) {
+ queue += child
+ }
+ }
+ }
+ }
+
+ private inline fun NodeInfo.visitNodeAndChildren(visitor: (NodeInfo) -> Boolean) {
+ val queue = mutableVectorOf(this)
+ while (queue.isNotEmpty()) {
+ val current = queue.removeAt(queue.lastIndex)
+ val visitChildren = visitor(current)
+ if (visitChildren) {
+ for (i in 0 until current.nodeInfo.childCount) {
+ queue += current.nodeInfo.getChild(i).toNodeInfo()
+ }
+ }
+ }
+ }
+
+ private fun View.createNodeInfo(): NodeInfo {
+ val rawNodeInfo = createAccessibilityNodeInfo()
+ val nodeInfoCompat = AccessibilityNodeInfoCompat.wrap(rawNodeInfo)
+ rawNodeInfo.setQueryFromAppProcessEnabled(this, true)
+ return nodeInfoCompat.toNodeInfo()
+ }
+}
+
+private fun AccessibilityNodeInfoCompat.toNodeInfo(): NodeInfo = NodeInfo(
+ nodeInfo = this,
+ boundsInScreen = Rect().also(::getBoundsInScreen).toComposeIntRect(),
+)
+
+private fun NodeInfo.dumpToLog(tag: String) {
+ val indent = " "
+ var depth = 0
+ visitSelfAndAncestors { node ->
+ Log.d(tag, indent.repeat(depth) + node.nodeInfo.unwrap().toString())
+ depth++
+ }
+}
+
+private inline fun NodeInfo.visitSelfAndAncestors(block: (NodeInfo) -> Unit) {
+ var node: NodeInfo? = this
+ while (node != null) {
+ block(node)
+ node = node.parent
+ }
+}
+
+private val NodeInfo.parent: NodeInfo?
+ get() = nodeInfo.parent?.toNodeInfo()
+
+// endregion
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
index 7dbf951..2643314 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
@@ -40,6 +40,7 @@
import androidx.compose.material.LocalContentColor
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.filled.Api
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -101,8 +102,7 @@
onEndFiltering = onEndFiltering
)
},
- modifier = Modifier
- .nestedScroll(scrollBehavior.nestedScrollConnection)
+ modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding ->
val modifier = Modifier
// as scaffold currently doesn't consume - consume what's needed
@@ -246,6 +246,7 @@
scrollBehavior = scrollBehavior,
navigationIcon = navigationIcon,
actions = {
+ AppBarIcons.AccessibilityNodeInspector()
AppBarIcons.Filter(onClick = onStartFiltering)
AppBarIcons.Settings(onClick = launchSettings)
}
@@ -262,6 +263,15 @@
}
@Composable
+ fun AccessibilityNodeInspector() {
+ AccessibilityNodeInspectorButton {
+ IconButton(onClick = {}) {
+ Icon(Icons.Filled.Api, contentDescription = null)
+ }
+ }
+ }
+
+ @Composable
fun Filter(onClick: () -> Unit) {
IconButton(modifier = Modifier.testTag(Tags.FilterButton), onClick = onClick) {
Icon(Icons.Filled.Search, null)
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
index a905398..cccbd34 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -49,6 +49,19 @@
</intent-filter>
</activity>
<activity
+ android:name=".StaticScrollingContentWithChromeInitialCompositionActivity"
+ android:label="C StaticScrollingWithChrome Init"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="androidx.compose.integration.macrobenchmark.target.STATIC_SCROLLING_CONTENT_WITH_CHROME_INITIAL_COMPOSITION_ACTIVITY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="androidx.compose.integration.macrobenchmark.target.STATIC_SCROLLING_CONTENT_WITH_CHROME_FIRST_FRAME_ACTIVITY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <activity
android:name=".TrivialStartupTracingActivity"
android:label="C TrivialTracing"
android:exported="true">
@@ -228,7 +241,8 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity
+
+ <activity
android:name=".VectorsListActivity"
android:label="Compose vectors list"
android:exported="true">
@@ -252,6 +266,14 @@
</intent-filter>
<intent-filter>
<action android:name="androidx.compose.integration.macrobenchmark.target.CROSSFADE_ACTIVITY" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".PagerOfLazyGridActivity"
+ android:exported="true"
+ android:theme="@style/Theme.AppCompat">
+ <intent-filter>
+ <action android:name="androidx.compose.integration.macrobenchmark.target.PAGER_LAZYGRID_ACTIVITY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerOfLazyGridActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerOfLazyGridActivity.kt
new file mode 100644
index 0000000..a18a1be
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerOfLazyGridActivity.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark.target
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PagerState
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.material.Button
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import kotlinx.coroutines.launch
+
+class PagerOfLazyGridActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val pageCount = intent.getIntExtra(PageCount, 100)
+ val gridItemCount = intent.getIntExtra(GridItemCount, 100)
+
+ setContent {
+ MaterialTheme {
+ HorizontalPagerOfLazyGrid(pageCount, gridItemCount)
+ }
+ }
+
+ launchIdlenessTracking()
+ }
+
+ companion object {
+ const val PageCount = "PAGE_COUNT"
+ const val GridItemCount = "GRID_ITEM_COUNT"
+ }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun HorizontalPagerOfLazyGrid(pages: Int = 100, gridItems: Int = 100) {
+ val pagerState: PagerState = rememberPagerState(initialPage = 1) { pages }
+ val coroutineScope = rememberCoroutineScope()
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colors.background)
+ ) {
+ Button(onClick = {
+ coroutineScope.launch {
+ pagerState.animateScrollToPage(pagerState.currentPage + 1)
+ }
+ }) {
+ Text("Next")
+ }
+
+ HorizontalPager(
+ state = pagerState,
+ modifier = Modifier.semantics { contentDescription = "Pager" }
+ ) { page: Int ->
+ Grid(gridItems, page)
+ }
+ }
+}
+
+@Composable
+private fun Grid(itemCount: Int, pageNum: Int) {
+ val text = remember(pageNum) { "Hello + $pageNum" }
+ LazyVerticalGrid(
+ modifier = Modifier.fillMaxSize(),
+ columns = GridCells.Fixed(3),
+ ) {
+ items(itemCount, contentType = { "cell" }) { _ ->
+ Button(onClick = {}) {
+ Text(text = text)
+ }
+ }
+ }
+}
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/StaticScrollingContentWithChromeInitialCompositionActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/StaticScrollingContentWithChromeInitialCompositionActivity.kt
new file mode 100644
index 0000000..87810a2
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/StaticScrollingContentWithChromeInitialCompositionActivity.kt
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark.target
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.BottomNavigation
+import androidx.compose.material.BottomNavigationItem
+import androidx.compose.material.Button
+import androidx.compose.material.Icon
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.Info
+import androidx.compose.material.icons.filled.MoreVert
+import androidx.compose.material.icons.filled.Person
+import androidx.compose.material.icons.filled.Place
+import androidx.compose.material.icons.filled.Star
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.trace
+
+class StaticScrollingContentWithChromeInitialCompositionActivity : ComponentActivity() {
+
+ private val onlyPerformComposition: Boolean
+ get() = intent.action == "androidx.compose.integration.macrobenchmark.target" +
+ ".STATIC_SCROLLING_CONTENT_WITH_CHROME_INITIAL_COMPOSITION_ACTIVITY"
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ if (onlyPerformComposition) {
+ ComposeOnlyLayout {
+ StaticScrollingContentWithChrome(
+ modifier = Modifier
+ .onPlaced { _ ->
+ throw RuntimeException(
+ "Content was placed, but should only be composed"
+ )
+ }
+ .drawWithContent {
+ throw RuntimeException(
+ "Content was drawn, but should only be composed"
+ )
+ }
+ )
+ }
+ } else {
+ StaticScrollingContentWithChrome()
+ }
+ }
+ }
+}
+
+/**
+ * A layout that will compose all of the [content], but will not place
+ * (and therefore not layout or draw) any of its children.
+ *
+ * This is useful for this benchmark as we care about the composition time. A major limitation
+ * of this approach is that any content in a SubcomposeLayout will not be composed
+ * and will not contribute to the overall measured time of this test.
+ */
+@Composable
+private fun ComposeOnlyLayout(
+ content: @Composable () -> Unit
+) {
+ Layout(content) { _, _ -> layout(0, 0) {} }
+}
+
+@Preview
+@Composable
+private fun StaticScrollingContentWithChrome(
+ modifier: Modifier = Modifier
+) = trace(sectionName = "StaticScrollingContentWithChrome") {
+ Column(modifier) {
+ TopBar()
+ ScrollingContent(modifier = Modifier.weight(1f))
+ BottomBar()
+ }
+}
+
+@Composable
+private fun TopBar(modifier: Modifier = Modifier) {
+ TopAppBar(
+ modifier = modifier,
+ title = {
+ Column {
+ Text(
+ "Initial Composition Macrobench",
+ style = MaterialTheme.typography.subtitle1,
+ maxLines = 1
+ )
+ Text(
+ "Static Scrolling Content w/ Chrome",
+ style = MaterialTheme.typography.caption,
+ maxLines = 1
+ )
+ }
+ },
+ navigationIcon = {
+ Button(onClick = {}) {
+ Icon(Icons.Default.Close, "Dismiss")
+ }
+ },
+ actions = {
+ Button(onClick = {}) {
+ Icon(Icons.Default.MoreVert, "Actions")
+ }
+ }
+ )
+}
+
+@Composable
+private fun BottomBar(modifier: Modifier = Modifier) {
+ BottomNavigation(modifier = modifier) {
+ BottomNavigationItem(
+ selected = true,
+ onClick = {},
+ icon = { Icon(Icons.Default.Home, "Home") }
+ )
+ BottomNavigationItem(
+ selected = false,
+ onClick = {},
+ icon = { Icon(Icons.Default.Add, "Add") }
+ )
+ }
+}
+
+@Composable
+private fun ScrollingContent(modifier: Modifier = Modifier) {
+ Column(
+ modifier
+ .fillMaxSize()
+ .verticalScroll(rememberScrollState())
+ .padding(vertical = 16.dp)
+ ) {
+ Item(
+ color = Color.DarkGray,
+ icon = Icons.Filled.Info,
+ modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .aspectRatio(16f / 9f)
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(16.dp))
+ )
+
+ repeat(5) { iteration ->
+ CardGroup(
+ title = "Group ${4 * iteration}",
+ groupIcon = Icons.Filled.Person,
+ groupColor = Color(0xFF1967D2)
+ )
+
+ CardGroup(
+ title = "Group ${4 * iteration + 1}",
+ groupIcon = Icons.Filled.Favorite,
+ groupColor = Color(0xFFC5221F)
+ )
+
+ CardGroup(
+ title = "Group ${4 * iteration + 2}",
+ groupIcon = Icons.Filled.Star,
+ groupColor = Color(0xFFF29900)
+ )
+
+ CardGroup(
+ title = "Group ${4 * iteration + 3}",
+ groupIcon = Icons.Filled.Place,
+ groupColor = Color(0xFF188038)
+ )
+ }
+ }
+}
+
+@Composable
+private fun CardGroup(
+ title: String,
+ groupIcon: ImageVector,
+ groupColor: Color,
+ modifier: Modifier = Modifier,
+ count: Int = 10
+) {
+ Column(
+ modifier = modifier
+ ) {
+ Text(
+ title,
+ style = MaterialTheme.typography.h6,
+ modifier = Modifier.padding(16.dp)
+ )
+
+ Row(
+ modifier = Modifier
+ .horizontalScroll(rememberScrollState())
+ .padding(horizontal = 12.dp)
+ ) {
+ repeat(count) {
+ Item(
+ color = groupColor,
+ icon = groupIcon,
+ modifier = Modifier
+ .padding(horizontal = 4.dp)
+ .size(64.dp)
+ .clip(RoundedCornerShape(4.dp))
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun Item(
+ color: Color,
+ icon: ImageVector,
+ modifier: Modifier = Modifier
+) {
+ Box(
+ modifier = modifier.background(color),
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(icon, null, tint = Color.White)
+ }
+}
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt
index 003b307..c070d43 100644
--- a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt
@@ -19,6 +19,8 @@
import android.content.Intent
import android.graphics.Point
import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
import androidx.benchmark.macro.FrameTimingMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.platform.app.InstrumentationRegistry
@@ -41,11 +43,12 @@
device = UiDevice.getInstance(instrumentation)
}
+ @OptIn(ExperimentalMetricApi::class)
@Test
fun start() {
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
- metrics = listOf(FrameTimingMetric()),
+ metrics = listOf(FrameTimingMetric(), FrameTimingGfxInfoMetric()),
compilationMode = CompilationMode.Full(),
iterations = 8,
setupBlock = {
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/PagerOfLazyGridBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/PagerOfLazyGridBenchmark.kt
new file mode 100644
index 0000000..91a4b14
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/PagerOfLazyGridBenchmark.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark
+
+import android.content.Intent
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import androidx.testutils.createCompilationParams
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class PagerOfLazyGridBenchmark(
+ private val compilationMode: CompilationMode
+) {
+ @get:Rule
+ val benchmarkRule = MacrobenchmarkRule()
+
+ private lateinit var device: UiDevice
+
+ @Before
+ fun setUp() {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ device = UiDevice.getInstance(instrumentation)
+ }
+
+ @Test
+ fun scroll() {
+ benchmarkRule.measureRepeated(
+ packageName = PackageName,
+ metrics = listOf(FrameTimingMetric()),
+ compilationMode = compilationMode,
+ startupMode = StartupMode.WARM,
+ iterations = 10,
+ setupBlock = {
+ val intent = Intent()
+ intent.action = Action
+ startActivityAndWait(intent)
+ }
+ ) {
+ val nextButton = device.findObject(By.text(NextDescription))
+ repeat(3) {
+ nextButton.click()
+ device.wait(Until.findObject(By.desc(ComposeIdle)), 3000)
+ }
+ }
+ }
+
+ companion object {
+ private const val PackageName = "androidx.compose.integration.macrobenchmark.target"
+ private const val Action =
+ "androidx.compose.integration.macrobenchmark.target.PAGER_LAZYGRID_ACTIVITY"
+ private const val ComposeIdle = "COMPOSE-IDLE"
+ private const val NextDescription = "Next"
+
+ @Parameterized.Parameters(name = "compilation={0}")
+ @JvmStatic
+ fun parameters() = createCompilationParams()
+ }
+}
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/StaticScrollingContentWithChromeInitialCompositionBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/StaticScrollingContentWithChromeInitialCompositionBenchmark.kt
new file mode 100644
index 0000000..b573368
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/StaticScrollingContentWithChromeInitialCompositionBenchmark.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark
+
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.testutils.createStartupCompilationParams
+import androidx.testutils.measureStartup
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class StaticScrollingContentWithChromeInitialCompositionBenchmark(
+ private val startupMode: StartupMode,
+ private val compilationMode: CompilationMode
+) {
+ @get:Rule
+ val benchmarkRule = MacrobenchmarkRule()
+
+ @Test
+ fun initialComposition() = benchmarkRule.measureStartup(
+ compilationMode = compilationMode,
+ startupMode = startupMode,
+ packageName = "androidx.compose.integration.macrobenchmark.target"
+ ) {
+ action = "androidx.compose.integration.macrobenchmark.target" +
+ ".STATIC_SCROLLING_CONTENT_WITH_CHROME_INITIAL_COMPOSITION_ACTIVITY"
+ }
+
+ @Test
+ fun firstFrame() = benchmarkRule.measureStartup(
+ compilationMode = compilationMode,
+ startupMode = startupMode,
+ packageName = "androidx.compose.integration.macrobenchmark.target"
+ ) {
+ action = "androidx.compose.integration.macrobenchmark.target" +
+ ".STATIC_SCROLLING_CONTENT_WITH_CHROME_FIRST_FRAME_ACTIVITY"
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "startup={0},compilation={1}")
+ @JvmStatic
+ fun parameters() = createStartupCompilationParams()
+ }
+}
diff --git a/compose/material/material-icons-core/build.gradle b/compose/material/material-icons-core/build.gradle
index 8931a2a..f720873 100644
--- a/compose/material/material-icons-core/build.gradle
+++ b/compose/material/material-icons-core/build.gradle
@@ -110,7 +110,7 @@
androidx {
name = "Compose Material Icons Core"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.COMPOSE
inceptionYear = "2020"
description = "Compose Material Design core icons. This module contains the most commonly used set of Material icons."
diff --git a/compose/material/material-icons-extended/build.gradle b/compose/material/material-icons-extended/build.gradle
index 568f7f0..324023b 100644
--- a/compose/material/material-icons-extended/build.gradle
+++ b/compose/material/material-icons-extended/build.gradle
@@ -124,7 +124,7 @@
androidx {
name = "Compose Material Icons Extended"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.COMPOSE
// This module has a large number (5000+) of generated source files and so doc generation /
// API tracking will simply take too long
diff --git a/compose/material/material-navigation/build.gradle b/compose/material/material-navigation/build.gradle
index 179c44c..1a55ed7 100644
--- a/compose/material/material-navigation/build.gradle
+++ b/compose/material/material-navigation/build.gradle
@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-import androidx.build.Publish
-import androidx.build.RunApiTasks
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -41,7 +39,7 @@
androidx {
name = "Compose Material Navigation"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.COMPOSE
inceptionYear = "2024"
description = "Compose Material integration with Navigation"
diff --git a/compose/material/material-ripple/build.gradle b/compose/material/material-ripple/build.gradle
index 7cbf897..8ca7ece 100644
--- a/compose/material/material-ripple/build.gradle
+++ b/compose/material/material-ripple/build.gradle
@@ -117,7 +117,7 @@
androidx {
name = "Compose Material Ripple"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.COMPOSE
inceptionYear = "2020"
description = "Material ripple used to build interactive components"
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index 7c87844..004b759 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -646,12 +646,10 @@
}
@SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class RippleConfiguration {
- ctor public RippleConfiguration(optional boolean isEnabled, optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
+ ctor public RippleConfiguration(optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
method public long getColor();
method public androidx.compose.material.ripple.RippleAlpha? getRippleAlpha();
- method public boolean isEnabled();
property public final long color;
- property public final boolean isEnabled;
property public final androidx.compose.material.ripple.RippleAlpha? rippleAlpha;
}
@@ -662,11 +660,11 @@
}
public final class RippleKt {
- method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.RippleConfiguration> getLocalRippleConfiguration();
+ method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.RippleConfiguration?> getLocalRippleConfiguration();
method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalUseFallbackRippleImplementation();
method @androidx.compose.runtime.Stable public static androidx.compose.foundation.IndicationNodeFactory ripple(androidx.compose.ui.graphics.ColorProducer color, optional boolean bounded, optional float radius);
method @androidx.compose.runtime.Stable public static androidx.compose.foundation.IndicationNodeFactory ripple(optional boolean bounded, optional float radius, optional long color);
- property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.RippleConfiguration> LocalRippleConfiguration;
+ property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.RippleConfiguration?> LocalRippleConfiguration;
property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalUseFallbackRippleImplementation;
}
@@ -912,8 +910,10 @@
@androidx.compose.runtime.Immutable public final class TextFieldDefaults {
method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void BorderBox(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
- method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
- method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
+ method @Deprecated @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
+ method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, 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.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
+ method @Deprecated @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
+ method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, 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.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
method public float getFocusedBorderThickness();
method public float getMinHeight();
method public float getMinWidth();
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index 7c87844..004b759 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -646,12 +646,10 @@
}
@SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Immutable public final class RippleConfiguration {
- ctor public RippleConfiguration(optional boolean isEnabled, optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
+ ctor public RippleConfiguration(optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
method public long getColor();
method public androidx.compose.material.ripple.RippleAlpha? getRippleAlpha();
- method public boolean isEnabled();
property public final long color;
- property public final boolean isEnabled;
property public final androidx.compose.material.ripple.RippleAlpha? rippleAlpha;
}
@@ -662,11 +660,11 @@
}
public final class RippleKt {
- method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.RippleConfiguration> getLocalRippleConfiguration();
+ method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.RippleConfiguration?> getLocalRippleConfiguration();
method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalUseFallbackRippleImplementation();
method @androidx.compose.runtime.Stable public static androidx.compose.foundation.IndicationNodeFactory ripple(androidx.compose.ui.graphics.ColorProducer color, optional boolean bounded, optional float radius);
method @androidx.compose.runtime.Stable public static androidx.compose.foundation.IndicationNodeFactory ripple(optional boolean bounded, optional float radius, optional long color);
- property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.RippleConfiguration> LocalRippleConfiguration;
+ property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material.RippleConfiguration?> LocalRippleConfiguration;
property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalUseFallbackRippleImplementation;
}
@@ -912,8 +910,10 @@
@androidx.compose.runtime.Immutable public final class TextFieldDefaults {
method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void BorderBox(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
- method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
- method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
+ method @Deprecated @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
+ method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, 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.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> border);
+ method @Deprecated @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
+ method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, 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.material.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding);
method public float getFocusedBorderThickness();
method public float getMinHeight();
method public float getMinWidth();
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index 97ec8c8..31c1ece 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -148,7 +148,7 @@
androidx {
name = "Compose Material Components"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.COMPOSE
inceptionYear = "2018"
description = "Compose Material Design Components library"
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TextFieldDecorationBoxDemos.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TextFieldDecorationBoxDemos.kt
index b6d93db..1144cc1 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TextFieldDecorationBoxDemos.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/TextFieldDecorationBoxDemos.kt
@@ -23,7 +23,6 @@
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
@@ -131,10 +130,6 @@
onValueChange = { text = it },
modifier = Modifier
.indicatorLine(enabled, false, interactionSource, TextFieldDefaults.textFieldColors())
- .background(
- TextFieldDefaults.textFieldColors().backgroundColor(enabled).value,
- TextFieldDefaults.TextFieldShape
- )
.width(TextFieldDefaults.MinWidth),
singleLine = singleLine,
interactionSource = interactionSource
@@ -174,9 +169,7 @@
BasicTextField(
value = text,
onValueChange = { text = it },
- modifier = indicator
- .background(colors.backgroundColor(enabled).value, TextFieldDefaults.TextFieldShape)
- .width(TextFieldDefaults.MinWidth),
+ modifier = indicator.width(TextFieldDefaults.MinWidth),
singleLine = singleLine,
interactionSource = interactionSource,
enabled = enabled
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SliderSample.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SliderSample.kt
index e2e6f8b..0e014ff 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SliderSample.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SliderSample.kt
@@ -17,6 +17,8 @@
package androidx.compose.material.samples
import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RangeSlider
@@ -28,34 +30,43 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import kotlin.math.roundToInt
@Sampled
@Composable
fun SliderSample() {
var sliderPosition by remember { mutableStateOf(0f) }
- Text(text = sliderPosition.toString())
- Slider(value = sliderPosition, onValueChange = { sliderPosition = it })
+ Column(modifier = Modifier.padding(horizontal = 16.dp)) {
+ Text(text = "%.2f".format(sliderPosition))
+ Slider(value = sliderPosition, onValueChange = { sliderPosition = it })
+ }
}
@Sampled
@Composable
fun StepsSliderSample() {
var sliderPosition by remember { mutableStateOf(0f) }
- Text(text = sliderPosition.toString())
- Slider(
- value = sliderPosition,
- onValueChange = { sliderPosition = it },
- valueRange = 0f..100f,
- onValueChangeFinished = {
- // launch some business logic update with the state you hold
- // viewModel.updateSelectedSliderValue(sliderPosition)
- },
- steps = 5,
- colors = SliderDefaults.colors(
- thumbColor = MaterialTheme.colors.secondary,
- activeTrackColor = MaterialTheme.colors.secondary
+ Column(modifier = Modifier.padding(horizontal = 16.dp)) {
+ Text(text = sliderPosition.roundToInt().toString())
+ Slider(
+ value = sliderPosition,
+ onValueChange = { sliderPosition = it },
+ valueRange = 0f..100f,
+ onValueChangeFinished = {
+ // launch some business logic update with the state you hold
+ // viewModel.updateSelectedSliderValue(sliderPosition)
+ },
+ // Only allow multiples of 10. Excluding the endpoints of `valueRange`,
+ // there are 9 steps (10, 20, ..., 90).
+ steps = 9,
+ colors = SliderDefaults.colors(
+ thumbColor = MaterialTheme.colors.secondary,
+ activeTrackColor = MaterialTheme.colors.secondary
+ )
)
- )
+ }
}
@Sampled
@@ -63,16 +74,20 @@
@OptIn(ExperimentalMaterialApi::class)
fun RangeSliderSample() {
var sliderPosition by remember { mutableStateOf(0f..100f) }
- Text(text = sliderPosition.toString())
- RangeSlider(
- value = sliderPosition,
- onValueChange = { sliderPosition = it },
- valueRange = 0f..100f,
- onValueChangeFinished = {
- // launch some business logic update with the state you hold
- // viewModel.updateSelectedSliderValue(sliderPosition)
- },
- )
+ Column(modifier = Modifier.padding(horizontal = 16.dp)) {
+ val rangeStart = "%.2f".format(sliderPosition.start)
+ val rangeEnd = "%.2f".format(sliderPosition.endInclusive)
+ Text(text = "$rangeStart .. $rangeEnd")
+ RangeSlider(
+ value = sliderPosition,
+ onValueChange = { sliderPosition = it },
+ valueRange = 0f..100f,
+ onValueChangeFinished = {
+ // launch some business logic update with the state you hold
+ // viewModel.updateSelectedSliderValue(sliderPosition)
+ },
+ )
+ }
}
@Sampled
@@ -80,19 +95,25 @@
@OptIn(ExperimentalMaterialApi::class)
fun StepRangeSliderSample() {
var sliderPosition by remember { mutableStateOf(0f..100f) }
- Text(text = sliderPosition.toString())
- RangeSlider(
- steps = 5,
- value = sliderPosition,
- onValueChange = { sliderPosition = it },
- valueRange = 0f..100f,
- onValueChangeFinished = {
- // launch some business logic update with the state you hold
- // viewModel.updateSelectedSliderValue(sliderPosition)
- },
- colors = SliderDefaults.colors(
- thumbColor = MaterialTheme.colors.secondary,
- activeTrackColor = MaterialTheme.colors.secondary
+ Column(modifier = Modifier.padding(horizontal = 16.dp)) {
+ val rangeStart = sliderPosition.start.roundToInt()
+ val rangeEnd = sliderPosition.endInclusive.roundToInt()
+ Text(text = "$rangeStart .. $rangeEnd")
+ RangeSlider(
+ value = sliderPosition,
+ onValueChange = { sliderPosition = it },
+ valueRange = 0f..100f,
+ onValueChangeFinished = {
+ // launch some business logic update with the state you hold
+ // viewModel.updateSelectedSliderValue(sliderPosition)
+ },
+ // Only allow multiples of 10. Excluding the endpoints of `valueRange`,
+ // there are 9 steps (10, 20, ..., 90).
+ steps = 9,
+ colors = SliderDefaults.colors(
+ thumbColor = MaterialTheme.colors.secondary,
+ activeTrackColor = MaterialTheme.colors.secondary
+ )
)
- )
+ }
}
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/RippleTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/RippleTest.kt
index 15c1d55..c52867d 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/RippleTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/RippleTest.kt
@@ -1015,10 +1015,6 @@
fun rippleConfiguration_disabled_dragged() {
val interactionSource = MutableInteractionSource()
- val rippleConfiguration = RippleConfiguration(
- isEnabled = false
- )
-
var scope: CoroutineScope? = null
rule.setContent {
@@ -1026,7 +1022,7 @@
MaterialTheme {
Surface {
CompositionLocalProvider(
- LocalRippleConfiguration provides rippleConfiguration
+ LocalRippleConfiguration provides null
) {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
RippleBoxWithBackground(
@@ -1072,7 +1068,7 @@
val contentColor = Color.Black
- var rippleConfiguration by mutableStateOf(RippleConfiguration())
+ var rippleConfiguration: RippleConfiguration? by mutableStateOf(RippleConfiguration())
var scope: CoroutineScope? = null
@@ -1119,7 +1115,6 @@
}
val newConfiguration = RippleConfiguration(
- isEnabled = true,
color = Color.Red,
rippleAlpha = RippleAlpha(0.5f, 0.5f, 0.5f, 0.5f)
)
@@ -1147,7 +1142,7 @@
}
rule.runOnUiThread {
- rippleConfiguration = RippleConfiguration(isEnabled = false)
+ rippleConfiguration = null
}
with(rule.onNodeWithTag(Tag)) {
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt
index c730d3c..4de9ec8 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt
@@ -22,6 +22,7 @@
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.material.AnimationDuration
import androidx.compose.material.GOLDEN_MATERIAL
import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor
@@ -145,6 +146,8 @@
@Test
fun outlinedTextField_error_focused() {
+ // stop animation of blinking cursor
+ rule.mainClock.autoAdvance = false
rule.setMaterialContent {
val text = "Input"
OutlinedTextField(
@@ -157,10 +160,7 @@
}
rule.onNodeWithTag(TextFieldTag).focus()
- rule.runOnIdle {
- // stop animation of blinking cursor
- rule.mainClock.autoAdvance = false
- }
+ rule.mainClock.advanceTimeBy(AnimationDuration.toLong())
assertAgainstGolden("outlined_textField_focused_errorState")
}
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt
index c322df8..61d68c1 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldDecorationBoxTest.kt
@@ -451,6 +451,124 @@
}
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun outlinedTextFieldBox_appliesBackgroundColor() {
+ val textFieldWidth = 300
+ val textFieldHeight = 150
+ val borderWidth = 2
+ val value = ""
+
+ rule.setMaterialContent {
+ CompositionLocalProvider(LocalDensity provides Density) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val singleLine = true
+ val colors = TextFieldDefaults.outlinedTextFieldColors(
+ backgroundColor = Color.Red
+ )
+ BasicTextField(
+ value = value,
+ onValueChange = {},
+ modifier = Modifier.size(
+ with(Density) { textFieldWidth.toDp() },
+ with(Density) { textFieldHeight.toDp() }
+ ),
+ singleLine = singleLine,
+ interactionSource = interactionSource
+ ) {
+ OutlinedTextFieldDecorationBox(
+ value = value,
+ innerTextField = it,
+ enabled = true,
+ visualTransformation = VisualTransformation.None,
+ interactionSource = interactionSource,
+ singleLine = singleLine,
+ border = {
+ TextFieldDefaults.BorderBox(
+ enabled = true,
+ isError = false,
+ colors = colors,
+ interactionSource = interactionSource,
+ shape = RectangleShape,
+ unfocusedBorderThickness = with(Density) { borderWidth.toDp() }
+ )
+ },
+ colors = colors,
+ contentPadding = PaddingValues(0.dp)
+ )
+ }
+ }
+ }
+
+ rule.onNodeWithText(value)
+ .captureToImage()
+ .assertPixels(IntSize(textFieldWidth, textFieldHeight)) {
+ // to account for border + edge pixels
+ if (it.x in (borderWidth + 2)..(textFieldWidth - borderWidth - 2) &&
+ it.y in (borderWidth + 2)..(textFieldHeight - borderWidth - 2)) {
+ Color.Red
+ } else null
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun textFieldBox_defaultIndicatorLineColor_appliesBackgroundColor() {
+ val textFieldWidth = 300
+ val textFieldHeight = 150
+ val borderWidth = 2
+ val value = ""
+
+ rule.setMaterialContent {
+ CompositionLocalProvider(LocalDensity provides Density) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val singleLine = true
+ val colors = TextFieldDefaults.textFieldColors(
+ backgroundColor = Color.Red
+ )
+ BasicTextField(
+ value = value,
+ onValueChange = {},
+ modifier = Modifier
+ .indicatorLine(
+ enabled = true,
+ isError = false,
+ colors = colors,
+ interactionSource = interactionSource,
+ unfocusedIndicatorLineThickness = with(Density) { borderWidth.toDp() }
+ )
+ .size(
+ with(Density) { textFieldWidth.toDp() },
+ with(Density) { textFieldHeight.toDp() }
+ ),
+ singleLine = singleLine,
+ interactionSource = interactionSource
+ ) {
+ TextFieldDecorationBox(
+ value = value,
+ innerTextField = it,
+ enabled = true,
+ visualTransformation = VisualTransformation.None,
+ interactionSource = interactionSource,
+ singleLine = singleLine,
+ colors = colors,
+ contentPadding = PaddingValues(0.dp)
+ )
+ }
+ }
+ }
+
+ rule.onNodeWithText(value)
+ .captureToImage()
+ .assertPixels(IntSize(textFieldWidth, textFieldHeight)) {
+ // to account for border + edge pixels
+ if (it.x in 2..(textFieldWidth - 2) &&
+ it.y in 2..(textFieldHeight - borderWidth - 2)) {
+ Color.Red
+ } else null
+ }
+ }
+
@Test
fun outlinedTextFieldBox_innerTextLocation_withMultilineLabel() {
val labelHeight = 60.dp
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt
index 20f42be..b81fecd 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt
@@ -22,6 +22,7 @@
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.width
+import androidx.compose.material.AnimationDuration
import androidx.compose.material.GOLDEN_MATERIAL
import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor
@@ -153,6 +154,8 @@
@Test
fun textField_error_focused() {
+ // stop animation of blinking cursor
+ rule.mainClock.autoAdvance = false
rule.setMaterialContent {
val text = "Input"
TextField(
@@ -165,10 +168,7 @@
}
rule.onNodeWithTag(TextFieldTag).focus()
- rule.runOnIdle {
- // stop animation of blinking cursor
- rule.mainClock.autoAdvance = false
- }
+ rule.mainClock.advanceTimeBy(AnimationDuration.toLong())
assertAgainstGolden("filled_textField_focused_errorState")
}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
index 66c7527..6cf332c 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
@@ -16,7 +16,6 @@
package androidx.compose.material
-import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
@@ -154,7 +153,7 @@
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1,
interactionSource: MutableInteractionSource? = null,
- shape: Shape = MaterialTheme.shapes.small,
+ shape: Shape = TextFieldDefaults.OutlinedTextFieldShape,
colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
) {
@Suppress("NAME_SHADOWING")
@@ -182,7 +181,6 @@
Modifier
}
)
- .background(colors.backgroundColor(enabled).value, shape)
.defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
.defaultMinSize(
minWidth = TextFieldDefaults.MinWidth,
@@ -213,6 +211,7 @@
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
+ shape = shape,
colors = colors,
border = {
TextFieldDefaults.BorderBox(
@@ -251,7 +250,7 @@
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- shape: Shape = MaterialTheme.shapes.small,
+ shape: Shape = TextFieldDefaults.OutlinedTextFieldShape,
colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
) {
OutlinedTextField(
@@ -391,7 +390,6 @@
Modifier
}
)
- .background(colors.backgroundColor(enabled).value, shape)
.defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
.defaultMinSize(
minWidth = TextFieldDefaults.MinWidth,
@@ -422,6 +420,7 @@
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
+ shape = shape,
colors = colors,
border = {
TextFieldDefaults.BorderBox(
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Ripple.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Ripple.kt
index 50cca67..7b371e38 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Ripple.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Ripple.kt
@@ -200,7 +200,8 @@
/**
* CompositionLocal used for providing [RippleConfiguration] down the tree. This acts as a
* tree-local 'override' for ripples used inside components that you cannot directly control, such
- * as to change the color of a specific component's ripple, or disable it entirely.
+ * as to change the color of a specific component's ripple, or disable it entirely by providing
+ * `null`.
*
* In most cases you should rely on the default theme behavior for consistency with other components
* - this exists as an escape hatch for individual components and is not intended to be used for
@@ -211,15 +212,15 @@
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@get:ExperimentalMaterialApi
@ExperimentalMaterialApi
-val LocalRippleConfiguration: ProvidableCompositionLocal<RippleConfiguration> =
+val LocalRippleConfiguration: ProvidableCompositionLocal<RippleConfiguration?> =
compositionLocalOf { RippleConfiguration() }
/**
* Configuration for [ripple] appearance, provided using [LocalRippleConfiguration]. In most cases
* the default values should be used, for custom design system use cases you should instead
- * build your own custom ripple using [createRippleModifierNode].
+ * build your own custom ripple using [createRippleModifierNode]. To disable the ripple, provide
+ * `null` using [LocalRippleConfiguration].
*
- * @param isEnabled whether the ripple is enabled. If false, no ripple will be rendered
* @param color the color override for the ripple. If [Color.Unspecified], then the default color
* from the theme will be used instead. Note that if the ripple has a color explicitly set with
* the parameter on [ripple], that will always be used instead of this value.
@@ -229,7 +230,6 @@
@Immutable
@ExperimentalMaterialApi
class RippleConfiguration(
- val isEnabled: Boolean = true,
val color: Color = Color.Unspecified,
val rippleAlpha: RippleAlpha? = null
) {
@@ -237,7 +237,6 @@
if (this === other) return true
if (other !is RippleConfiguration) return false
- if (isEnabled != other.isEnabled) return false
if (color != other.color) return false
if (rippleAlpha != other.rippleAlpha) return false
@@ -245,14 +244,13 @@
}
override fun hashCode(): Int {
- var result = isEnabled.hashCode()
- result = 31 * result + color.hashCode()
+ var result = color.hashCode()
result = 31 * result + (rippleAlpha?.hashCode() ?: 0)
return result
}
override fun toString(): String {
- return "RippleConfiguration(enabled=$isEnabled, color=$color, rippleAlpha=$rippleAlpha)"
+ return "RippleConfiguration(color=$color, rippleAlpha=$rippleAlpha)"
}
}
@@ -353,13 +351,14 @@
}
/**
- * Handles changes to [RippleConfiguration.isEnabled]. Changes to [RippleConfiguration.color] and
- * [RippleConfiguration.rippleAlpha] are handled as part of the ripple definition.
+ * Handles [LocalRippleConfiguration] changing between null / non-null. Changes to
+ * [RippleConfiguration.color] and [RippleConfiguration.rippleAlpha] are handled as part of
+ * the ripple definition.
*/
private fun updateConfiguration() {
observeReads {
val configuration = currentValueOf(LocalRippleConfiguration)
- if (!configuration.isEnabled) {
+ if (configuration == null) {
removeRipple()
} else {
if (rippleNode == null) attachNewRipple()
@@ -373,8 +372,10 @@
if (userDefinedColor.isSpecified) {
userDefinedColor
} else {
+ // If this is null, the ripple will be removed, so this should always be non-null in
+ // normal use
val rippleConfiguration = currentValueOf(LocalRippleConfiguration)
- if (rippleConfiguration.color.isSpecified) {
+ if (rippleConfiguration?.color?.isSpecified == true) {
rippleConfiguration.color
} else {
RippleDefaults.rippleColor(
@@ -385,8 +386,10 @@
}
}
val calculateRippleAlpha = {
+ // If this is null, the ripple will be removed, so this should always be non-null in
+ // normal use
val rippleConfiguration = currentValueOf(LocalRippleConfiguration)
- rippleConfiguration.rippleAlpha ?: RippleDefaults.rippleAlpha(
+ rippleConfiguration?.rippleAlpha ?: RippleDefaults.rippleAlpha(
contentColor = currentValueOf(LocalContentColor),
lightTheme = currentValueOf(LocalColors).isLight
)
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
index 3004819..85fc6f7 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
@@ -132,9 +132,9 @@
* @param enabled whether or not component is enabled and can be interacted with or not
* @param valueRange range of values that Slider value can take. Passed [value] will be coerced to
* this range
- * @param steps if greater than 0, specifies the amounts of discrete values, evenly distributed
- * between across the whole value range. If 0, slider will behave as a continuous slider and allow
- * to choose any value from the range specified. Must not be negative.
+ * @param steps if positive, specifies the amount of discrete allowable values (in addition to the
+ * endpoints of the value range). Step values are evenly distributed across the range. If 0, the
+ * slider will behave continuously and allow any value from the range. Must not be negative.
* @param onValueChangeFinished lambda to be invoked when value change has ended. This callback
* shouldn't be used to update the slider value (use [onValueChange] for that), but rather to
* know when the user has completed selecting a new value by ending a drag or a click.
@@ -282,9 +282,9 @@
* @param enabled whether or not component is enabled and can we interacted with or not
* @param valueRange range of values that Range Slider values can take. Passed [value] will be
* coerced to this range
- * @param steps if greater than 0, specifies the amounts of discrete values, evenly distributed
- * between across the whole value range. If 0, range slider will behave as a continuous slider and
- * allow to choose any values from the range specified. Must not be negative.
+ * @param steps if positive, specifies the amount of discrete allowable values (in addition to the
+ * endpoints of the value range). Step values are evenly distributed across the range. If 0, the
+ * range slider will behave continuously and allow any value from the range. Must not be negative.
* @param onValueChangeFinished lambda to be invoked when value change has ended. This callback
* shouldn't be used to update the range slider values (use [onValueChange] for that), but rather to
* know when the user has completed selecting a new value by ending a drag or a click.
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
index d0ca8cf..b85ec36 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
@@ -17,7 +17,6 @@
package androidx.compose.material
import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
@@ -177,8 +176,7 @@
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1,
interactionSource: MutableInteractionSource? = null,
- shape: Shape =
- MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
+ shape: Shape = TextFieldDefaults.TextFieldShape,
colors: TextFieldColors = TextFieldDefaults.textFieldColors()
) {
@Suppress("NAME_SHADOWING")
@@ -193,7 +191,6 @@
BasicTextField(
value = value,
modifier = modifier
- .background(colors.backgroundColor(enabled).value, shape)
.indicatorLine(enabled, isError, interactionSource, colors)
.defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
.defaultMinSize(
@@ -226,7 +223,8 @@
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
- colors = colors
+ shape = shape,
+ colors = colors,
)
}
)
@@ -384,7 +382,6 @@
BasicTextField(
value = value,
modifier = modifier
- .background(colors.backgroundColor(enabled).value, shape)
.indicatorLine(enabled, isError, interactionSource, colors)
.defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
.defaultMinSize(
@@ -417,7 +414,8 @@
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
- colors = colors
+ shape = shape,
+ colors = colors,
)
}
)
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
index 37904b7..8f09e82 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
@@ -544,6 +544,7 @@
* container
* @param trailingIcon the optional trailing icon to be displayed at the end of the text field
* container
+ * @param shape the shape of the text field's container
* @param colors [TextFieldColors] that will be used to resolve color of the text and content
* (including label, placeholder, leading and trailing icons, bottom indicator) for this text field in
* different states. See [TextFieldDefaults.textFieldColors]
@@ -570,6 +571,7 @@
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
+ shape: Shape = TextFieldShape,
colors: TextFieldColors = textFieldColors(),
contentPadding: PaddingValues =
if (label == null) {
@@ -591,8 +593,10 @@
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
+ shape = shape,
colors = colors,
- contentPadding = contentPadding
+ contentPadding = contentPadding,
+ border = null,
)
}
@@ -638,6 +642,7 @@
* container
* @param trailingIcon the optional trailing icon to be displayed at the end of the text field
* container
+ * @param shape the shape of the text field's container and border
* @param colors [TextFieldColors] that will be used to resolve color of the text and content
* (including label, placeholder, leading and trailing icons, border) for this text field in
* different states. See [TextFieldDefaults.outlinedTextFieldColors]
@@ -662,10 +667,11 @@
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
+ shape: Shape = OutlinedTextFieldShape,
colors: TextFieldColors = outlinedTextFieldColors(),
contentPadding: PaddingValues = outlinedTextFieldPadding(),
border: @Composable () -> Unit = {
- BorderBox(enabled, isError, interactionSource, colors)
+ BorderBox(enabled, isError, interactionSource, colors, shape)
}
) {
CommonDecorationBox(
@@ -681,11 +687,95 @@
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
+ shape = shape,
colors = colors,
contentPadding = contentPadding,
- border = border
+ border = border,
)
}
+
+ @Deprecated(
+ level = DeprecationLevel.HIDDEN,
+ message = "Maintained for binary compatibility. Use overload with `shape` parameter."
+ )
+ @Composable
+ @ExperimentalMaterialApi
+ fun TextFieldDecorationBox(
+ value: String,
+ innerTextField: @Composable () -> Unit,
+ enabled: Boolean,
+ singleLine: Boolean,
+ visualTransformation: VisualTransformation,
+ interactionSource: InteractionSource,
+ isError: Boolean = false,
+ label: @Composable (() -> Unit)? = null,
+ placeholder: @Composable (() -> Unit)? = null,
+ leadingIcon: @Composable (() -> Unit)? = null,
+ trailingIcon: @Composable (() -> Unit)? = null,
+ colors: TextFieldColors = textFieldColors(),
+ contentPadding: PaddingValues =
+ if (label == null) {
+ textFieldWithoutLabelPadding()
+ } else {
+ textFieldWithLabelPadding()
+ }
+ ) = TextFieldDecorationBox(
+ value = value,
+ innerTextField = innerTextField,
+ enabled = enabled,
+ singleLine = singleLine,
+ visualTransformation = visualTransformation,
+ interactionSource = interactionSource,
+ isError = isError,
+ label = label,
+ placeholder = placeholder,
+ leadingIcon = leadingIcon,
+ trailingIcon = trailingIcon,
+ shape = TextFieldShape,
+ colors = colors,
+ contentPadding = contentPadding,
+ )
+
+ @Deprecated(
+ level = DeprecationLevel.HIDDEN,
+ message = "Maintained for binary compatibility. Use overload with `shape` parameter."
+ )
+ @Composable
+ @ExperimentalMaterialApi
+ fun OutlinedTextFieldDecorationBox(
+ value: String,
+ innerTextField: @Composable () -> Unit,
+ enabled: Boolean,
+ singleLine: Boolean,
+ visualTransformation: VisualTransformation,
+ interactionSource: InteractionSource,
+ isError: Boolean = false,
+ label: @Composable (() -> Unit)? = null,
+ placeholder: @Composable (() -> Unit)? = null,
+ leadingIcon: @Composable (() -> Unit)? = null,
+ trailingIcon: @Composable (() -> Unit)? = null,
+ colors: TextFieldColors = outlinedTextFieldColors(),
+ contentPadding: PaddingValues = outlinedTextFieldPadding(),
+ border: @Composable () -> Unit = {
+ BorderBox(enabled, isError, interactionSource, colors)
+ }
+ ) = OutlinedTextFieldDecorationBox(
+ value = value,
+ innerTextField = innerTextField,
+ enabled = enabled,
+ singleLine = singleLine,
+ visualTransformation = visualTransformation,
+ interactionSource = interactionSource,
+ isError = isError,
+ label = label,
+ placeholder = placeholder,
+ leadingIcon = leadingIcon,
+ trailingIcon = trailingIcon,
+ shape = OutlinedTextFieldShape,
+ colors = colors,
+ contentPadding = contentPadding,
+ border = border,
+ )
}
@Immutable
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
index 5c493f87..85a7722 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
@@ -22,6 +22,7 @@
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Box
@@ -37,6 +38,7 @@
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.layout.IntrinsicMeasurable
import androidx.compose.ui.layout.LayoutIdParentData
@@ -66,16 +68,17 @@
innerTextField: @Composable () -> Unit,
visualTransformation: VisualTransformation,
label: @Composable (() -> Unit)?,
- placeholder: @Composable (() -> Unit)? = null,
- leadingIcon: @Composable (() -> Unit)? = null,
- trailingIcon: @Composable (() -> Unit)? = null,
- singleLine: Boolean = false,
- enabled: Boolean = true,
- isError: Boolean = false,
+ placeholder: @Composable (() -> Unit)?,
+ leadingIcon: @Composable (() -> Unit)?,
+ trailingIcon: @Composable (() -> Unit)?,
+ singleLine: Boolean,
+ enabled: Boolean,
+ isError: Boolean,
interactionSource: InteractionSource,
contentPadding: PaddingValues,
+ shape: Shape,
colors: TextFieldColors,
- border: @Composable (() -> Unit)? = null
+ border: @Composable (() -> Unit)?,
) {
val transformedText = remember(value, visualTransformation) {
visualTransformation.filter(AnnotatedString(value))
@@ -159,10 +162,13 @@
}
}
+ val backgroundModifier =
+ Modifier.background(colors.backgroundColor(enabled).value, shape)
+
when (type) {
TextFieldType.Filled -> {
TextFieldLayout(
- modifier = Modifier,
+ modifier = backgroundModifier,
textField = innerTextField,
placeholder = decoratedPlaceholder,
label = decoratedLabel,
@@ -186,7 +192,7 @@
}
OutlinedTextFieldLayout(
- modifier = Modifier,
+ modifier = backgroundModifier,
textField = innerTextField,
placeholder = decoratedPlaceholder,
label = decoratedLabel,
diff --git a/compose/material3/adaptive/adaptive-layout/api/current.txt b/compose/material3/adaptive/adaptive-layout/api/current.txt
index 9d1a055..cf79bdb 100644
--- a/compose/material3/adaptive/adaptive-layout/api/current.txt
+++ b/compose/material3/adaptive/adaptive-layout/api/current.txt
@@ -38,7 +38,7 @@
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ListDetailPaneScaffoldRole {
+ public final class ListDetailPaneScaffoldRole {
method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getDetail();
method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getExtra();
method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getList();
@@ -63,7 +63,7 @@
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void AnimatedPane(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class PaneScaffoldDirective {
+ @androidx.compose.runtime.Immutable public final class PaneScaffoldDirective {
ctor public PaneScaffoldDirective(int maxHorizontalPartitions, float horizontalPartitionSpacerSize, int maxVerticalPartitions, float verticalPartitionSpacerSize, float defaultPanePreferredWidth, java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective copy(optional int maxHorizontalPartitions, optional float horizontalPartitionSpacerSize, optional int maxVerticalPartitions, optional float verticalPartitionSpacerSize, optional float defaultPanePreferredWidth, optional java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
method public float getDefaultPanePreferredWidth();
@@ -91,7 +91,7 @@
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.layout.PaneScaffoldDirective calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int verticalHingePolicy);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldScope {
+ public sealed interface PaneScaffoldScope {
method public androidx.compose.ui.Modifier preferredWidth(androidx.compose.ui.Modifier, float width);
}
@@ -127,7 +127,7 @@
property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ThreePaneScaffoldRole {
+ public enum ThreePaneScaffoldRole {
enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Primary;
enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Secondary;
enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Tertiary;
diff --git a/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt b/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
index 9d1a055..cf79bdb 100644
--- a/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
+++ b/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
@@ -38,7 +38,7 @@
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ListDetailPaneScaffoldRole {
+ public final class ListDetailPaneScaffoldRole {
method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getDetail();
method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getExtra();
method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getList();
@@ -63,7 +63,7 @@
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void AnimatedPane(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class PaneScaffoldDirective {
+ @androidx.compose.runtime.Immutable public final class PaneScaffoldDirective {
ctor public PaneScaffoldDirective(int maxHorizontalPartitions, float horizontalPartitionSpacerSize, int maxVerticalPartitions, float verticalPartitionSpacerSize, float defaultPanePreferredWidth, java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective copy(optional int maxHorizontalPartitions, optional float horizontalPartitionSpacerSize, optional int maxVerticalPartitions, optional float verticalPartitionSpacerSize, optional float defaultPanePreferredWidth, optional java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
method public float getDefaultPanePreferredWidth();
@@ -91,7 +91,7 @@
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.layout.PaneScaffoldDirective calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int verticalHingePolicy);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldScope {
+ public sealed interface PaneScaffoldScope {
method public androidx.compose.ui.Modifier preferredWidth(androidx.compose.ui.Modifier, float width);
}
@@ -127,7 +127,7 @@
property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ThreePaneScaffoldRole {
+ public enum ThreePaneScaffoldRole {
enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Primary;
enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Secondary;
enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Tertiary;
diff --git a/compose/material3/adaptive/adaptive-layout/build.gradle b/compose/material3/adaptive/adaptive-layout/build.gradle
index 7e7f7c9..bc2d364 100644
--- a/compose/material3/adaptive/adaptive-layout/build.gradle
+++ b/compose/material3/adaptive/adaptive-layout/build.gradle
@@ -44,6 +44,7 @@
api(project(":compose:material3:adaptive:adaptive"))
api(project(":compose:animation:animation-core"))
api(project(":compose:ui:ui"))
+ implementation(project(":compose:animation:animation"))
implementation("androidx.compose.foundation:foundation:1.6.5")
implementation("androidx.compose.foundation:foundation-layout:1.6.5")
implementation("androidx.compose.ui:ui-geometry:1.6.5")
@@ -116,7 +117,7 @@
androidx {
name = "Material Adaptive"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2023"
description = "Compose Material Design Adaptive Library"
samples(project(":compose:material3:adaptive:adaptive-samples"))
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/LargeScreenTestUtils.kt b/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/LargeScreenTestUtils.kt
index 5dfa0175..37edf98c 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/LargeScreenTestUtils.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/LargeScreenTestUtils.kt
@@ -19,7 +19,6 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.currentWindowSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@@ -30,7 +29,6 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.toSize
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
internal fun ComposeContentTestRule.setContentWithSimulatedSize(
simulatedWidth: Dp,
simulatedHeight: Dp,
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldTest.kt b/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldTest.kt
index 829f4a2..f738efb 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldTest.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldTest.kt
@@ -166,7 +166,6 @@
}
}
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private val MockScaffoldDirective = PaneScaffoldDirective.Default
internal const val ThreePaneScaffoldTestTag = "SampleThreePaneScaffold"
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt b/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt
index 52b3b90..f7b506b 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt
@@ -268,18 +268,21 @@
private val hingeList = listOf(
HingeInfo(
bounds = Rect(0F, 0F, 1F, 1F),
+ isFlat = true,
isVertical = true,
isSeparating = false,
isOccluding = true
),
HingeInfo(
bounds = Rect(1F, 1F, 2F, 2F),
+ isFlat = false,
isVertical = true,
isSeparating = false,
isOccluding = true
),
HingeInfo(
bounds = Rect(2F, 2F, 3F, 3F),
+ isFlat = true,
isVertical = true,
isSeparating = true,
isOccluding = false
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt
index b504210..b3234de 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt
@@ -20,7 +20,6 @@
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.TargetBasedAnimation
import androidx.compose.animation.core.VectorConverter
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.ApproachLayoutModifierNode
@@ -120,7 +119,6 @@
lookaheadCoordinates: LayoutCoordinates
) = animateFraction() != 1f
- @OptIn(ExperimentalComposeUiApi::class)
override fun ApproachMeasureScope.approachMeasure(
measurable: Measurable,
constraints: Constraints
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
index 74f39ac..6ab8f53 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
@@ -95,7 +95,6 @@
* of three pane scaffolds. We suggest you to use the values defined here instead of the raw
* [ThreePaneScaffoldRole] under the context of [ListDetailPaneScaffold] for better code clarity.
*/
-@ExperimentalMaterial3AdaptiveApi
object ListDetailPaneScaffoldRole {
/**
* The list pane of [ListDetailPaneScaffold], which is supposed to hold a list of item summaries
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/Pane.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/Pane.kt
index 2d255ce..9a5aa23 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/Pane.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/Pane.kt
@@ -18,7 +18,6 @@
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.AnimatedVisibilityScope
-import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -37,8 +36,6 @@
* @sample androidx.compose.material3.adaptive.samples.ListDetailPaneScaffoldSample
* @sample androidx.compose.material3.adaptive.samples.ListDetailPaneScaffoldSampleWithExtraPane
*/
-@Suppress("IllegalExperimentalApiUsage") // TODO: address before moving to beta
-@OptIn(ExperimentalAnimationApi::class)
@ExperimentalMaterial3AdaptiveApi
@Composable
fun ThreePaneScaffoldScope.AnimatedPane(
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.kt
index e164e2f..b840410 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.kt
@@ -16,7 +16,6 @@
package androidx.compose.material3.adaptive.layout
-import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.ParentDataModifierNode
@@ -29,7 +28,6 @@
/**
* Scope for the panes of pane scaffolds.
*/
-@ExperimentalMaterial3AdaptiveApi
sealed interface PaneScaffoldScope {
/**
* This modifier specifies the preferred width for a pane, and the pane scaffold implementation
@@ -43,7 +41,6 @@
fun Modifier.preferredWidth(width: Dp): Modifier
}
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
internal abstract class PaneScaffoldScopeImpl : PaneScaffoldScope {
override fun Modifier.preferredWidth(width: Dp): Modifier {
require(width == Dp.Unspecified || width > 0.dp) { "invalid width" }
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
index 8ba2159..8072c34 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
@@ -126,7 +126,6 @@
}
}
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun getExcludedVerticalBounds(posture: Posture, hingePolicy: HingePolicy): List<Rect> {
return when (hingePolicy) {
HingePolicy.AvoidSeparating -> posture.separatingVerticalHingeBounds
@@ -155,9 +154,7 @@
* @property excludedBounds the bounds of all areas in the window that the layout needs to avoid
* displaying anything upon it. Usually these bounds represent where physical hinges are.
*/
-@ExperimentalMaterial3AdaptiveApi
@Immutable
-// TODO(conradchen): Hide the constructor. Please use the copy() method instead.
class PaneScaffoldDirective(
val maxHorizontalPartitions: Int,
val horizontalPartitionSpacerSize: Dp,
@@ -190,8 +187,7 @@
maxVerticalPartitions: Int = this.maxVerticalPartitions,
verticalPartitionSpacerSize: Dp = this.verticalPartitionSpacerSize,
defaultPanePreferredWidth: Dp = this.defaultPanePreferredWidth,
- @Suppress("ListIterator") // No guarantee to be an array list
- excludedBounds: List<Rect> = this.excludedBounds.toList()
+ excludedBounds: List<Rect> = this.excludedBounds
): PaneScaffoldDirective = PaneScaffoldDirective(
maxHorizontalPartitions,
horizontalPartitionSpacerSize,
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
index 86e729e..6e92343 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
@@ -580,7 +580,6 @@
}
}
- @OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun MutableList<PaneMeasurable>.createPaneMeasurableIfNeeded(
measurables: List<Measurable>,
priority: Int,
@@ -632,7 +631,6 @@
)
}
- @OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun Placeable.PlacementScope.measureAndPlacePanesWithLocalBounds(
partitionBounds: IntRect,
spacerSize: Int,
@@ -747,7 +745,6 @@
}
}
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private class PaneMeasurable(
val measurable: Measurable,
val priority: Int,
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldHorizontalOrder.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldHorizontalOrder.kt
index 589c663..a672945 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldHorizontalOrder.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldHorizontalOrder.kt
@@ -111,7 +111,6 @@
/**
* The set of the available pane roles of [ThreePaneScaffold].
*/
-@ExperimentalMaterial3AdaptiveApi
enum class ThreePaneScaffoldRole {
/**
* The primary pane of [ThreePaneScaffold]. It is supposed to have the highest priority during
diff --git a/compose/material3/adaptive/adaptive-navigation/build.gradle b/compose/material3/adaptive/adaptive-navigation/build.gradle
index 5297890..9087365 100644
--- a/compose/material3/adaptive/adaptive-navigation/build.gradle
+++ b/compose/material3/adaptive/adaptive-navigation/build.gradle
@@ -112,7 +112,7 @@
androidx {
name = "Material Adaptive"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2023"
description = "Compose Material Design Adaptive Library"
}
diff --git a/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/ListDetailPaneScaffoldNavigatorTest.kt b/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/ListDetailPaneScaffoldNavigatorTest.kt
index 5218d92..6307eda 100644
--- a/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/ListDetailPaneScaffoldNavigatorTest.kt
+++ b/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/ListDetailPaneScaffoldNavigatorTest.kt
@@ -566,10 +566,8 @@
}
}
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private val MockSinglePaneScaffoldDirective = PaneScaffoldDirective.Default
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private val MockDualPaneScaffoldDirective = PaneScaffoldDirective.Default.copy(
maxHorizontalPartitions = 2,
horizontalPartitionSpacerSize = 16.dp,
diff --git a/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/SupportingPaneScaffoldNavigatorTest.kt b/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/SupportingPaneScaffoldNavigatorTest.kt
index d270567..baa76ed 100644
--- a/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/SupportingPaneScaffoldNavigatorTest.kt
+++ b/compose/material3/adaptive/adaptive-navigation/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigation/SupportingPaneScaffoldNavigatorTest.kt
@@ -580,10 +580,8 @@
}
}
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private val MockSinglePaneScaffoldDirective = PaneScaffoldDirective.Default
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private val MockDualPaneScaffoldDirective = PaneScaffoldDirective.Default.copy(
maxHorizontalPartitions = 2,
horizontalPartitionSpacerSize = 16.dp,
diff --git a/compose/material3/adaptive/adaptive/api/current.txt b/compose/material3/adaptive/adaptive/api/current.txt
index 6fe8f2a..4532ffe 100644
--- a/compose/material3/adaptive/adaptive/api/current.txt
+++ b/compose/material3/adaptive/adaptive/api/current.txt
@@ -15,12 +15,14 @@
}
@androidx.compose.runtime.Immutable public final class HingeInfo {
- ctor public HingeInfo(androidx.compose.ui.geometry.Rect bounds, boolean isVertical, boolean isSeparating, boolean isOccluding);
+ ctor public HingeInfo(androidx.compose.ui.geometry.Rect bounds, boolean isFlat, boolean isVertical, boolean isSeparating, boolean isOccluding);
method public androidx.compose.ui.geometry.Rect getBounds();
+ method public boolean isFlat();
method public boolean isOccluding();
method public boolean isSeparating();
method public boolean isVertical();
property public final androidx.compose.ui.geometry.Rect bounds;
+ property public final boolean isFlat;
property public final boolean isOccluding;
property public final boolean isSeparating;
property public final boolean isVertical;
diff --git a/compose/material3/adaptive/adaptive/api/restricted_current.txt b/compose/material3/adaptive/adaptive/api/restricted_current.txt
index 6fe8f2a..4532ffe 100644
--- a/compose/material3/adaptive/adaptive/api/restricted_current.txt
+++ b/compose/material3/adaptive/adaptive/api/restricted_current.txt
@@ -15,12 +15,14 @@
}
@androidx.compose.runtime.Immutable public final class HingeInfo {
- ctor public HingeInfo(androidx.compose.ui.geometry.Rect bounds, boolean isVertical, boolean isSeparating, boolean isOccluding);
+ ctor public HingeInfo(androidx.compose.ui.geometry.Rect bounds, boolean isFlat, boolean isVertical, boolean isSeparating, boolean isOccluding);
method public androidx.compose.ui.geometry.Rect getBounds();
+ method public boolean isFlat();
method public boolean isOccluding();
method public boolean isSeparating();
method public boolean isVertical();
property public final androidx.compose.ui.geometry.Rect bounds;
+ property public final boolean isFlat;
property public final boolean isOccluding;
property public final boolean isSeparating;
property public final boolean isVertical;
diff --git a/compose/material3/adaptive/adaptive/build.gradle b/compose/material3/adaptive/adaptive/build.gradle
index 42422b8..7f83a8b 100644
--- a/compose/material3/adaptive/adaptive/build.gradle
+++ b/compose/material3/adaptive/adaptive/build.gradle
@@ -112,7 +112,7 @@
androidx {
name = "Material Adaptive"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2023"
description = "Compose Material Design Adaptive Library"
}
diff --git a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectFoldingFeaturesAsStateTest.kt b/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectFoldingFeaturesAsStateTest.kt
index 1eb4823..26a5113 100644
--- a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectFoldingFeaturesAsStateTest.kt
+++ b/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectFoldingFeaturesAsStateTest.kt
@@ -33,7 +33,6 @@
import org.junit.rules.TestRule
import org.junit.runner.RunWith
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CollectFoldingFeaturesAsStateTest {
diff --git a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt b/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt
index 8ff84ab..4a62e9d 100644
--- a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt
+++ b/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt
@@ -37,7 +37,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CollectWindowSizeAsStateTest {
diff --git a/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt b/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt
index 61ad91e..7907e41 100644
--- a/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt
+++ b/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt
@@ -35,6 +35,7 @@
}
hingeList.add(HingeInfo(
bounds = it.bounds.toComposeRect(),
+ isFlat = it.state == FoldingFeature.State.FLAT,
isVertical = it.orientation == FoldingFeature.Orientation.VERTICAL,
isSeparating = it.isSeparating,
isOccluding = it.occlusionType == FoldingFeature.OcclusionType.FULL
diff --git a/compose/material3/adaptive/adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/Posture.kt b/compose/material3/adaptive/adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/Posture.kt
index 1510287..349405b 100644
--- a/compose/material3/adaptive/adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/Posture.kt
+++ b/compose/material3/adaptive/adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/Posture.kt
@@ -100,6 +100,8 @@
* A class that contains the info of a hinge relevant to a [Posture].
*
* @param bounds the bounds of the hinge in the relevant viewport.
+ * @param isFlat `true` if the hinge is fully open and the relevant window space presented to the
+ * user is flat.
* @param isVertical `true` if the hinge is a vertical one, i.e., it separates the viewport into
* left and right; `false` if the hinge is horizontal, i.e., it separates the viewport
* into top and bottom.
@@ -109,6 +111,7 @@
@Immutable
class HingeInfo(
val bounds: Rect,
+ val isFlat: Boolean,
val isVertical: Boolean,
val isSeparating: Boolean,
val isOccluding: Boolean
@@ -117,6 +120,7 @@
if (this === other) return true
if (other !is HingeInfo) return false
if (bounds != other.bounds) return false
+ if (isFlat != other.isFlat) return false
if (isVertical != other.isVertical) return false
if (isSeparating != other.isSeparating) return false
if (isOccluding != other.isOccluding) return false
@@ -125,6 +129,7 @@
override fun hashCode(): Int {
var result = bounds.hashCode()
+ result = 31 * result + isFlat.hashCode()
result = 31 * result + isVertical.hashCode()
result = 31 * result + isSeparating.hashCode()
result = 31 * result + isOccluding.hashCode()
@@ -133,6 +138,7 @@
override fun toString(): String {
return "HingeInfo(bounds=$bounds, " +
+ "isFlat=$isFlat, " +
"isVertical=$isVertical, " +
"isSeparating=$isSeparating, " +
"isOccluding=$isOccluding)"
diff --git a/compose/material3/adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/TestUtils.kt b/compose/material3/adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/TestUtils.kt
index fd44e30..217aa37 100644
--- a/compose/material3/adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/TestUtils.kt
+++ b/compose/material3/adaptive/benchmark/src/androidTest/java/androidx/compose/material3/adaptive/benchmark/TestUtils.kt
@@ -34,10 +34,8 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
val singlePaneDirective = PaneScaffoldDirective.Default
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
val dualPaneDirective = PaneScaffoldDirective.Default.copy(
maxHorizontalPartitions = 2,
horizontalPartitionSpacerSize = 24.dp,
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationSuiteScaffoldBenchmarkTest.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationSuiteScaffoldBenchmarkTest.kt
index 6b2b165..16a22cc 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationSuiteScaffoldBenchmarkTest.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationSuiteScaffoldBenchmarkTest.kt
@@ -19,7 +19,6 @@
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
@@ -59,7 +58,6 @@
}
}
-@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
internal class NavigationSuiteScaffoldTestCase : LayeredComposeTestCase(), ToggleableTestCase {
private lateinit var selectedIndexState: MutableIntState
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SwitchBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SwitchBenchmark.kt
new file mode 100644
index 0000000..6a164ba
--- /dev/null
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SwitchBenchmark.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.benchmark
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Switch
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.ToggleableTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkFirstCompose
+import androidx.compose.testutils.benchmark.benchmarkFirstDraw
+import androidx.compose.testutils.benchmark.benchmarkFirstLayout
+import androidx.compose.testutils.benchmark.benchmarkFirstMeasure
+import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class SwitchBenchmark {
+
+ @get:Rule
+ val benchmarkRule = ComposeBenchmarkRule()
+
+ private val SwitchTestCaseFactory = { SwitchTestCase() }
+
+ @Ignore
+ @Test
+ fun first_compose() {
+ benchmarkRule.benchmarkFirstCompose(SwitchTestCaseFactory)
+ }
+
+ @Ignore
+ @Test
+ fun first_measure() {
+ benchmarkRule.benchmarkFirstMeasure(SwitchTestCaseFactory)
+ }
+
+ @Ignore
+ @Test
+ fun first_layout() {
+ benchmarkRule.benchmarkFirstLayout(SwitchTestCaseFactory)
+ }
+
+ @Ignore
+ @Test
+ fun first_draw() {
+ benchmarkRule.benchmarkFirstDraw(SwitchTestCaseFactory)
+ }
+
+ @Test
+ fun firstPixel() {
+ benchmarkRule.benchmarkToFirstPixel(SwitchTestCaseFactory)
+ }
+
+ @Test
+ fun toggle_recomposeMeasureLayout() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = SwitchTestCaseFactory,
+ assertOneRecomposition = false
+ )
+ }
+}
+
+internal class SwitchTestCase : LayeredComposeTestCase(), ToggleableTestCase {
+
+ private var state by mutableStateOf(false)
+
+ @Composable
+ override fun MeasuredContent() {
+ Switch(checked = state, onCheckedChange = { })
+ }
+
+ override fun toggleState() {
+ state = !state
+ }
+
+ @Composable
+ override fun ContentWrappers(content: @Composable () -> Unit) {
+ MaterialTheme {
+ content()
+ }
+ }
+}
diff --git a/compose/material3/material3-adaptive-navigation-suite/api/current.txt b/compose/material3/material3-adaptive-navigation-suite/api/current.txt
index 7a7bc4a..83d23c0 100644
--- a/compose/material3/material3-adaptive-navigation-suite/api/current.txt
+++ b/compose/material3/material3-adaptive-navigation-suite/api/current.txt
@@ -4,7 +4,7 @@
@SuppressCompatibility @kotlin.RequiresOptIn(message="This material3-adaptive-navigation-suite API is experimental and is likely to" + "change or to be removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3AdaptiveNavigationSuiteApi {
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final class NavigationSuiteColors {
+ public final class NavigationSuiteColors {
method public long getNavigationBarContainerColor();
method public long getNavigationBarContentColor();
method public long getNavigationDrawerContainerColor();
@@ -19,13 +19,13 @@
property public final long navigationRailContentColor;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final class NavigationSuiteDefaults {
+ public final class NavigationSuiteDefaults {
method @androidx.compose.runtime.Composable public androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors colors(optional long navigationBarContainerColor, optional long navigationBarContentColor, optional long navigationRailContainerColor, optional long navigationRailContentColor, optional long navigationDrawerContainerColor, optional long navigationDrawerContentColor);
method @androidx.compose.runtime.Composable public androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItemColors itemColors(optional androidx.compose.material3.NavigationBarItemColors navigationBarItemColors, optional androidx.compose.material3.NavigationRailItemColors navigationRailItemColors, optional androidx.compose.material3.NavigationDrawerItemColors navigationDrawerItemColors);
field public static final androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults INSTANCE;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final class NavigationSuiteItemColors {
+ public final class NavigationSuiteItemColors {
ctor public NavigationSuiteItemColors(androidx.compose.material3.NavigationBarItemColors navigationBarItemColors, androidx.compose.material3.NavigationRailItemColors navigationRailItemColors, androidx.compose.material3.NavigationDrawerItemColors navigationDrawerItemColors);
method public androidx.compose.material3.NavigationBarItemColors getNavigationBarItemColors();
method public androidx.compose.material3.NavigationDrawerItemColors getNavigationDrawerItemColors();
@@ -35,7 +35,7 @@
property public final androidx.compose.material3.NavigationRailItemColors navigationRailItemColors;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final class NavigationSuiteScaffoldDefaults {
+ public final class NavigationSuiteScaffoldDefaults {
method public String calculateFromAdaptiveInfo(androidx.compose.material3.adaptive.WindowAdaptiveInfo adaptiveInfo);
method @androidx.compose.runtime.Composable public long getContainerColor();
method @androidx.compose.runtime.Composable public long getContentColor();
@@ -45,16 +45,16 @@
}
public final class NavigationSuiteScaffoldKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi @androidx.compose.runtime.Composable public static void NavigationSuite(optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors colors, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi @androidx.compose.runtime.Composable public static void NavigationSuiteScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope,kotlin.Unit> navigationSuiteItems, optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors navigationSuiteColors, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi @androidx.compose.runtime.Composable public static void NavigationSuiteScaffoldLayout(kotlin.jvm.functions.Function0<kotlin.Unit> navigationSuite, optional String layoutType, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void NavigationSuite(optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors colors, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void NavigationSuiteScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope,kotlin.Unit> navigationSuiteItems, optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors navigationSuiteColors, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void NavigationSuiteScaffoldLayout(kotlin.jvm.functions.Function0<kotlin.Unit> navigationSuite, optional String layoutType, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public sealed interface NavigationSuiteScope {
+ public sealed interface NavigationSuiteScope {
method public void item(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional boolean alwaysShowLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItemColors? colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi @kotlin.jvm.JvmInline public final value class NavigationSuiteType {
+ @kotlin.jvm.JvmInline public final value class NavigationSuiteType {
field public static final androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType.Companion Companion;
}
diff --git a/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt b/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt
index 7a7bc4a..83d23c0 100644
--- a/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt
+++ b/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt
@@ -4,7 +4,7 @@
@SuppressCompatibility @kotlin.RequiresOptIn(message="This material3-adaptive-navigation-suite API is experimental and is likely to" + "change or to be removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3AdaptiveNavigationSuiteApi {
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final class NavigationSuiteColors {
+ public final class NavigationSuiteColors {
method public long getNavigationBarContainerColor();
method public long getNavigationBarContentColor();
method public long getNavigationDrawerContainerColor();
@@ -19,13 +19,13 @@
property public final long navigationRailContentColor;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final class NavigationSuiteDefaults {
+ public final class NavigationSuiteDefaults {
method @androidx.compose.runtime.Composable public androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors colors(optional long navigationBarContainerColor, optional long navigationBarContentColor, optional long navigationRailContainerColor, optional long navigationRailContentColor, optional long navigationDrawerContainerColor, optional long navigationDrawerContentColor);
method @androidx.compose.runtime.Composable public androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItemColors itemColors(optional androidx.compose.material3.NavigationBarItemColors navigationBarItemColors, optional androidx.compose.material3.NavigationRailItemColors navigationRailItemColors, optional androidx.compose.material3.NavigationDrawerItemColors navigationDrawerItemColors);
field public static final androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults INSTANCE;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final class NavigationSuiteItemColors {
+ public final class NavigationSuiteItemColors {
ctor public NavigationSuiteItemColors(androidx.compose.material3.NavigationBarItemColors navigationBarItemColors, androidx.compose.material3.NavigationRailItemColors navigationRailItemColors, androidx.compose.material3.NavigationDrawerItemColors navigationDrawerItemColors);
method public androidx.compose.material3.NavigationBarItemColors getNavigationBarItemColors();
method public androidx.compose.material3.NavigationDrawerItemColors getNavigationDrawerItemColors();
@@ -35,7 +35,7 @@
property public final androidx.compose.material3.NavigationRailItemColors navigationRailItemColors;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final class NavigationSuiteScaffoldDefaults {
+ public final class NavigationSuiteScaffoldDefaults {
method public String calculateFromAdaptiveInfo(androidx.compose.material3.adaptive.WindowAdaptiveInfo adaptiveInfo);
method @androidx.compose.runtime.Composable public long getContainerColor();
method @androidx.compose.runtime.Composable public long getContentColor();
@@ -45,16 +45,16 @@
}
public final class NavigationSuiteScaffoldKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi @androidx.compose.runtime.Composable public static void NavigationSuite(optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors colors, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi @androidx.compose.runtime.Composable public static void NavigationSuiteScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope,kotlin.Unit> navigationSuiteItems, optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors navigationSuiteColors, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi @androidx.compose.runtime.Composable public static void NavigationSuiteScaffoldLayout(kotlin.jvm.functions.Function0<kotlin.Unit> navigationSuite, optional String layoutType, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void NavigationSuite(optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors colors, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void NavigationSuiteScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope,kotlin.Unit> navigationSuiteItems, optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteColors navigationSuiteColors, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void NavigationSuiteScaffoldLayout(kotlin.jvm.functions.Function0<kotlin.Unit> navigationSuite, optional String layoutType, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public sealed interface NavigationSuiteScope {
+ public sealed interface NavigationSuiteScope {
method public void item(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional boolean alwaysShowLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItemColors? colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi @kotlin.jvm.JvmInline public final value class NavigationSuiteType {
+ @kotlin.jvm.JvmInline public final value class NavigationSuiteType {
field public static final androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType.Companion Companion;
}
diff --git a/compose/material3/material3-adaptive-navigation-suite/build.gradle b/compose/material3/material3-adaptive-navigation-suite/build.gradle
index de32cd7..bac0af8 100644
--- a/compose/material3/material3-adaptive-navigation-suite/build.gradle
+++ b/compose/material3/material3-adaptive-navigation-suite/build.gradle
@@ -112,7 +112,7 @@
androidx {
name = "Material Adaptive Navigation Suite"
mavenVersion = LibraryVersions.COMPOSE_MATERIAL3_ADAPTIVE_NAVIGATION_SUITE
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2023"
description = "Compose Material Design Adaptive Navigation Suite Library"
samples(project(":compose:material3:material3-adaptive-navigation-suite:material3-adaptive-navigation-suite-samples"))
diff --git a/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt b/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt
index 45d63a4..8326319 100644
--- a/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt
@@ -27,7 +27,6 @@
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
-import androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuite
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldDefaults
@@ -43,10 +42,7 @@
import androidx.compose.ui.unit.dp
import androidx.window.core.layout.WindowWidthSizeClass
-@OptIn(
- ExperimentalMaterial3AdaptiveApi::class,
- ExperimentalMaterial3AdaptiveNavigationSuiteApi::class
-)
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Preview
@Sampled
@Composable
@@ -76,10 +72,7 @@
}
}
-@OptIn(
- ExperimentalMaterial3AdaptiveApi::class,
- ExperimentalMaterial3AdaptiveNavigationSuiteApi::class
-)
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Preview
@Sampled
@Composable
@@ -117,10 +110,7 @@
}
}
-@OptIn(
- ExperimentalMaterial3AdaptiveApi::class,
- ExperimentalMaterial3AdaptiveNavigationSuiteApi::class
-)
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Preview
@Sampled
@Composable
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt b/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
index 737aa7f..11dfa5f 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
@@ -34,7 +34,6 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
@RunWith(JUnit4::class)
class NavigationSuiteScaffoldTest {
@get:Rule
@@ -92,7 +91,6 @@
}
}
-@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
@Composable
private fun SampleNavigationSuiteScaffoldLayout(
layoutType: NavigationSuiteType
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteTest.kt b/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteTest.kt
index df34599..ea3718c 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteTest.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteTest.kt
@@ -28,7 +28,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class NavigationSuiteTest {
@@ -47,7 +46,6 @@
}
}
-@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
@Composable
private fun SampleNavigationSuite(
layoutType: NavigationSuiteType
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt b/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
index 0183840..12ec173 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
@@ -25,8 +25,7 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class,
- ExperimentalMaterial3AdaptiveApi::class)
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@RunWith(JUnit4::class)
class NavigationSuiteScaffoldTest {
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
index e2055fb..b951ec3 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
@@ -42,7 +42,6 @@
import androidx.compose.material3.PermanentDrawerSheet
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
-import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.contentColorFor
@@ -84,8 +83,6 @@
* passed in [content] lambda inside the navigation suite scaffold.
* @param content the content of your screen
*/
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-@ExperimentalMaterial3AdaptiveNavigationSuiteApi
@Composable
fun NavigationSuiteScaffold(
navigationSuiteItems: NavigationSuiteScope.() -> Unit,
@@ -142,8 +139,6 @@
* [NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo]
* @param content the content of your screen
*/
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-@ExperimentalMaterial3AdaptiveNavigationSuiteApi
@Composable
fun NavigationSuiteScaffoldLayout(
navigationSuite: @Composable () -> Unit,
@@ -221,8 +216,6 @@
* @param content the content inside the current navigation component, typically
* [NavigationSuiteScope.item]s
*/
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-@ExperimentalMaterial3AdaptiveNavigationSuiteApi
@Composable
fun NavigationSuite(
modifier: Modifier = Modifier,
@@ -310,7 +303,6 @@
}
/** The scope associated with the [NavigationSuiteScope]. */
-@ExperimentalMaterial3AdaptiveNavigationSuiteApi
sealed interface NavigationSuiteScope {
/**
@@ -360,7 +352,6 @@
* The [NavigationSuiteType] informs the [NavigationSuite] of what navigation component to expect.
*/
@JvmInline
-@ExperimentalMaterial3AdaptiveNavigationSuiteApi
value class NavigationSuiteType private constructor(private val description: String) {
override fun toString(): String {
return description
@@ -400,7 +391,6 @@
}
/** Contains the default values used by the [NavigationSuiteScaffold]. */
-@ExperimentalMaterial3AdaptiveNavigationSuiteApi
object NavigationSuiteScaffoldDefaults {
/**
* Returns the expected [NavigationSuiteType] according to the provided [WindowAdaptiveInfo].
@@ -409,7 +399,6 @@
* @param adaptiveInfo the provided [WindowAdaptiveInfo]
* @see NavigationSuiteScaffold
*/
- @OptIn(ExperimentalMaterial3AdaptiveApi::class)
fun calculateFromAdaptiveInfo(adaptiveInfo: WindowAdaptiveInfo): NavigationSuiteType {
return with(adaptiveInfo) {
if (windowPosture.isTabletop ||
@@ -434,7 +423,6 @@
}
/** Contains the default values used by the [NavigationSuite]. */
-@ExperimentalMaterial3AdaptiveNavigationSuiteApi
object NavigationSuiteDefaults {
/**
* Creates a [NavigationSuiteColors] with the provided colors for the container color, according
@@ -519,7 +507,6 @@
* @param navigationDrawerContentColor the content color for the [PermanentDrawerSheet] of the
* [NavigationSuite]
*/
-@ExperimentalMaterial3AdaptiveNavigationSuiteApi
class NavigationSuiteColors
internal constructor(
val navigationBarContainerColor: Color,
@@ -543,14 +530,12 @@
* @param navigationDrawerItemColors the [NavigationDrawerItemColors] associated with the
* [NavigationDrawerItem] of the [NavigationSuiteScope.item]
*/
-@ExperimentalMaterial3AdaptiveNavigationSuiteApi
-class NavigationSuiteItemColors constructor(
+class NavigationSuiteItemColors(
val navigationBarItemColors: NavigationBarItemColors,
val navigationRailItemColors: NavigationRailItemColors,
val navigationDrawerItemColors: NavigationDrawerItemColors,
)
-@OptIn(ExperimentalMaterial3AdaptiveApi::class)
internal val WindowAdaptiveInfoDefault
@Composable
get() = currentWindowAdaptiveInfo()
@@ -560,7 +545,6 @@
val itemList: MutableVector<NavigationSuiteItem>
}
-@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
private class NavigationSuiteItem(
val selected: Boolean,
val onClick: () -> Unit,
@@ -575,7 +559,6 @@
val interactionSource: MutableInteractionSource
)
-@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
private class NavigationSuiteScopeImpl : NavigationSuiteScope,
NavigationSuiteItemProvider {
@@ -614,7 +597,6 @@
get() = itemList.size
}
-@OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class)
@Composable
private fun rememberStateOfItems(
content: NavigationSuiteScope.() -> Unit
diff --git a/compose/material3/material3-common/build.gradle b/compose/material3/material3-common/build.gradle
index f41127b..b55c721 100644
--- a/compose/material3/material3-common/build.gradle
+++ b/compose/material3/material3-common/build.gradle
@@ -111,7 +111,7 @@
androidx {
name = "Compose Material 3 Common"
mavenVersion = LibraryVersions.COMPOSE_MATERIAL3_COMMON
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2023"
description = "Compose Material 3 Common Library. This library contains foundational, themeless " +
"components that can be shared between different Material libraries or used by app" +
diff --git a/compose/material3/material3-window-size-class/build.gradle b/compose/material3/material3-window-size-class/build.gradle
index 28239af..638ecf4 100644
--- a/compose/material3/material3-window-size-class/build.gradle
+++ b/compose/material3/material3-window-size-class/build.gradle
@@ -119,7 +119,7 @@
androidx {
name = "Compose Material 3 Window Size Class"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2022"
description = "Provides window size classes for building responsive UIs"
samples(project(":compose:material3:material3-window-size-class:material3-window-size-class-samples"))
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 26756e5..83a3415 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -996,22 +996,24 @@
}
@SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class ModalBottomSheetDefaults {
- method public androidx.compose.material3.ModalBottomSheetProperties properties(optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean isFocusable, optional boolean shouldDismissOnBackPress);
+ method public androidx.compose.material3.ModalBottomSheetProperties getProperties();
+ method @Deprecated public androidx.compose.material3.ModalBottomSheetProperties properties(optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean isFocusable, optional boolean shouldDismissOnBackPress);
+ property public final androidx.compose.material3.ModalBottomSheetProperties properties;
field public static final androidx.compose.material3.ModalBottomSheetDefaults INSTANCE;
}
- @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class ModalBottomSheetProperties {
- ctor public ModalBottomSheetProperties(androidx.compose.ui.window.SecureFlagPolicy securePolicy, boolean isFocusable, boolean shouldDismissOnBackPress);
+ @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class ModalBottomSheetProperties {
+ ctor public ModalBottomSheetProperties(optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean shouldDismissOnBackPress);
+ ctor @Deprecated public ModalBottomSheetProperties(androidx.compose.ui.window.SecureFlagPolicy securePolicy, boolean isFocusable, boolean shouldDismissOnBackPress);
method public androidx.compose.ui.window.SecureFlagPolicy getSecurePolicy();
method public boolean getShouldDismissOnBackPress();
- method public boolean isFocusable();
- property public final boolean isFocusable;
property public final androidx.compose.ui.window.SecureFlagPolicy securePolicy;
property public final boolean shouldDismissOnBackPress;
}
public final class ModalBottomSheet_androidKt {
- method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalBottomSheet(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SheetState sheetState, optional float sheetMaxWidth, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional float tonalElevation, optional long scrimColor, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dragHandle, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.ModalBottomSheetProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalBottomSheet(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SheetState sheetState, optional float sheetMaxWidth, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional float tonalElevation, optional long scrimColor, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dragHandle, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.ModalBottomSheetProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalBottomSheet(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SheetState sheetState, optional float sheetMaxWidth, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional float tonalElevation, optional long scrimColor, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dragHandle, optional kotlin.jvm.functions.Function0<? extends androidx.compose.foundation.layout.WindowInsets> contentWindowInsets, optional androidx.compose.material3.ModalBottomSheetProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.SheetState rememberModalBottomSheetState(optional boolean skipPartiallyExpanded, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
}
@@ -1253,12 +1255,10 @@
}
@SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class RippleConfiguration {
- ctor public RippleConfiguration(optional boolean isEnabled, optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
+ ctor public RippleConfiguration(optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
method public long getColor();
method public androidx.compose.material.ripple.RippleAlpha? getRippleAlpha();
- method public boolean isEnabled();
property public final long color;
- property public final boolean isEnabled;
property public final androidx.compose.material.ripple.RippleAlpha? rippleAlpha;
}
@@ -1269,11 +1269,11 @@
}
public final class RippleKt {
- method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.RippleConfiguration> getLocalRippleConfiguration();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.RippleConfiguration?> getLocalRippleConfiguration();
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalUseFallbackRippleImplementation();
method @androidx.compose.runtime.Stable public static androidx.compose.foundation.IndicationNodeFactory ripple(androidx.compose.ui.graphics.ColorProducer color, optional boolean bounded, optional float radius);
method @androidx.compose.runtime.Stable public static androidx.compose.foundation.IndicationNodeFactory ripple(optional boolean bounded, optional float radius, optional long color);
- property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.RippleConfiguration> LocalRippleConfiguration;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.RippleConfiguration?> LocalRippleConfiguration;
property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalUseFallbackRippleImplementation;
}
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 26756e5..83a3415 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -996,22 +996,24 @@
}
@SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class ModalBottomSheetDefaults {
- method public androidx.compose.material3.ModalBottomSheetProperties properties(optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean isFocusable, optional boolean shouldDismissOnBackPress);
+ method public androidx.compose.material3.ModalBottomSheetProperties getProperties();
+ method @Deprecated public androidx.compose.material3.ModalBottomSheetProperties properties(optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean isFocusable, optional boolean shouldDismissOnBackPress);
+ property public final androidx.compose.material3.ModalBottomSheetProperties properties;
field public static final androidx.compose.material3.ModalBottomSheetDefaults INSTANCE;
}
- @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class ModalBottomSheetProperties {
- ctor public ModalBottomSheetProperties(androidx.compose.ui.window.SecureFlagPolicy securePolicy, boolean isFocusable, boolean shouldDismissOnBackPress);
+ @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class ModalBottomSheetProperties {
+ ctor public ModalBottomSheetProperties(optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean shouldDismissOnBackPress);
+ ctor @Deprecated public ModalBottomSheetProperties(androidx.compose.ui.window.SecureFlagPolicy securePolicy, boolean isFocusable, boolean shouldDismissOnBackPress);
method public androidx.compose.ui.window.SecureFlagPolicy getSecurePolicy();
method public boolean getShouldDismissOnBackPress();
- method public boolean isFocusable();
- property public final boolean isFocusable;
property public final androidx.compose.ui.window.SecureFlagPolicy securePolicy;
property public final boolean shouldDismissOnBackPress;
}
public final class ModalBottomSheet_androidKt {
- method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalBottomSheet(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SheetState sheetState, optional float sheetMaxWidth, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional float tonalElevation, optional long scrimColor, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dragHandle, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.ModalBottomSheetProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalBottomSheet(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SheetState sheetState, optional float sheetMaxWidth, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional float tonalElevation, optional long scrimColor, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dragHandle, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.ModalBottomSheetProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalBottomSheet(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SheetState sheetState, optional float sheetMaxWidth, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional float tonalElevation, optional long scrimColor, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dragHandle, optional kotlin.jvm.functions.Function0<? extends androidx.compose.foundation.layout.WindowInsets> contentWindowInsets, optional androidx.compose.material3.ModalBottomSheetProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.SheetState rememberModalBottomSheetState(optional boolean skipPartiallyExpanded, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
}
@@ -1253,12 +1255,10 @@
}
@SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class RippleConfiguration {
- ctor public RippleConfiguration(optional boolean isEnabled, optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
+ ctor public RippleConfiguration(optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
method public long getColor();
method public androidx.compose.material.ripple.RippleAlpha? getRippleAlpha();
- method public boolean isEnabled();
property public final long color;
- property public final boolean isEnabled;
property public final androidx.compose.material.ripple.RippleAlpha? rippleAlpha;
}
@@ -1269,11 +1269,11 @@
}
public final class RippleKt {
- method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.RippleConfiguration> getLocalRippleConfiguration();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.RippleConfiguration?> getLocalRippleConfiguration();
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalUseFallbackRippleImplementation();
method @androidx.compose.runtime.Stable public static androidx.compose.foundation.IndicationNodeFactory ripple(androidx.compose.ui.graphics.ColorProducer color, optional boolean bounded, optional float radius);
method @androidx.compose.runtime.Stable public static androidx.compose.foundation.IndicationNodeFactory ripple(optional boolean bounded, optional float radius, optional long color);
- property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.RippleConfiguration> LocalRippleConfiguration;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.RippleConfiguration?> LocalRippleConfiguration;
property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalUseFallbackRippleImplementation;
}
diff --git a/compose/material3/material3/build.gradle b/compose/material3/material3/build.gradle
index cadd1ea..36f7bb2c 100644
--- a/compose/material3/material3/build.gradle
+++ b/compose/material3/material3/build.gradle
@@ -143,7 +143,7 @@
androidx {
name = "Compose Material3 Components"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "Compose Material You Design Components library"
samples(project(":compose:material3:material3:material3-samples"))
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/common/CatalogScaffold.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/common/CatalogScaffold.kt
index 6156e44..7a50700 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/common/CatalogScaffold.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/common/CatalogScaffold.kt
@@ -17,7 +17,6 @@
package androidx.compose.material3.catalog.library.ui.common
import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold
@@ -93,7 +92,6 @@
ModalBottomSheet(
onDismissRequest = { openThemePicker = false },
sheetState = sheetState,
- windowInsets = WindowInsets(0),
content = {
ThemePicker(
theme = theme,
diff --git a/compose/material3/material3/lint-baseline.xml b/compose/material3/material3/lint-baseline.xml
index c30eb05..6e32e2f 100644
--- a/compose/material3/material3/lint-baseline.xml
+++ b/compose/material3/material3/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -222,13 +222,6 @@
</issue>
<issue
- id="UnsafeOptInUsageError"
- message="This declaration is opt-in and its usage should be marked with `@androidx.compose.material3.ExperimentalMaterial3Api` or `@OptIn(markerClass = androidx.compose.material3.ExperimentalMaterial3Api.class)`">
- <location
- file="src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt"/>
- </issue>
-
- <issue
id="PrimitiveInCollection"
message="variable crossAxisSizes with type List<Integer>: replace with IntList"
errorLine1=" val crossAxisSizes = mutableListOf<Int>()"
@@ -248,42 +241,6 @@
<issue
id="PrimitiveInCollection"
- message="field anchors with type Map<T, Float>: replace with ObjectFloatMap"
- errorLine1=" internal val anchors = mutableMapOf<T, Float>()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/material3/AnchoredDraggable.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="return type Map<T, Float> of getAnchors$lint_module: replace with ObjectFloatMap"
- errorLine1=" internal val anchors = mutableMapOf<T, Float>()"
- errorLine2=" ~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/material3/AnchoredDraggable.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="constructor MapDraggableAnchors has parameter anchors with type Map<T, Float>: replace with ObjectFloatMap"
- errorLine1="private class MapDraggableAnchors<T>(private val anchors: Map<T, Float>) : DraggableAnchors<T> {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/material3/AnchoredDraggable.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
- message="field anchors with type Map<T, Float>: replace with ObjectFloatMap"
- errorLine1="private class MapDraggableAnchors<T>(private val anchors: Map<T, Float>) : DraggableAnchors<T> {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/material3/AnchoredDraggable.kt"/>
- </issue>
-
- <issue
- id="PrimitiveInCollection"
message="variable tabContentWidths with type List<Dp>: replace with FloatList"
errorLine1=" val tabContentWidths = mutableListOf<Dp>()"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/BottomSheetSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/BottomSheetSamples.kt
index ae1e46b..b720202 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/BottomSheetSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/BottomSheetSamples.kt
@@ -23,7 +23,6 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -34,7 +33,6 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Menu
-import androidx.compose.material3.BottomSheetDefaults
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
@@ -73,8 +71,7 @@
@Composable
fun ModalBottomSheetSample() {
var openBottomSheet by rememberSaveable { mutableStateOf(false) }
- var skipPartiallyExpanded by remember { mutableStateOf(false) }
- var edgeToEdgeEnabled by remember { mutableStateOf(false) }
+ var skipPartiallyExpanded by rememberSaveable { mutableStateOf(false) }
val scope = rememberCoroutineScope()
val bottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = skipPartiallyExpanded
@@ -96,17 +93,6 @@
Spacer(Modifier.width(16.dp))
Text("Skip partially expanded State")
}
- Row(
- Modifier.toggleable(
- value = edgeToEdgeEnabled,
- role = Role.Checkbox,
- onValueChange = { checked -> edgeToEdgeEnabled = checked }
- )
- ) {
- Checkbox(checked = edgeToEdgeEnabled, onCheckedChange = null)
- Spacer(Modifier.width(16.dp))
- Text("Toggle edge to edge enabled")
- }
Button(
onClick = { openBottomSheet = !openBottomSheet },
modifier = Modifier.align(Alignment.CenterHorizontally)
@@ -117,15 +103,11 @@
// Sheet content
if (openBottomSheet) {
- val windowInsets = if (edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = { openBottomSheet = false },
sheetState = bottomSheetState,
- windowInsets = windowInsets
) {
-
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Button(
// Note: If you provide logic outside of onDismissRequest to remove the sheet,
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt
index 34aa79f..d763b89 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SliderSamples.kt
@@ -137,7 +137,9 @@
// launch some business logic update with the state you hold
// viewModel.updateSelectedSliderValue(sliderPosition)
},
- steps = 4
+ // Only allow multiples of 10. Excluding the endpoints of `valueRange`,
+ // there are 9 steps (10, 20, ..., 90).
+ steps = 9
)
}
}
@@ -344,7 +346,9 @@
// launch some business logic update with the state you hold
// viewModel.updateSelectedSliderValue(sliderPosition)
},
- steps = 4
+ // Only allow multiples of 10. Excluding the endpoints of `valueRange`,
+ // there are 9 steps (10, 20, ..., 90).
+ steps = 9
)
}
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SwitchSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SwitchSamples.kt
index 9fbb12a..a427429 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SwitchSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SwitchSamples.kt
@@ -49,23 +49,20 @@
@Composable
fun SwitchWithThumbIconSample() {
var checked by remember { mutableStateOf(true) }
- // Icon isn't focusable, no need for content description
- val icon: (@Composable () -> Unit)? = if (checked) {
- {
- Icon(
- imageVector = Icons.Filled.Check,
- contentDescription = null,
- modifier = Modifier.size(SwitchDefaults.IconSize),
- )
- }
- } else {
- null
- }
Switch(
modifier = Modifier.semantics { contentDescription = "Demo with icon" },
checked = checked,
onCheckedChange = { checked = it },
- thumbContent = icon
+ thumbContent = {
+ if (checked) {
+ // Icon isn't focusable, no need for content description
+ Icon(
+ imageVector = Icons.Filled.Check,
+ contentDescription = null,
+ modifier = Modifier.size(SwitchDefaults.IconSize),
+ )
+ }
+ }
)
}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
index a3464b2..6bdf964 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
@@ -57,11 +57,12 @@
import androidx.compose.ui.semantics.SemanticsActions
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHeightIsEqualTo
import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
import androidx.compose.ui.test.assertWidthIsEqualTo
import androidx.compose.ui.test.getUnclippedBoundsInRoot
-import androidx.compose.ui.test.isPopup
+import androidx.compose.ui.test.isDialog
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithTag
@@ -79,6 +80,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.height
import androidx.compose.ui.unit.width
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -96,12 +98,11 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
@MediumTest
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalMaterial3Api::class)
-class ModalBottomSheetTest(private val edgeToEdgeWrapper: EdgeToEdgeWrapper) {
+class ModalBottomSheetTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@@ -119,14 +120,10 @@
val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
rule.setContent {
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
-
if (showBottomSheet) {
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = { showBottomSheet = false },
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -141,7 +138,7 @@
// Tap Scrim
val outsideY = with(rule.density) {
- rule.onAllNodes(isPopup()).onFirst().getUnclippedBoundsInRoot().height.roundToPx() / 4
+ rule.onAllNodes(isDialog()).onFirst().getUnclippedBoundsInRoot().height.roundToPx() / 4
}
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).click(0, outsideY)
rule.waitForIdle()
@@ -156,14 +153,10 @@
val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
rule.setContent {
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
-
if (showBottomSheet) {
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = { showBottomSheet = false },
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -194,12 +187,9 @@
rule.setContent {
val context = LocalContext.current
screenWidth = context.resources.displayMetrics.widthPixels
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -235,11 +225,8 @@
try {
latch.await(1500, TimeUnit.MILLISECONDS)
rule.setContent {
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -249,7 +236,7 @@
}
}
rule.waitForIdle()
- val simulatedRootWidth = rule.onNode(isPopup()).getUnclippedBoundsInRoot().width
+ val simulatedRootWidth = rule.onNode(isDialog()).getUnclippedBoundsInRoot().width
val maxSheetWidth = 640.dp
val expectedSheetWidth = maxSheetWidth.coerceAtMost(simulatedRootWidth)
// Our sheet should be max 640 dp but fill the width if the container is less wide
@@ -299,12 +286,9 @@
rule.setContent {
val context = LocalContext.current
screenWidthPx = context.resources.displayMetrics.widthPixels
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetMaxWidth = Dp.Unspecified,
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -330,17 +314,13 @@
var height by mutableStateOf(0.dp)
rule.setContent {
- val config = LocalContext.current.resources.configuration
- height = config.screenHeightDp.dp
sheetState = rememberModalBottomSheetState()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
dragHandle = null,
- windowInsets = windowInsets
+ contentWindowInsets = { WindowInsets(0) }
) {
Box(
Modifier
@@ -351,7 +331,7 @@
}
}
- height = rule.onNode(isPopup()).getUnclippedBoundsInRoot().height
+ height = rule.onNode(isDialog()).getUnclippedBoundsInRoot().height
assertThat(sheetState.currentValue).isEqualTo(SheetValue.Expanded)
rule.onNodeWithTag(sheetTag).assertTopPositionInRootIsEqualTo(height - sheetHeight)
}
@@ -363,12 +343,9 @@
rule.setContent {
sheetState = rememberModalBottomSheetState()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -380,7 +357,7 @@
}
rule.waitForIdle()
screenHeightPx = with(rule.density) {
- rule.onNode(isPopup()).getUnclippedBoundsInRoot().height.toPx()
+ rule.onNode(isDialog()).getUnclippedBoundsInRoot().height.toPx()
}
assertThat(sheetState.targetValue).isEqualTo(SheetValue.PartiallyExpanded)
assertThat(sheetState.requireOffset())
@@ -395,13 +372,10 @@
rule.setContent {
val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
if (showBottomSheet) {
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = { showBottomSheet = false },
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -435,13 +409,10 @@
rule.setContent {
val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
if (showBottomSheet) {
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = { showBottomSheet = false },
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -481,13 +452,11 @@
val context = LocalContext.current
screenHeight = context.resources.configuration.screenHeightDp.dp
state = rememberModalBottomSheetState()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = state,
dragHandle = null,
- windowInsets = windowInsets
+ contentWindowInsets = { WindowInsets(0) }
) {
Box(
Modifier
@@ -496,7 +465,7 @@
)
}
}
- screenHeight = rule.onNode(isPopup()).getUnclippedBoundsInRoot().height
+ screenHeight = rule.onNode(isDialog()).getUnclippedBoundsInRoot().height
assertThat(state.requireOffset()).isWithin(1f).of(expectedExpandedAnchor)
size = 100.dp
@@ -513,8 +482,6 @@
lateinit var sheetMaxWidth: MutableState<Dp>
var screenWidth by mutableStateOf(0.dp)
rule.setContent {
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
sheetMaxWidth = remember { mutableStateOf(0.dp) }
val context = LocalContext.current
val density = LocalDensity.current
@@ -522,7 +489,6 @@
ModalBottomSheet(
onDismissRequest = {},
sheetMaxWidth = sheetMaxWidth.value,
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -547,14 +513,12 @@
rule.setContent {
state = rememberModalBottomSheetState()
scope = rememberCoroutineScope()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = state,
dragHandle = null,
- windowInsets = windowInsets
+ contentWindowInsets = { WindowInsets(0) }
) {}
}
assertThat(state.anchoredDraggableState.currentValue).isEqualTo(SheetValue.Hidden)
@@ -575,14 +539,11 @@
lateinit var scope: CoroutineScope
rule.setContent {
state = rememberModalBottomSheetState()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
-
ModalBottomSheet(
onDismissRequest = {},
sheetState = state,
dragHandle = null,
- windowInsets = windowInsets
+ contentWindowInsets = { WindowInsets(0) }
) {
scope = rememberCoroutineScope()
LazyColumn {
@@ -630,7 +591,7 @@
sheetState = rememberModalBottomSheetState()
ModalBottomSheet(
onDismissRequest = {},
- sheetState = sheetState
+ sheetState = sheetState,
) {
scrollState = rememberScrollState()
Column(
@@ -700,14 +661,10 @@
rule.setContent {
scope = rememberCoroutineScope()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
-
ModalBottomSheet(
onDismissRequest = {},
modifier = Modifier.testTag(topTag),
sheetState = sheetState,
- windowInsets = windowInsets
) {
if (showShortContent) {
Box(
@@ -748,12 +705,9 @@
lateinit var sheetState: SheetState
rule.setContent {
sheetState = rememberModalBottomSheetState()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -784,8 +738,6 @@
newState != SheetValue.Hidden
}
)
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
@@ -797,7 +749,6 @@
.size(dragHandleSize)
)
},
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -827,7 +778,7 @@
// Tap Scrim
val outsideY = with(rule.density) {
- rule.onAllNodes(isPopup()).onFirst().getUnclippedBoundsInRoot().height.roundToPx() / 4
+ rule.onAllNodes(isDialog()).onFirst().getUnclippedBoundsInRoot().height.roundToPx() / 4
}
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).click(0, outsideY)
rule.waitForIdle()
@@ -843,12 +794,9 @@
rule.setContent {
sheetState = rememberModalBottomSheetState()
scope = rememberCoroutineScope()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -880,13 +828,10 @@
lateinit var sheetState: SheetState
rule.setContent {
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -916,12 +861,9 @@
lateinit var sheetState: SheetState
rule.setContent {
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -946,13 +888,9 @@
)
rule.setContent {
scope = rememberCoroutineScope()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
-
ModalBottomSheet(
onDismissRequest = {},
sheetState = bottomSheetState,
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -976,8 +914,6 @@
@Test
fun modalBottomSheet_testDismissAction_tallBottomSheet_whenPartiallyExpanded() {
rule.setContent {
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
dragHandle = {
@@ -987,7 +923,6 @@
.size(dragHandleSize)
)
},
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -1009,8 +944,6 @@
lateinit var sheetState: SheetState
rule.setContent {
sheetState = rememberModalBottomSheetState()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
@@ -1021,7 +954,6 @@
.size(dragHandleSize)
)
},
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -1052,9 +984,6 @@
rule.setContent {
sheetState = rememberModalBottomSheetState()
scope = rememberCoroutineScope()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
-
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
@@ -1065,7 +994,6 @@
.size(dragHandleSize)
)
},
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -1075,7 +1003,7 @@
}
}
screenHeightPx = with(rule.density) {
- rule.onNode(isPopup()).getUnclippedBoundsInRoot().height.toPx()
+ rule.onNode(isDialog()).getUnclippedBoundsInRoot().height.toPx()
}
scope.launch {
sheetState.expand()
@@ -1103,9 +1031,6 @@
rule.setContent {
sheetState = rememberModalBottomSheetState()
scope = rememberCoroutineScope()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
-
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
@@ -1116,7 +1041,6 @@
.size(dragHandleSize)
)
},
- windowInsets = windowInsets
) {
Box(
Modifier
@@ -1126,7 +1050,7 @@
}
}
screenHeightPx = with(rule.density) {
- rule.onNode(isPopup()).getUnclippedBoundsInRoot().height.toPx()
+ rule.onNode(isDialog()).getUnclippedBoundsInRoot().height.toPx()
}
scope.launch {
sheetState.expand()
@@ -1151,14 +1075,12 @@
lateinit var scope: CoroutineScope
rule.setContent {
scope = rememberCoroutineScope()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
dragHandle = null,
- windowInsets = windowInsets
+ contentWindowInsets = { WindowInsets(0) }
) {
if (hasSheetContent) {
Box(Modifier.fillMaxHeight(0.4f))
@@ -1190,14 +1112,11 @@
lateinit var scope: CoroutineScope
rule.setContent {
scope = rememberCoroutineScope()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
-
ModalBottomSheet(
onDismissRequest = {},
sheetState = sheetState,
dragHandle = null,
- windowInsets = windowInsets
+ contentWindowInsets = { WindowInsets(0) }
) {
if (hasSheetContent) {
Box(Modifier.fillMaxHeight(0.6f))
@@ -1236,13 +1155,9 @@
rule.setContent {
scope = rememberCoroutineScope()
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
-
ModalBottomSheet(
onDismissRequest = { callCount += 1 },
sheetState = sheetState,
- windowInsets = windowInsets
) {
Column(
Modifier
@@ -1288,14 +1203,10 @@
lateinit var sheetState: SheetState
rule.setContent {
- val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
- WindowInsets(0) else BottomSheetDefaults.windowInsets
sheetState = rememberModalBottomSheetState()
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = {},
- windowInsets = windowInsets,
- properties = ModalBottomSheetDefaults.properties(isFocusable = true)
) {
Box(Modifier.testTag(sheetTag)) {
TextField(
@@ -1311,7 +1222,7 @@
rule.mainClock.autoAdvance = false
val textFieldNode = rule.onNodeWithTag(textFieldTag)
- var sheetNode = rule.onNodeWithTag(sheetTag)
+ val sheetNode = rule.onNodeWithTag(sheetTag)
val initialTop = sheetNode.getUnclippedBoundsInRoot().top
// Focus on the text field to force ime visibility.
@@ -1331,7 +1242,9 @@
var value = LayoutDirection.Ltr
rule.setContent {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
- ModalBottomSheet(onDismissRequest = { /*TODO*/ }) {
+ ModalBottomSheet(
+ onDismissRequest = { /*TODO*/ },
+ ) {
value = LocalLayoutDirection.current
}
}
@@ -1341,18 +1254,18 @@
}
}
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun parameters() = arrayOf(
- EdgeToEdgeWrapper("EdgeToEdge", true),
- EdgeToEdgeWrapper("NonEdgeToEdge", false)
- )
- }
-
- class EdgeToEdgeWrapper(val name: String, val edgeToEdgeEnabled: Boolean) {
- override fun toString(): String {
- return name
+ @Test
+ fun modalBottomSheetContent_respectsProvidedInsets() {
+ rule.setContent {
+ ModalBottomSheet(
+ onDismissRequest = { /*TODO*/ },
+ dragHandle = {},
+ contentWindowInsets = { WindowInsets(bottom = sheetHeight) },
+ ) {
+ Box(Modifier.testTag(sheetTag))
+ }
}
+ // Size entirely filled by padding provided by WindowInsetPadding
+ rule.onNodeWithTag(sheetTag).onParent().assertHeightIsEqualTo(sheetHeight)
}
}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/RippleTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/RippleTest.kt
index a802cae..80cc29d 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/RippleTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/RippleTest.kt
@@ -503,10 +503,6 @@
fun rippleConfiguration_disabled_dragged() {
val interactionSource = MutableInteractionSource()
- val rippleConfiguration = RippleConfiguration(
- isEnabled = false
- )
-
var scope: CoroutineScope? = null
rule.setContent {
@@ -514,7 +510,7 @@
MaterialTheme {
Surface {
CompositionLocalProvider(
- LocalRippleConfiguration provides rippleConfiguration
+ LocalRippleConfiguration provides null
) {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
RippleBoxWithBackground(
@@ -560,7 +556,7 @@
val contentColor = Color.Black
- var rippleConfiguration by mutableStateOf(RippleConfiguration())
+ var rippleConfiguration: RippleConfiguration? by mutableStateOf(RippleConfiguration())
var scope: CoroutineScope? = null
@@ -607,7 +603,6 @@
}
val newConfiguration = RippleConfiguration(
- isEnabled = true,
color = Color.Red,
rippleAlpha = RippleAlpha(0.5f, 0.5f, 0.5f, 0.5f)
)
@@ -635,7 +630,7 @@
}
rule.runOnUiThread {
- rippleConfiguration = RippleConfiguration(isEnabled = false)
+ rippleConfiguration = null
}
with(rule.onNodeWithTag(Tag)) {
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt
index 9bfec4d..e441735 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt
@@ -17,17 +17,20 @@
package androidx.compose.material3
import android.content.Context
-import android.graphics.PixelFormat
+import android.graphics.Outline
import android.os.Build
-import android.view.Gravity
-import android.view.KeyEvent
+import android.view.ContextThemeWrapper
+import android.view.MotionEvent
import android.view.View
-import android.view.ViewTreeObserver
+import android.view.ViewOutlineProvider
+import android.view.Window
import android.view.WindowManager
import android.window.BackEvent
import android.window.OnBackAnimationCallback
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
+import androidx.activity.ComponentDialog
+import androidx.activity.addCallback
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.compose.animation.core.Animatable
@@ -62,6 +65,7 @@
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -72,6 +76,7 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.R
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.GraphicsLayerScope
import androidx.compose.ui.graphics.Shape
@@ -81,21 +86,25 @@
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.AbstractComposeView
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.platform.ViewRootForInspector
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.collapse
+import androidx.compose.ui.semantics.dialog
import androidx.compose.ui.semantics.dismiss
import androidx.compose.ui.semantics.expand
import androidx.compose.ui.semantics.paneTitle
-import androidx.compose.ui.semantics.popup
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
+import androidx.compose.ui.window.DialogWindowProvider
import androidx.compose.ui.window.SecureFlagPolicy
+import androidx.core.view.WindowCompat
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.setViewTreeLifecycleOwner
@@ -139,10 +148,10 @@
* darker color in light theme and lighter color in dark theme. See also: [Surface].
* @param scrimColor Color of the scrim that obscures content when the bottom sheet is open.
* @param dragHandle Optional visual marker to swipe the bottom sheet.
- * @param windowInsets window insets to be passed to the bottom sheet window via [PaddingValues]
+ * @param contentWindowInsets window insets to be passed to the bottom sheet content via [PaddingValues]
* params.
* @param properties [ModalBottomSheetProperties] for further customization of this
- * modal bottom sheet's behavior.
+ * modal bottom sheet's window behavior.
* @param content The content to be displayed inside the bottom sheet.
*/
@Composable
@@ -158,8 +167,8 @@
tonalElevation: Dp = 0.dp,
scrimColor: Color = BottomSheetDefaults.ScrimColor,
dragHandle: @Composable (() -> Unit)? = { BottomSheetDefaults.DragHandle() },
- windowInsets: WindowInsets = BottomSheetDefaults.windowInsets,
- properties: ModalBottomSheetProperties = ModalBottomSheetDefaults.properties(),
+ contentWindowInsets: @Composable () -> WindowInsets = { BottomSheetDefaults.windowInsets },
+ properties: ModalBottomSheetProperties = ModalBottomSheetDefaults.properties,
content: @Composable ColumnScope.() -> Unit,
) {
val scope = rememberCoroutineScope()
@@ -180,7 +189,7 @@
val predictiveBackProgress = remember { Animatable(initialValue = 0f) }
- ModalBottomSheetPopup(
+ ModalBottomSheetDialog(
properties = properties,
onDismissRequest = {
if (sheetState.currentValue == Expanded && sheetState.hasPartiallyExpandedState) {
@@ -194,9 +203,13 @@
}
},
predictiveBackProgress = predictiveBackProgress,
- windowInsets = windowInsets,
) {
- Box(Modifier.fillMaxSize(), propagateMinConstraints = false) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .imePadding(),
+ propagateMinConstraints = false
+ ) {
Scrim(
color = scrimColor,
onDismissRequest = animateToDismiss,
@@ -215,6 +228,7 @@
contentColor,
tonalElevation,
dragHandle,
+ contentWindowInsets,
content
)
}
@@ -241,6 +255,7 @@
contentColor: Color = contentColorFor(containerColor),
tonalElevation: Dp = BottomSheetDefaults.Elevation,
dragHandle: @Composable (() -> Unit)? = { BottomSheetDefaults.DragHandle() },
+ contentWindowInsets: @Composable () -> WindowInsets = { BottomSheetDefaults.windowInsets },
content: @Composable ColumnScope.() -> Unit
) {
val bottomSheetPaneTitle = getString(string = Strings.BottomSheetPaneTitle)
@@ -314,6 +329,7 @@
Column(
Modifier
.fillMaxWidth()
+ .windowInsetsPadding(contentWindowInsets())
.graphicsLayer {
val progress = predictiveBackProgress.value
val predictiveBackScaleX = calculatePredictiveBackScaleX(progress)
@@ -392,39 +408,46 @@
}
}
+// Logic forked from androidx.compose.ui.window.DialogProperties. Removed dismissOnClickOutside
+// and usePlatformDefaultWidth as they are not relevant for fullscreen experience.
/**
* Properties used to customize the behavior of a [ModalBottomSheet].
*
* @param securePolicy Policy for setting [WindowManager.LayoutParams.FLAG_SECURE] on the bottom
* sheet's window.
- * @param isFocusable Whether the modal bottom sheet is focusable. When true,
- * the modal bottom sheet will receive IME events and key presses, such as when
- * the back button is pressed.
* @param shouldDismissOnBackPress Whether the modal bottom sheet can be dismissed by pressing
* the back button. If true, pressing the back button will call onDismissRequest.
- * Note that [isFocusable] must be set to true in order to receive key events such as
- * the back button - if the modal bottom sheet is not focusable then this property does nothing.
*/
+@Immutable
@ExperimentalMaterial3Api
class ModalBottomSheetProperties(
- val securePolicy: SecureFlagPolicy,
- val isFocusable: Boolean,
- val shouldDismissOnBackPress: Boolean
+ val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
+ val shouldDismissOnBackPress: Boolean = true,
) {
+ @Deprecated(
+ message = "'isFocusable' param is no longer used. Use constructor without this parameter.",
+ level = DeprecationLevel.WARNING,
+ replaceWith = ReplaceWith(
+ "ModalBottomSheetProperties(securePolicy, shouldDismissOnBackPress)"
+ )
+ )
+ @Suppress("UNUSED_PARAMETER")
+ constructor(
+ securePolicy: SecureFlagPolicy,
+ isFocusable: Boolean,
+ shouldDismissOnBackPress: Boolean,
+ ) : this(securePolicy, shouldDismissOnBackPress)
+
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ModalBottomSheetProperties) return false
-
if (securePolicy != other.securePolicy) return false
- if (isFocusable != other.isFocusable) return false
- if (shouldDismissOnBackPress != other.shouldDismissOnBackPress) return false
return true
}
override fun hashCode(): Int {
var result = securePolicy.hashCode()
- result = 31 * result + isFocusable.hashCode()
result = 31 * result + shouldDismissOnBackPress.hashCode()
return result
}
@@ -436,6 +459,11 @@
@Immutable
@ExperimentalMaterial3Api
object ModalBottomSheetDefaults {
+
+ /**
+ * Properties used to customize the behavior of a [ModalBottomSheet]. */
+ val properties = ModalBottomSheetProperties()
+
/**
* Properties used to customize the behavior of a [ModalBottomSheet].
*
@@ -449,11 +477,17 @@
* Note that [isFocusable] must be set to true in order to receive key events such as
* the back button - if the modal bottom sheet is not focusable then this property does nothing.
*/
+ @Deprecated(
+ level = DeprecationLevel.WARNING,
+ message = "'isFocusable' param is no longer used. Use value without this parameter.",
+ replaceWith = ReplaceWith("properties")
+ )
+ @Suppress("UNUSED_PARAMETER")
fun properties(
securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
isFocusable: Boolean = true,
shouldDismissOnBackPress: Boolean = true
- ) = ModalBottomSheetProperties(securePolicy, isFocusable, shouldDismissOnBackPress)
+ ) = ModalBottomSheetProperties(securePolicy, shouldDismissOnBackPress)
}
/**
@@ -475,6 +509,95 @@
initialValue = Hidden,
)
+/**
+ * <a href="https://m3.material.io/components/bottom-sheets/overview" class="external" target="_blank">Material Design modal bottom sheet</a>.
+ *
+ * Modal bottom sheets are used as an alternative to inline menus or simple dialogs on mobile,
+ * especially when offering a long list of action items, or when items require longer descriptions
+ * and icons. Like dialogs, modal bottom sheets appear in front of app content, disabling all other
+ * app functionality when they appear, and remaining on screen until confirmed, dismissed, or a
+ * required action has been taken.
+ *
+ * 
+ *
+ * A simple example of a modal bottom sheet looks like this:
+ *
+ * @sample androidx.compose.material3.samples.ModalBottomSheetSample
+ *
+ * @param onDismissRequest Executes when the user clicks outside of the bottom sheet, after sheet
+ * animates to [Hidden].
+ * @param modifier Optional [Modifier] for the bottom sheet.
+ * @param sheetState The state of the bottom sheet.
+ * @param sheetMaxWidth [Dp] that defines what the maximum width the sheet will take.
+ * Pass in [Dp.Unspecified] for a sheet that spans the entire screen width.
+ * @param shape The shape of the bottom sheet.
+ * @param containerColor The color used for the background of this bottom sheet
+ * @param contentColor The preferred color for content inside this bottom sheet. Defaults to either
+ * the matching content color for [containerColor], or to the current [LocalContentColor] if
+ * [containerColor] is not a color from the theme.
+ * @param tonalElevation when [containerColor] 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 scrimColor Color of the scrim that obscures content when the bottom sheet is open.
+ * @param dragHandle Optional visual marker to swipe the bottom sheet.
+ * @param windowInsets window insets to be passed to the bottom sheet content via [PaddingValues]
+ * params.
+ * @param properties [ModalBottomSheetProperties] for further customization of this
+ * modal bottom sheet's window behavior.
+ * @param content The content to be displayed inside the bottom sheet.
+ */
+@Composable
+@ExperimentalMaterial3Api
+@Deprecated(
+ level = DeprecationLevel.HIDDEN,
+ message = "Use constructor with contentWindowInsets parameter.",
+ replaceWith = ReplaceWith(
+ "ModalBottomSheet(" +
+ "onDismissRequest," +
+ "modifier," +
+ "sheetState," +
+ "sheetMaxWidth," +
+ "shape," +
+ "containerColor," +
+ "contentColor," +
+ "tonalElevation," +
+ "scrimColor," +
+ "dragHandle," +
+ "{ windowInsets }," +
+ "properties," +
+ "content," +
+ ")")
+)
+fun ModalBottomSheet(
+ onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
+ sheetState: SheetState = rememberModalBottomSheetState(),
+ sheetMaxWidth: Dp = BottomSheetDefaults.SheetMaxWidth,
+ shape: Shape = BottomSheetDefaults.ExpandedShape,
+ containerColor: Color = BottomSheetDefaults.ContainerColor,
+ contentColor: Color = contentColorFor(containerColor),
+ tonalElevation: Dp = 0.dp,
+ scrimColor: Color = BottomSheetDefaults.ScrimColor,
+ dragHandle: @Composable (() -> Unit)? = { BottomSheetDefaults.DragHandle() },
+ windowInsets: WindowInsets = BottomSheetDefaults.windowInsets,
+ properties: ModalBottomSheetProperties = ModalBottomSheetDefaults.properties,
+ content: @Composable ColumnScope.() -> Unit,
+) = ModalBottomSheet(
+ onDismissRequest = onDismissRequest,
+ modifier = modifier,
+ sheetState = sheetState,
+ sheetMaxWidth = sheetMaxWidth,
+ shape = shape,
+ containerColor = containerColor,
+ contentColor = contentColor,
+ tonalElevation = tonalElevation,
+ scrimColor = scrimColor,
+ dragHandle = dragHandle,
+ contentWindowInsets = { windowInsets },
+ properties = properties,
+ content = content,
+)
+
@Composable
private fun Scrim(
color: Color,
@@ -507,195 +630,96 @@
}
}
-/**
- * Popup specific for modal bottom sheet.
- */
+// Fork of androidx.compose.ui.window.AndroidDialog_androidKt.Dialog
+// Added predictiveBackProgress param to pass into BottomSheetDialogWrapper.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-internal fun ModalBottomSheetPopup(
- properties: ModalBottomSheetProperties,
+private fun ModalBottomSheetDialog(
onDismissRequest: () -> Unit,
+ properties: ModalBottomSheetProperties,
predictiveBackProgress: Animatable<Float, AnimationVector1D>,
- windowInsets: WindowInsets,
- content: @Composable () -> Unit,
+ content: @Composable () -> Unit
) {
val view = LocalView.current
- val id = rememberSaveable { UUID.randomUUID() }
- val parentComposition = rememberCompositionContext()
- val currentContent by rememberUpdatedState(content)
- val scope = rememberCoroutineScope()
+ val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
- val modalBottomSheetWindow = remember {
- ModalBottomSheetWindow(
- properties = properties,
- onDismissRequest = onDismissRequest,
- composeView = view,
- saveId = id,
- predictiveBackProgress = predictiveBackProgress,
- scope = scope
+ val composition = rememberCompositionContext()
+ val currentContent by rememberUpdatedState(content)
+ val dialogId = rememberSaveable { UUID.randomUUID() }
+ val scope = rememberCoroutineScope()
+ val dialog = remember(view, density) {
+ ModalBottomSheetDialogWrapper(
+ onDismissRequest,
+ properties,
+ view,
+ layoutDirection,
+ density,
+ dialogId,
+ predictiveBackProgress,
+ scope,
).apply {
- setCustomContent(
- parent = parentComposition,
- content = {
- Box(
- Modifier
- .semantics { this.popup() }
- .windowInsetsPadding(windowInsets)
- .then(
- // TODO(b/290893168): Figure out a solution for APIs < 30.
- if (Build.VERSION.SDK_INT >= 33)
- Modifier.imePadding()
- else Modifier
- )
- ) {
- currentContent()
- }
+ setContent(composition) {
+ Box(
+ Modifier.semantics { dialog() },
+ ) {
+ currentContent()
}
- )
+ }
}
}
- DisposableEffect(modalBottomSheetWindow) {
- modalBottomSheetWindow.show()
- modalBottomSheetWindow.superSetLayoutDirection(layoutDirection)
+ DisposableEffect(dialog) {
+ dialog.show()
+
onDispose {
- modalBottomSheetWindow.disposeComposition()
- modalBottomSheetWindow.dismiss()
+ dialog.dismiss()
+ dialog.disposeComposition()
}
}
+
+ SideEffect {
+ dialog.updateParameters(
+ onDismissRequest = onDismissRequest,
+ properties = properties,
+ layoutDirection = layoutDirection
+ )
+ }
}
-/** Custom compose view for [ModalBottomSheet] */
-@OptIn(ExperimentalMaterial3Api::class)
-private class ModalBottomSheetWindow(
- private val properties: ModalBottomSheetProperties,
- private var onDismissRequest: () -> Unit,
- private val composeView: View,
+// Fork of androidx.compose.ui.window.DialogLayout
+// Additional parameters required for current predictive back implementation.
+@Suppress("ViewConstructor")
+private class ModalBottomSheetDialogLayout(
+ context: Context,
+ override val window: Window,
+ val shouldDismissOnBackPress: Boolean,
+ private val onDismissRequest: () -> Unit,
private val predictiveBackProgress: Animatable<Float, AnimationVector1D>,
private val scope: CoroutineScope,
- saveId: UUID
-) :
- AbstractComposeView(composeView.context),
- ViewTreeObserver.OnGlobalLayoutListener,
- ViewRootForInspector {
-
- private var backCallback: Any? = null
-
- init {
- id = android.R.id.content
- // Set up view owners
- setViewTreeLifecycleOwner(composeView.findViewTreeLifecycleOwner())
- setViewTreeViewModelStoreOwner(composeView.findViewTreeViewModelStoreOwner())
- setViewTreeSavedStateRegistryOwner(composeView.findViewTreeSavedStateRegistryOwner())
- setTag(androidx.compose.ui.R.id.compose_view_saveable_id_tag, "Popup:$saveId")
- // Enable children to draw their shadow by not clipping them
- clipChildren = false
- }
-
- private val windowManager =
- composeView.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
-
- private val displayWidth: Int
- get() = context.resources.displayMetrics.widthPixels
-
- private val params: WindowManager.LayoutParams =
- WindowManager.LayoutParams().apply {
- // Position bottom sheet from the bottom of the screen
- gravity = Gravity.BOTTOM or Gravity.START
- // Application panel window
- type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
- // Fill up the entire app view
- width = displayWidth
- height = WindowManager.LayoutParams.MATCH_PARENT
-
- // Format of screen pixels
- format = PixelFormat.TRANSLUCENT
- // Title used as fallback for a11y services
- // TODO: Provide bottom sheet window resource
- title = composeView.context.resources.getString(
- androidx.compose.ui.R.string.default_popup_window_title
- )
- // Get the Window token from the parent view
- token = composeView.applicationWindowToken
-
- // Flags specific to modal bottom sheet.
- flags = flags and (
- WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES or
- WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- ).inv()
-
- flags = flags or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
-
- // Security flag
- val secureFlagEnabled =
- properties.securePolicy.shouldApplySecureFlag(composeView.isFlagSecureEnabled())
- if (secureFlagEnabled) {
- flags = flags or WindowManager.LayoutParams.FLAG_SECURE
- } else {
- flags = flags and (WindowManager.LayoutParams.FLAG_SECURE.inv())
- }
-
- // Focusable
- if (!properties.isFocusable) {
- flags = flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- } else {
- flags = flags and (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE.inv())
- }
- }
+) : AbstractComposeView(context), DialogWindowProvider {
private var content: @Composable () -> Unit by mutableStateOf({})
+ private var backCallback: Any? = null
+
override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
private set
+ fun setContent(parent: CompositionContext, content: @Composable () -> Unit) {
+ setParentCompositionContext(parent)
+ this.content = content
+ shouldCreateCompositionOnAttachedToWindow = true
+ createComposition()
+ }
+
+ // Display width and height logic removed, size will always span fillMaxSize().
+
@Composable
override fun Content() {
content()
}
- fun setCustomContent(
- parent: CompositionContext? = null,
- content: @Composable () -> Unit
- ) {
- parent?.let { setParentCompositionContext(it) }
- this.content = content
- shouldCreateCompositionOnAttachedToWindow = true
- }
-
- fun show() {
- windowManager.addView(this, params)
- }
-
- fun dismiss() {
- setViewTreeLifecycleOwner(null)
- setViewTreeSavedStateRegistryOwner(null)
- composeView.viewTreeObserver.removeOnGlobalLayoutListener(this)
- windowManager.removeViewImmediate(this)
- }
-
- /**
- * Taken from PopupWindow. Calls [onDismissRequest] when back button is pressed.
- */
- override fun dispatchKeyEvent(event: KeyEvent): Boolean {
- if (event.keyCode == KeyEvent.KEYCODE_BACK && properties.shouldDismissOnBackPress) {
- if (keyDispatcherState == null) {
- return super.dispatchKeyEvent(event)
- }
- if (event.action == KeyEvent.ACTION_DOWN && event.repeatCount == 0) {
- val state = keyDispatcherState
- state?.startTracking(event, this)
- return true
- } else if (event.action == KeyEvent.ACTION_UP) {
- val state = keyDispatcherState
- if (state != null && state.isTracking(event) && !event.isCanceled) {
- onDismissRequest()
- return true
- }
- }
- }
- return super.dispatchKeyEvent(event)
- }
-
+ // Existing predictive back behavior below.
override fun onAttachedToWindow() {
super.onAttachedToWindow()
@@ -709,7 +733,7 @@
}
private fun maybeRegisterBackCallback() {
- if (!properties.shouldDismissOnBackPress || Build.VERSION.SDK_INT < 33) {
+ if (!shouldDismissOnBackPress || Build.VERSION.SDK_INT < 33) {
return
}
if (backCallback == null) {
@@ -729,24 +753,6 @@
backCallback = null
}
- override fun onGlobalLayout() {
- // No-op
- }
-
- override fun setLayoutDirection(layoutDirection: Int) {
- // Do nothing. ViewRootImpl will call this method attempting to set the layout direction
- // from the context's locale, but we have one already from the parent composition.
- }
-
- // Sets the "real" layout direction for our content that we obtain from the parent composition.
- fun superSetLayoutDirection(layoutDirection: LayoutDirection) {
- val direction = when (layoutDirection) {
- LayoutDirection.Ltr -> android.util.LayoutDirection.LTR
- LayoutDirection.Rtl -> android.util.LayoutDirection.RTL
- }
- super.setLayoutDirection(direction)
- }
-
@RequiresApi(34)
private object Api34Impl {
@JvmStatic
@@ -807,8 +813,165 @@
}
}
-// Taken from AndroidPopup.android.kt
-private fun View.isFlagSecureEnabled(): Boolean {
+// Fork of androidx.compose.ui.window.DialogWrapper.
+// predictiveBackProgress and scope params added for predictive back implementation.
+// EdgeToEdgeFloatingDialogWindowTheme provided to allow theme to extend into status bar.
+@ExperimentalMaterial3Api
+private class ModalBottomSheetDialogWrapper(
+ private var onDismissRequest: () -> Unit,
+ private var properties: ModalBottomSheetProperties,
+ private val composeView: View,
+ layoutDirection: LayoutDirection,
+ density: Density,
+ dialogId: UUID,
+ predictiveBackProgress: Animatable<Float, AnimationVector1D>,
+ scope: CoroutineScope,
+) : ComponentDialog(
+ ContextThemeWrapper(
+ composeView.context,
+ androidx.compose.material3.R.style.EdgeToEdgeFloatingDialogWindowTheme
+ )
+), ViewRootForInspector {
+
+ private val dialogLayout: ModalBottomSheetDialogLayout
+
+ // On systems older than Android S, there is a bug in the surface insets matrix math used by
+ // elevation, so high values of maxSupportedElevation break accessibility services: b/232788477.
+ private val maxSupportedElevation = 8.dp
+
+ override val subCompositionView: AbstractComposeView get() = dialogLayout
+
+ init {
+ val window = window ?: error("Dialog has no window")
+ window.requestFeature(Window.FEATURE_NO_TITLE)
+ window.setBackgroundDrawableResource(android.R.color.transparent)
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ dialogLayout = ModalBottomSheetDialogLayout(
+ context,
+ window,
+ properties.shouldDismissOnBackPress,
+ onDismissRequest,
+ predictiveBackProgress,
+ scope,
+ ).apply {
+ // Set unique id for AbstractComposeView. This allows state restoration for the state
+ // defined inside the Dialog via rememberSaveable()
+ setTag(R.id.compose_view_saveable_id_tag, "Dialog:$dialogId")
+ // Enable children to draw their shadow by not clipping them
+ clipChildren = false
+ // Allocate space for elevation
+ with(density) { elevation = maxSupportedElevation.toPx() }
+ // Simple outline to force window manager to allocate space for shadow.
+ // Note that the outline affects clickable area for the dismiss listener. In case of
+ // shapes like circle the area for dismiss might be to small (rectangular outline
+ // consuming clicks outside of the circle).
+ outlineProvider = object : ViewOutlineProvider() {
+ override fun getOutline(view: View, result: Outline) {
+ result.setRect(0, 0, view.width, view.height)
+ // We set alpha to 0 to hide the view's shadow and let the composable to draw
+ // its own shadow. This still enables us to get the extra space needed in the
+ // surface.
+ result.alpha = 0f
+ }
+ }
+ }
+ // Clipping logic removed because we are spanning edge to edge.
+
+ setContentView(dialogLayout)
+ dialogLayout.setViewTreeLifecycleOwner(composeView.findViewTreeLifecycleOwner())
+ dialogLayout.setViewTreeViewModelStoreOwner(composeView.findViewTreeViewModelStoreOwner())
+ dialogLayout.setViewTreeSavedStateRegistryOwner(
+ composeView.findViewTreeSavedStateRegistryOwner()
+ )
+
+ // Initial setup
+ updateParameters(onDismissRequest, properties, layoutDirection)
+
+ WindowCompat.getInsetsController(window, window.decorView).apply {
+ isAppearanceLightStatusBars = false
+ isAppearanceLightNavigationBars = false
+ }
+ // Due to how the onDismissRequest callback works
+ // (it enforces a just-in-time decision on whether to update the state to hide the dialog)
+ // we need to unconditionally add a callback here that is always enabled,
+ // meaning we'll never get a system UI controlled predictive back animation
+ // for these dialogs
+ onBackPressedDispatcher.addCallback(this) {
+ if (properties.shouldDismissOnBackPress) {
+ onDismissRequest()
+ }
+ }
+ }
+
+ private fun setLayoutDirection(layoutDirection: LayoutDirection) {
+ dialogLayout.layoutDirection = when (layoutDirection) {
+ LayoutDirection.Ltr -> android.util.LayoutDirection.LTR
+ LayoutDirection.Rtl -> android.util.LayoutDirection.RTL
+ }
+ }
+
+ fun setContent(parentComposition: CompositionContext, children: @Composable () -> Unit) {
+ dialogLayout.setContent(parentComposition, children)
+ }
+
+ private fun setSecurePolicy(securePolicy: SecureFlagPolicy) {
+ val secureFlagEnabled =
+ securePolicy.shouldApplySecureFlag(composeView.isFlagSecureEnabled())
+ window!!.setFlags(
+ if (secureFlagEnabled) {
+ WindowManager.LayoutParams.FLAG_SECURE
+ } else {
+ WindowManager.LayoutParams.FLAG_SECURE.inv()
+ },
+ WindowManager.LayoutParams.FLAG_SECURE
+ )
+ }
+
+ fun updateParameters(
+ onDismissRequest: () -> Unit,
+ properties: ModalBottomSheetProperties,
+ layoutDirection: LayoutDirection
+ ) {
+ this.onDismissRequest = onDismissRequest
+ this.properties = properties
+ setSecurePolicy(properties.securePolicy)
+ setLayoutDirection(layoutDirection)
+
+ // Window flags to span parent window.
+ window?.setLayout(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ )
+ window?.setSoftInputMode(
+ if (Build.VERSION.SDK_INT >= 30) {
+ WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
+ } else {
+ @Suppress("DEPRECATION")
+ WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+ },
+ )
+ }
+
+ fun disposeComposition() {
+ dialogLayout.disposeComposition()
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ val result = super.onTouchEvent(event)
+ if (result) {
+ onDismissRequest()
+ }
+
+ return result
+ }
+
+ override fun cancel() {
+ // Prevents the dialog from dismissing itself
+ return
+ }
+}
+
+internal fun View.isFlagSecureEnabled(): Boolean {
val windowParams = rootView.layoutParams as? WindowManager.LayoutParams
if (windowParams != null) {
return (windowParams.flags and WindowManager.LayoutParams.FLAG_SECURE) != 0
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt
index 5250586..8b569bd 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt
@@ -34,8 +34,8 @@
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.FlingBehavior
-import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
+import androidx.compose.foundation.gestures.snapping.snapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -712,12 +712,13 @@
return remember(decayAnimationSpec, lazyListState) {
val original = SnapLayoutInfoProvider(lazyListState)
val snapLayoutInfoProvider = object : SnapLayoutInfoProvider by original {
- override fun calculateApproachOffset(initialVelocity: Float): Float {
- return 0.0f
- }
+ override fun calculateApproachOffset(
+ velocity: Float,
+ decayOffset: Float
+ ): Float = 0.0f
}
- SnapFlingBehavior(
+ snapFlingBehavior(
snapLayoutInfoProvider = snapLayoutInfoProvider,
decayAnimationSpec = decayAnimationSpec,
snapAnimationSpec = spring(stiffness = Spring.StiffnessMediumLow)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Ripple.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Ripple.kt
index 322b33f..485b51c 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Ripple.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Ripple.kt
@@ -165,7 +165,8 @@
/**
* CompositionLocal used for providing [RippleConfiguration] down the tree. This acts as a
* tree-local 'override' for ripples used inside components that you cannot directly control, such
- * as to change the color of a specific component's ripple, or disable it entirely.
+ * as to change the color of a specific component's ripple, or disable it entirely by providing
+ * `null`.
*
* In most cases you should rely on the default theme behavior for consistency with other components
* - this exists as an escape hatch for individual components and is not intended to be used for
@@ -176,15 +177,15 @@
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@get:ExperimentalMaterial3Api
@ExperimentalMaterial3Api
-val LocalRippleConfiguration: ProvidableCompositionLocal<RippleConfiguration> =
+val LocalRippleConfiguration: ProvidableCompositionLocal<RippleConfiguration?> =
compositionLocalOf { RippleConfiguration() }
/**
* Configuration for [ripple] appearance, provided using [LocalRippleConfiguration]. In most cases
* the default values should be used, for custom design system use cases you should instead
- * build your own custom ripple using [createRippleModifierNode].
+ * build your own custom ripple using [createRippleModifierNode]. To disable the ripple, provide
+ * `null` using [LocalRippleConfiguration].
*
- * @param isEnabled whether the ripple is enabled. If false, no ripple will be rendered
* @param color the color override for the ripple. If [Color.Unspecified], then the default color
* from the theme will be used instead. Note that if the ripple has a color explicitly set with
* the parameter on [ripple], that will always be used instead of this value.
@@ -194,7 +195,6 @@
@Immutable
@ExperimentalMaterial3Api
class RippleConfiguration(
- val isEnabled: Boolean = true,
val color: Color = Color.Unspecified,
val rippleAlpha: RippleAlpha? = null
) {
@@ -202,7 +202,6 @@
if (this === other) return true
if (other !is RippleConfiguration) return false
- if (isEnabled != other.isEnabled) return false
if (color != other.color) return false
if (rippleAlpha != other.rippleAlpha) return false
@@ -210,14 +209,13 @@
}
override fun hashCode(): Int {
- var result = isEnabled.hashCode()
- result = 31 * result + color.hashCode()
+ var result = color.hashCode()
result = 31 * result + (rippleAlpha?.hashCode() ?: 0)
return result
}
override fun toString(): String {
- return "RippleConfiguration(enabled=$isEnabled, color=$color, rippleAlpha=$rippleAlpha)"
+ return "RippleConfiguration(color=$color, rippleAlpha=$rippleAlpha)"
}
}
@@ -311,13 +309,14 @@
}
/**
- * Handles changes to [RippleConfiguration.isEnabled]. Changes to [RippleConfiguration.color] and
- * [RippleConfiguration.rippleAlpha] are handled as part of the ripple definition.
+ * Handles [LocalRippleConfiguration] changing between null / non-null. Changes to
+ * [RippleConfiguration.color] and [RippleConfiguration.rippleAlpha] are handled as part of
+ * the ripple definition.
*/
private fun updateConfiguration() {
observeReads {
val configuration = currentValueOf(LocalRippleConfiguration)
- if (!configuration.isEnabled) {
+ if (configuration == null) {
removeRipple()
} else {
if (rippleNode == null) attachNewRipple()
@@ -331,8 +330,10 @@
if (userDefinedColor.isSpecified) {
userDefinedColor
} else {
+ // If this is null, the ripple will be removed, so this should always be non-null in
+ // normal use
val rippleConfiguration = currentValueOf(LocalRippleConfiguration)
- if (rippleConfiguration.color.isSpecified) {
+ if (rippleConfiguration?.color?.isSpecified == true) {
rippleConfiguration.color
} else {
currentValueOf(LocalContentColor)
@@ -341,8 +342,10 @@
}
val calculateRippleAlpha = {
+ // If this is null, the ripple will be removed, so this should always be non-null in
+ // normal use
val rippleConfiguration = currentValueOf(LocalRippleConfiguration)
- rippleConfiguration.rippleAlpha ?: RippleDefaults.RippleAlpha
+ rippleConfiguration?.rippleAlpha ?: RippleDefaults.RippleAlpha
}
rippleNode = delegate(createRippleModifierNode(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
index a8977c5..a04b579 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
@@ -25,6 +25,7 @@
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.material3.SheetValue.Expanded
import androidx.compose.material3.SheetValue.Hidden
@@ -34,7 +35,6 @@
import androidx.compose.material3.internal.animateTo
import androidx.compose.material3.internal.getString
import androidx.compose.material3.internal.snapTo
-import androidx.compose.material3.internal.systemBarsForVisualComponents
import androidx.compose.material3.tokens.ScrimTokens
import androidx.compose.material3.tokens.SheetBottomTokens
import androidx.compose.runtime.Composable
@@ -332,11 +332,11 @@
val SheetMaxWidth = 640.dp
/**
- * Default insets to be used and consumed by the [ModalBottomSheet] window.
+ * Default insets to be used and consumed by the [ModalBottomSheet]'s content.
*/
val windowInsets: WindowInsets
@Composable
- get() = WindowInsets.systemBarsForVisualComponents.only(WindowInsetsSides.Vertical)
+ get() = WindowInsets.safeDrawing.only(WindowInsetsSides.Bottom)
/**
* The optional visual marker placed on top of a bottom sheet to indicate it may be dragged.
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
index 0683f94..c06a03e 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
@@ -140,9 +140,9 @@
* services.
* @param valueRange range of values that this slider can take. The passed [value] will be coerced
* to this range.
- * @param steps if greater than 0, specifies the amount of discrete allowable values, evenly
- * distributed across the whole value range. If 0, the slider will behave continuously and allow any
- * value from the range specified. Must not be negative.
+ * @param steps if positive, specifies the amount of discrete allowable values (in addition to the
+ * endpoints of the value range). Step values are evenly distributed across the range. If 0, the
+ * slider will behave continuously and allow any value from the range. Must not be negative.
* @param onValueChangeFinished called when value change has ended. This should not be used to
* update the slider value (use [onValueChange] instead), but rather to know when the user has
* completed selecting a new value by ending a drag or a click.
@@ -236,9 +236,9 @@
* @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
* for this slider. You can create and pass in your own `remember`ed instance to observe
* [Interaction]s and customize the appearance / behavior of this slider in different states.
- * @param steps if greater than 0, specifies the amount of discrete allowable values, evenly
- * distributed across the whole value range. If 0, the slider will behave continuously and allow any
- * value from the range specified. Must not be negative.
+ * @param steps if positive, specifies the amount of discrete allowable values (in addition to the
+ * endpoints of the value range). Step values are evenly distributed across the range. If 0, the
+ * slider will behave continuously and allow any value from the range. Must not be negative.
* @param thumb the thumb to be displayed on the slider, it is placed on top of the track. The
* lambda receives a [SliderState] which is used to obtain the current active track.
* @param track the track to be displayed on the slider, it is placed underneath the thumb. The
@@ -402,9 +402,9 @@
* @param enabled whether or not component is enabled and can we interacted with or not
* @param valueRange range of values that Range Slider values can take. Passed [value] will be
* coerced to this range
- * @param steps if greater than 0, specifies the amounts of discrete values, evenly distributed
- * between across the whole value range. If 0, range slider will behave as a continuous slider and
- * allow to choose any value from the range specified. Must not be negative.
+ * @param steps if positive, specifies the amount of discrete allowable values (in addition to the
+ * endpoints of the value range). Step values are evenly distributed across the range. If 0, the
+ * range slider will behave continuously and allow any value from the range. Must not be negative.
* @param onValueChangeFinished lambda to be invoked when value change has ended. This callback
* shouldn't be used to update the range slider values (use [onValueChange] for that), but rather to
* know when the user has completed selecting a new value by ending a drag or a click.
@@ -503,9 +503,9 @@
* @param endInteractionSource the [MutableInteractionSource] representing the stream of
* [Interaction]s for the end thumb. You can create and pass in your own
* `remember`ed instance to observe.
- * @param steps if greater than 0, specifies the amounts of discrete values, evenly distributed
- * between across the whole value range. If 0, range slider will behave as a continuous slider and
- * allow to choose any value from the range specified. Must not be negative.
+ * @param steps if positive, specifies the amount of discrete allowable values (in addition to the
+ * endpoints of the value range). Step values are evenly distributed across the range. If 0, the
+ * range slider will behave continuously and allow any value from the range. Must not be negative.
* @param startThumb the start thumb to be displayed on the Range Slider. The lambda receives a
* [RangeSliderState] which is used to obtain the current active track.
* @param endThumb the end thumb to be displayed on the Range Slider. The lambda receives a
@@ -2031,9 +2031,9 @@
* @param value [Float] that indicates the initial
* position of the thumb. If outside of [valueRange]
* provided, value will be coerced to this range.
- * @param steps if greater than 0, specifies the amounts of discrete values, evenly distributed
- * between across the whole value range. If 0, range slider will behave as a continuous slider and
- * allow to choose any value from the range specified. Must not be negative.
+ * @param steps if positive, specifies the amount of discrete allowable values (in addition to the
+ * endpoints of the value range). Step values are evenly distributed across the range. If 0, the
+ * slider will behave continuously and allow any value from the range. Must not be negative.
* @param onValueChangeFinished lambda to be invoked when value change has ended. This callback
* shouldn't be used to update the range slider values (use [onValueChange] for that),
* but rather to know when the user has completed selecting a new value by ending a drag or a click.
@@ -2158,9 +2158,9 @@
* @param activeRangeEnd [Float] that indicates the initial
* end of the active range of the slider. If outside of [valueRange]
* provided, value will be coerced to this range.
- * @param steps if greater than 0, specifies the amounts of discrete values, evenly distributed
- * between across the whole value range. If 0, range slider will behave as a continuous slider and
- * allow to choose any value from the range specified. Must not be negative.
+ * @param steps if positive, specifies the amount of discrete allowable values (in addition to the
+ * endpoints of the value range). Step values are evenly distributed across the range. If 0, the
+ * range slider will behave continuously and allow any value from the range. Must not be negative.
* @param onValueChangeFinished lambda to be invoked when value change has ended. This callback
* shouldn't be used to update the range slider values (use [onValueChange] for that), but rather
* to know when the user has completed selecting a new value by ending a drag or a click.
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt
index 7e17cb0..5681ab7 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt
@@ -17,6 +17,8 @@
package androidx.compose.material3
import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.SnapSpec
import androidx.compose.animation.core.TweenSpec
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -24,38 +26,34 @@
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.requiredSize
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material3.tokens.SwitchTokens
+import androidx.compose.material3.tokens.SwitchTokens.TrackOutlineWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.takeOrElse
-import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.invalidateMeasurement
+import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.semantics.Role
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
-import kotlin.math.roundToInt
import kotlinx.coroutines.launch
/**
@@ -98,43 +96,14 @@
colors: SwitchColors = SwitchDefaults.colors(),
interactionSource: MutableInteractionSource? = null,
) {
- @Suppress("NAME_SHADOWING")
- val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
- val uncheckedThumbDiameter = if (thumbContent == null) {
- UncheckedThumbDiameter
- } else {
- ThumbDiameter
- }
-
- val thumbPaddingStart = (SwitchHeight - uncheckedThumbDiameter) / 2
- val minBound = with(LocalDensity.current) { thumbPaddingStart.toPx() }
- val maxBound = with(LocalDensity.current) { ThumbPathLength.toPx() }
- val valueToOffset = remember<(Boolean) -> Float>(minBound, maxBound) {
- { value -> if (value) maxBound else minBound }
- }
-
- val targetValue = valueToOffset(checked)
- val offset = remember { Animatable(targetValue) }
- val scope = rememberCoroutineScope()
-
- SideEffect {
- // min bound might have changed if the icon is only rendered in checked state.
- offset.updateBounds(lowerBound = minBound)
- }
-
- DisposableEffect(checked) {
- if (offset.targetValue != targetValue) {
- scope.launch {
- offset.animateTo(targetValue, AnimationSpec)
- }
- }
- onDispose { }
- }
+ @Suppress("NAME_SHADOWING") val interactionSource =
+ interactionSource ?: remember { MutableInteractionSource() }
// TODO: Add Swipeable modifier b/223797571
- val toggleableModifier =
- if (onCheckedChange != null) {
- Modifier.toggleable(
+ val toggleableModifier = if (onCheckedChange != null) {
+ Modifier
+ .minimumInteractiveComponentSize()
+ .toggleable(
value = checked,
onValueChange = onCheckedChange,
enabled = enabled,
@@ -142,101 +111,59 @@
interactionSource = interactionSource,
indication = null
)
- } else {
- Modifier
- }
+ } else {
+ Modifier
+ }
- Box(
- modifier
- .then(
- if (onCheckedChange != null) {
- Modifier.minimumInteractiveComponentSize()
- } else {
- Modifier
- }
- )
+ SwitchImpl(
+ modifier = modifier
.then(toggleableModifier)
.wrapContentSize(Alignment.Center)
- .requiredSize(SwitchWidth, SwitchHeight)
- ) {
- SwitchImpl(
- checked = checked,
- enabled = enabled,
- colors = colors,
- thumbValue = offset.asState(),
- interactionSource = interactionSource,
- thumbShape = SwitchTokens.HandleShape.value,
- uncheckedThumbDiameter = uncheckedThumbDiameter,
- minBound = thumbPaddingStart,
- maxBound = ThumbPathLength,
- thumbContent = thumbContent,
- )
- }
+ .requiredSize(SwitchWidth, SwitchHeight),
+ checked = checked,
+ enabled = enabled,
+ colors = colors,
+ interactionSource = interactionSource,
+ thumbShape = SwitchTokens.HandleShape.value,
+ thumbContent = thumbContent,
+ )
}
@Composable
@Suppress("ComposableLambdaParameterNaming", "ComposableLambdaParameterPosition")
-private fun BoxScope.SwitchImpl(
+private fun SwitchImpl(
+ modifier: Modifier,
checked: Boolean,
enabled: Boolean,
colors: SwitchColors,
- thumbValue: State<Float>,
thumbContent: (@Composable () -> Unit)?,
interactionSource: InteractionSource,
thumbShape: Shape,
- uncheckedThumbDiameter: Dp,
- minBound: Dp,
- maxBound: Dp,
) {
val trackColor = colors.trackColor(enabled, checked)
- val isPressed by interactionSource.collectIsPressedAsState()
-
- val thumbValueDp = with(LocalDensity.current) { thumbValue.value.toDp() }
- val thumbSizeDp = if (isPressed) {
- SwitchTokens.PressedHandleWidth
- } else {
- uncheckedThumbDiameter + (ThumbDiameter - uncheckedThumbDiameter) *
- ((thumbValueDp - minBound) / (maxBound - minBound))
- }
-
- val thumbOffset = if (isPressed) {
- with(LocalDensity.current) {
- if (checked) {
- ThumbPathLength - SwitchTokens.TrackOutlineWidth
- } else {
- SwitchTokens.TrackOutlineWidth
- }.toPx()
- }
- } else {
- thumbValue.value
- }
-
+ val resolvedThumbColor = colors.thumbColor(enabled, checked)
val trackShape = SwitchTokens.TrackShape.value
- val modifier = Modifier
- .align(Alignment.Center)
- .width(SwitchWidth)
- .height(SwitchHeight)
- .border(
- SwitchTokens.TrackOutlineWidth,
- colors.borderColor(enabled, checked),
- trackShape
- )
- .background(trackColor, trackShape)
- Box(modifier) {
- val resolvedThumbColor = colors.thumbColor(enabled, checked)
+ Box(
+ modifier
+ .border(
+ TrackOutlineWidth,
+ colors.borderColor(enabled, checked),
+ trackShape
+ )
+ .background(trackColor, trackShape)
+ ) {
Box(
modifier = Modifier
.align(Alignment.CenterStart)
- .offset { IntOffset(thumbOffset.roundToInt(), 0) }
+ .then(ThumbElement(interactionSource, checked))
.indication(
interactionSource = interactionSource,
indication = rippleOrFallbackImplementation(
bounded = false,
- SwitchTokens.StateLayerSize / 2
+ radius = SwitchTokens.StateLayerSize / 2
)
)
- .requiredSize(thumbSizeDp)
.background(resolvedThumbColor, thumbShape),
contentAlignment = Alignment.Center
) {
@@ -251,14 +178,124 @@
}
}
-internal val ThumbDiameter = SwitchTokens.SelectedHandleWidth
-internal val UncheckedThumbDiameter = SwitchTokens.UnselectedHandleWidth
-private val SwitchWidth = SwitchTokens.TrackWidth
-private val SwitchHeight = SwitchTokens.TrackHeight
-private val ThumbPadding = (SwitchHeight - ThumbDiameter) / 2
-private val ThumbPathLength = (SwitchWidth - ThumbDiameter) - ThumbPadding
+private data class ThumbElement(
+ val interactionSource: InteractionSource,
+ val checked: Boolean,
+) : ModifierNodeElement<ThumbNode>() {
+ override fun create() = ThumbNode(interactionSource, checked)
-private val AnimationSpec = TweenSpec<Float>(durationMillis = 100)
+ override fun update(node: ThumbNode) {
+ node.interactionSource = interactionSource
+ if (node.checked != checked) {
+ node.invalidateMeasurement()
+ }
+ node.checked = checked
+ node.update()
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ name = "switchThumb"
+ properties["interactionSource"] = interactionSource
+ properties["checked"] = checked
+ }
+}
+
+private class ThumbNode(
+ var interactionSource: InteractionSource,
+ var checked: Boolean,
+) : Modifier.Node(), LayoutModifierNode {
+
+ override val shouldAutoInvalidate: Boolean
+ get() = false
+
+ private var isPressed = false
+ private var offsetAnim: Animatable<Float, AnimationVector1D>? = null
+ private var sizeAnim: Animatable<Float, AnimationVector1D>? = null
+ private var initialOffset: Float = Float.NaN
+ private var initialSize: Float = Float.NaN
+
+ override fun onAttach() {
+ coroutineScope.launch {
+ var pressCount = 0
+ interactionSource.interactions.collect { interaction ->
+ when (interaction) {
+ is PressInteraction.Press -> pressCount++
+ is PressInteraction.Release -> pressCount--
+ is PressInteraction.Cancel -> pressCount--
+ }
+ val pressed = pressCount > 0
+ if (isPressed != pressed) {
+ isPressed = pressed
+ invalidateMeasurement()
+ }
+ }
+ }
+ }
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ val hasContent = measurable.maxIntrinsicHeight(constraints.maxWidth) != 0 &&
+ measurable.maxIntrinsicWidth(constraints.maxHeight) != 0
+ val size = when {
+ isPressed -> SwitchTokens.PressedHandleWidth
+ hasContent || checked -> ThumbDiameter
+ else -> UncheckedThumbDiameter
+ }.toPx()
+
+ val actualSize = (sizeAnim?.value ?: size).toInt()
+ val placeable = measurable.measure(
+ Constraints.fixed(actualSize, actualSize)
+ )
+ val thumbPaddingStart = (SwitchHeight - size.toDp()) / 2f
+ val minBound = thumbPaddingStart.toPx()
+ val thumbPathLength = (SwitchWidth - ThumbDiameter) - ThumbPadding
+ val maxBound = thumbPathLength.toPx()
+ val offset = when {
+ isPressed && checked -> maxBound - TrackOutlineWidth.toPx()
+ isPressed && !checked -> TrackOutlineWidth.toPx()
+ checked -> maxBound
+ else -> minBound
+ }
+
+ if (sizeAnim?.targetValue != size) {
+ coroutineScope.launch {
+ sizeAnim?.animateTo(
+ size,
+ if (isPressed) SnapSpec else AnimationSpec
+ )
+ }
+ }
+
+ if (offsetAnim?.targetValue != offset) {
+ coroutineScope.launch {
+ offsetAnim?.animateTo(
+ offset,
+ if (isPressed) SnapSpec else AnimationSpec
+ )
+ }
+ }
+
+ if (initialSize.isNaN() && initialOffset.isNaN()) {
+ initialSize = size
+ initialOffset = offset
+ }
+
+ return layout(actualSize, actualSize) {
+ placeable.placeRelative(offsetAnim?.value?.toInt() ?: offset.toInt(), 0)
+ }
+ }
+
+ fun update() {
+ if (sizeAnim == null && !initialSize.isNaN()) {
+ sizeAnim = Animatable(initialSize)
+ }
+
+ if (offsetAnim == null && !initialOffset.isNaN())
+ offsetAnim = Animatable(initialOffset)
+ }
+}
/**
* Contains the default values used by [Switch]
@@ -572,3 +609,13 @@
return result
}
}
+
+/* @VisibleForTesting */
+internal val ThumbDiameter = SwitchTokens.SelectedHandleWidth
+internal val UncheckedThumbDiameter = SwitchTokens.UnselectedHandleWidth
+
+private val SwitchWidth = SwitchTokens.TrackWidth
+private val SwitchHeight = SwitchTokens.TrackHeight
+private val ThumbPadding = (SwitchHeight - ThumbDiameter) / 2
+private val SnapSpec = SnapSpec<Float>()
+private val AnimationSpec = TweenSpec<Float>(durationMillis = 100)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt
index 8984250..dfbc26f 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt
@@ -20,7 +20,6 @@
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.DecayAnimationSpec
import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.calculateTargetValue
import androidx.compose.animation.core.spring
import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.gestures.Orientation
@@ -657,14 +656,9 @@
*/
@Composable
fun noSnapFlingBehavior(): TargetedFlingBehavior {
- val splineDecay = rememberSplineBasedDecay<Float>()
val decayLayoutInfoProvider = remember {
object : SnapLayoutInfoProvider {
- override fun calculateApproachOffset(initialVelocity: Float): Float {
- return splineDecay.calculateTargetValue(0f, initialVelocity)
- }
-
- override fun calculateSnappingOffset(currentVelocity: Float): Float = 0f
+ override fun calculateSnapOffset(velocity: Float): Float = 0f
}
}
diff --git a/compose/material3/material3/src/main/res/values-v30/styles.xml b/compose/material3/material3/src/main/res/values-v30/styles.xml
new file mode 100644
index 0000000..3d9425e
--- /dev/null
+++ b/compose/material3/material3/src/main/res/values-v30/styles.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2024 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- "always" Value for windowLayoutInDisplayCutoutMode. Needed for APIs 30+. -->
+ <integer name="m3c_window_layout_in_display_cutout_mode">3</integer>
+</resources>
\ No newline at end of file
diff --git a/compose/material3/material3/src/main/res/values/styles.xml b/compose/material3/material3/src/main/res/values/styles.xml
new file mode 100644
index 0000000..8249ac4
--- /dev/null
+++ b/compose/material3/material3/src/main/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2024 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <style name="EdgeToEdgeFloatingDialogWindowTheme">
+ <item name="android:dialogTheme">@style/EdgeToEdgeFloatingDialogTheme</item>
+ </style>
+ <style name="EdgeToEdgeFloatingDialogTheme" parent="android:Theme.DeviceDefault.Dialog">
+ <item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="27">@integer/m3c_window_layout_in_display_cutout_mode</item>
+ <item name="android:windowClipToOutline">false</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowElevation">0dp</item>
+ </style>
+ <!-- "shortEdges" Value for windowLayoutInDisplayCutoutMode. Needed for APIs 27-29. -->
+ <integer name="m3c_window_layout_in_display_cutout_mode">1</integer>
+</resources>
\ No newline at end of file
diff --git a/compose/runtime/runtime-livedata/build.gradle b/compose/runtime/runtime-livedata/build.gradle
index 0a09b7d..fdcb555 100644
--- a/compose/runtime/runtime-livedata/build.gradle
+++ b/compose/runtime/runtime-livedata/build.gradle
@@ -49,7 +49,7 @@
androidx {
name = "Compose LiveData integration"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose integration with LiveData"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/runtime/runtime-rxjava2/build.gradle b/compose/runtime/runtime-rxjava2/build.gradle
index 2f58ae2..682c0a4 100644
--- a/compose/runtime/runtime-rxjava2/build.gradle
+++ b/compose/runtime/runtime-rxjava2/build.gradle
@@ -47,7 +47,7 @@
androidx {
name = "Compose RxJava 2 integration"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose integration with RxJava 2"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/runtime/runtime-rxjava3/build.gradle b/compose/runtime/runtime-rxjava3/build.gradle
index f90ceba..490e4e0 100644
--- a/compose/runtime/runtime-rxjava3/build.gradle
+++ b/compose/runtime/runtime-rxjava3/build.gradle
@@ -47,7 +47,7 @@
androidx {
name = "Compose RxJava 3 integration"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose integration with RxJava 3"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/runtime/runtime-saveable/build.gradle b/compose/runtime/runtime-saveable/build.gradle
index e73d562..5612159 100644
--- a/compose/runtime/runtime-saveable/build.gradle
+++ b/compose/runtime/runtime-saveable/build.gradle
@@ -116,7 +116,7 @@
androidx {
name = "Compose Saveable"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose components that allow saving and restoring the local ui state"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/runtime/runtime-test-utils/build.gradle b/compose/runtime/runtime-test-utils/build.gradle
index 1565411..7aa6600 100644
--- a/compose/runtime/runtime-test-utils/build.gradle
+++ b/compose/runtime/runtime-test-utils/build.gradle
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import androidx.build.LibraryType
+import androidx.build.PlatformIdentifier
import androidx.build.Publish
plugins {
@@ -26,6 +27,8 @@
android()
desktop()
+ defaultPlatform(PlatformIdentifier.ANDROID)
+
sourceSets {
commonMain {
dependencies {
@@ -63,7 +66,7 @@
androidx {
name = "Compose Internal Test Utils"
type = LibraryType.INTERNAL_TEST_LIBRARY
- publish = Publish.NONE
+ publish = Publish.SNAPSHOT_ONLY
inceptionYear = "2024"
description = "Compose runtime test utils shared between runtime and compiler tests."
}
diff --git a/compose/runtime/runtime-test-utils/lint-baseline.xml b/compose/runtime/runtime-test-utils/lint-baseline.xml
new file mode 100644
index 0000000..f55f039
--- /dev/null
+++ b/compose/runtime/runtime-test-utils/lint-baseline.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" val filtered get() = contacts.filter { it.name.contains(filter) }"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/ContactModel.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" val toRun = awaiters.toList()"
+ errorLine2=" ~~~~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" toRun.map { it.runFrame(frameTime) }.forEach { it() }"
+ errorLine2=" ~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" toRun.map { it.runFrame(frameTime) }.forEach { it() }"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" children.forEach { it.render(indent + 2, builder) }"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" val copyOfItems = itemsToMove.map { it }"
+ errorLine2=" ~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" else attributes.map { " ${it.key}='${it.value}'" }.joinToString()"
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" children.map { it.toString() }.joinToString(" ")"
+ errorLine2=" ~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" children.map { it.toString() }.joinToString(" ")"
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1=" for (child in children) {"
+ errorLine2=" ~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
+ </issue>
+
+ <issue
+ id="ListIterator"
+ message="Creating an unnecessary Iterator to iterate through a List"
+ errorLine1="fun View.flatten(): List<View> = listOf(this) + children.flatMap { it.flatten() }"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
+ </issue>
+
+</issues>
diff --git a/compose/runtime/runtime-tracing/build.gradle b/compose/runtime/runtime-tracing/build.gradle
index 4f60cda..a6b744f 100644
--- a/compose/runtime/runtime-tracing/build.gradle
+++ b/compose/runtime/runtime-tracing/build.gradle
@@ -21,9 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import androidx.build.RunApiTasks
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -51,7 +49,7 @@
androidx {
name = "Compose Runtime: Tracing"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2022"
description = "Additional tracing in Compose"
metalavaK2UastEnabled = true
diff --git a/compose/runtime/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index 063fec6..60a8ba3 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -132,7 +132,7 @@
androidx {
name = "Compose Runtime"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Tree composition support for code generated by the Compose compiler plugin and corresponding public API"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SlotTableIntegrationBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SlotTableIntegrationBenchmark.kt
new file mode 100644
index 0000000..9b61a35
--- /dev/null
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SlotTableIntegrationBenchmark.kt
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime.benchmark
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import kotlin.random.Random
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class, ExperimentalTestApi::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SlotTableIntegrationBenchmark : ComposeBenchmarkBase() {
+
+ @UiThreadTest
+ @Test
+ fun create() = runBlockingTestWithFrameClock {
+ measureCompose {
+ Column(
+ modifier = Modifier.size(width = 20.dp, height = 300.dp)
+ ) {
+ repeat(100) {
+ key(it) {
+ Pixel(color = Color.Blue)
+ }
+ }
+ }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun removeManyGroups() = runBlockingTestWithFrameClock {
+ var includeGroups by mutableStateOf(true)
+ measureRecomposeSuspending {
+ compose {
+ Column(
+ modifier = Modifier.size(width = 20.dp, height = 300.dp)
+ ) {
+ if (includeGroups) {
+ repeat(100) {
+ key(it) {
+ Pixel(color = Color.Blue)
+ }
+ }
+ }
+ }
+ }
+ update { includeGroups = false }
+ reset { includeGroups = true }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun removeAlternatingGroups() = runBlockingTestWithFrameClock {
+ var insertAlternatingGroups by mutableStateOf(true)
+ measureRecomposeSuspending {
+ compose {
+ Column(
+ modifier = Modifier.size(width = 20.dp, height = 300.dp)
+ ) {
+ repeat(100) { index ->
+ if (index % 2 == 0 || insertAlternatingGroups) {
+ key(index) {
+ Pixel(color = Color.Blue)
+ }
+ }
+ }
+ }
+ }
+ update { insertAlternatingGroups = false }
+ reset { insertAlternatingGroups = true }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun removeManyReplaceGroups() = runBlockingTestWithFrameClock {
+ var insertAlternatingGroups by mutableStateOf(true)
+ measureRecomposeSuspending {
+ compose {
+ Column(
+ modifier = Modifier.size(width = 20.dp, height = 300.dp)
+ ) {
+ repeat(100) { index ->
+ if (index % 2 == 0 || insertAlternatingGroups) {
+ Pixel(color = Color(
+ red = 0,
+ green = 2 * index,
+ blue = 0
+ ))
+ }
+ }
+ }
+ }
+ update { insertAlternatingGroups = false }
+ reset { insertAlternatingGroups = true }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun insertManyGroups() = runBlockingTestWithFrameClock {
+ var includeGroups by mutableStateOf(false)
+ measureRecomposeSuspending {
+ compose {
+ Column(
+ modifier = Modifier.size(width = 20.dp, height = 300.dp)
+ ) {
+ if (includeGroups) {
+ repeat(100) {
+ key(it) {
+ Pixel(color = Color.Blue)
+ }
+ }
+ }
+ }
+ }
+ update { includeGroups = true }
+ reset { includeGroups = false }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun insertAlternatingGroups() = runBlockingTestWithFrameClock {
+ var insertAlternatingGroups by mutableStateOf(false)
+ measureRecomposeSuspending {
+ compose {
+ Column(
+ modifier = Modifier.size(width = 20.dp, height = 300.dp)
+ ) {
+ repeat(100) { index ->
+ if (index % 2 == 0 || insertAlternatingGroups) {
+ key(index) {
+ Pixel(color = Color.Blue)
+ }
+ }
+ }
+ }
+ }
+ update { insertAlternatingGroups = true }
+ reset { insertAlternatingGroups = false }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun insertManyReplaceGroups() = runBlockingTestWithFrameClock {
+ var insertAlternatingGroups by mutableStateOf(false)
+ measureRecomposeSuspending {
+ compose {
+ Column(
+ modifier = Modifier.size(width = 20.dp, height = 300.dp)
+ ) {
+ repeat(100) { index ->
+ if (index % 2 == 0 || insertAlternatingGroups) {
+ Pixel(color = Color(
+ red = 0,
+ green = 2 * index,
+ blue = 0
+ ))
+ }
+ }
+ }
+ }
+ update { insertAlternatingGroups = true }
+ reset { insertAlternatingGroups = false }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun updateManyNestedGroups() = runBlockingTestWithFrameClock {
+ var seed by mutableIntStateOf(1337)
+ measureRecomposeSuspending {
+ compose {
+ val random = remember(seed) { Random(seed) }
+ MatryoshkaLayout(
+ depth = 100,
+ content = {
+ MinimalBox {
+ Pixel(color = Color(random.nextInt()))
+ Pixel(color = Color.Red)
+ Pixel(color = Color.Green)
+ Pixel(color = Color.Blue)
+ }
+ MinimalBox {
+ NonRenderingText("abcdef")
+ }
+ NonRenderingText(
+ text = random.nextString(),
+ textColor = Color(random.nextInt()),
+ textSize = random.nextInt(6, 32).dp,
+ ellipsize = random.nextBoolean(),
+ minLines = random.nextInt(),
+ maxLines = random.nextInt(),
+ )
+ }
+ )
+ }
+ update { seed++ }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun updateDisjointGroups() = runBlockingTestWithFrameClock {
+ var seed by mutableIntStateOf(1337)
+ measureRecomposeSuspending {
+ compose {
+ MinimalBox {
+ repeat(10) { container ->
+ MinimalBox {
+ MatryoshkaLayout(
+ depth = 100,
+ content = { depth ->
+ if (depth > 50) {
+ val random = Random(seed * container + depth)
+ NonRenderingText(
+ text = random.nextString(),
+ textColor = Color(random.nextInt()),
+ textSize = random.nextInt(6, 32).dp,
+ ellipsize = random.nextBoolean(),
+ minLines = random.nextInt(),
+ maxLines = random.nextInt(),
+ )
+ } else {
+ NonRenderingText("foo")
+ }
+ }
+ )
+ }
+ }
+ }
+ }
+ update { seed++ }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun updateDeepCompositionLocalHierarchy() = runBlockingTestWithFrameClock {
+ val PixelColorLocal = compositionLocalOf { Color.Unspecified }
+ var seed by mutableIntStateOf(1337)
+ measureRecomposeSuspending {
+ compose {
+ val random = remember(seed) { Random(seed) }
+ Pixel(PixelColorLocal.current)
+ CompositionLocalProvider(
+ PixelColorLocal provides Color(random.nextInt())
+ ) {
+ Pixel(PixelColorLocal.current)
+ CompositionLocalProvider(
+ PixelColorLocal provides Color(random.nextInt())
+ ) {
+ Pixel(PixelColorLocal.current)
+ CompositionLocalProvider(
+ PixelColorLocal provides Color(random.nextInt())
+ ) {
+ Pixel(PixelColorLocal.current)
+ CompositionLocalProvider(
+ PixelColorLocal provides Color(random.nextInt())
+ ) {
+ Pixel(PixelColorLocal.current)
+ CompositionLocalProvider(
+ PixelColorLocal provides Color(random.nextInt())
+ ) {
+ Pixel(PixelColorLocal.current)
+ CompositionLocalProvider(
+ PixelColorLocal provides Color(random.nextInt())
+ ) {
+ Pixel(PixelColorLocal.current)
+ CompositionLocalProvider(
+ PixelColorLocal provides Color(random.nextInt())
+ ) {
+ Pixel(PixelColorLocal.current)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ update { seed++ }
+ }
+ }
+
+ @UiThreadTest
+ @Test
+ fun reverseGroups() = runBlockingTestWithFrameClock {
+ val originalItems = (1..100).toList()
+ var keys by mutableStateOf(originalItems)
+ measureRecomposeSuspending {
+ compose {
+ Column(
+ modifier = Modifier.size(width = 20.dp, height = 300.dp)
+ ) {
+ keys.forEach {
+ key(it) {
+ Pixel(color = Color.Blue)
+ }
+ }
+ }
+ }
+ update { keys = keys.reversed() }
+ reset { keys = originalItems }
+ }
+ }
+}
+
+@Composable
+private fun Pixel(color: Color) {
+ Layout(
+ modifier = Modifier.background(color)
+ ) { _, _ ->
+ layout(1, 1) {}
+ }
+}
+
+@Composable
+private fun NonRenderingText(
+ text: String,
+ textColor: Color = Color.Unspecified,
+ textSize: Dp = Dp.Unspecified,
+ ellipsize: Boolean = false,
+ minLines: Int = 1,
+ maxLines: Int = Int.MAX_VALUE
+) {
+ use(text)
+ use(textColor.value.toInt())
+ use(textSize.value)
+ use(ellipsize)
+ use(minLines)
+ use(maxLines)
+ Layout { _, _ ->
+ layout(1, 1) {}
+ }
+}
+
+@Composable
+private fun MinimalBox(
+ modifier: Modifier = Modifier,
+ content: @Composable () -> Unit
+) {
+ Layout(content, modifier, MinimalBoxMeasurePolicy)
+}
+
+@Composable
+private fun MatryoshkaLayout(
+ depth: Int,
+ content: @Composable (depth: Int) -> Unit
+) {
+ if (depth <= 0) {
+ content(0)
+ } else {
+ Layout(
+ content = {
+ content(depth)
+ MatryoshkaLayout(depth - 1, content)
+ },
+ measurePolicy = MinimalBoxMeasurePolicy
+ )
+ }
+}
+
+private val MinimalBoxMeasurePolicy = MeasurePolicy { measurables, constraints ->
+ val placeables = measurables.map { it.measure(constraints) }
+ val (usedWidth, usedHeight) = placeables.fold(
+ initial = IntOffset(0, 0)
+ ) { (maxWidth, maxHeight), placeable ->
+ IntOffset(
+ maxOf(maxWidth, placeable.measuredWidth),
+ maxOf(maxHeight, placeable.measuredHeight)
+ )
+ }
+
+ layout(
+ width = usedWidth,
+ height = usedHeight
+ ) {
+ placeables.forEach { it.place(0, 0) }
+ }
+}
+
+private fun Random.nextString(length: Int = 16) = buildString(length) {
+ repeat(length) { append(nextInt('A'.code, 'z'.code).toChar()) }
+}
+
+@Suppress("UNUSED_PARAMETER") private fun use(value: Any?) {}
+@Suppress("UNUSED_PARAMETER") private fun use(value: Int) {}
+@Suppress("UNUSED_PARAMETER") private fun use(value: Long) {}
+@Suppress("UNUSED_PARAMETER") private fun use(value: Float) {}
+@Suppress("UNUSED_PARAMETER") private fun use(value: Double) {}
+@Suppress("UNUSED_PARAMETER") private fun use(value: Boolean) {}
diff --git a/compose/runtime/runtime/lint-baseline.xml b/compose/runtime/runtime/lint-baseline.xml
index 3ec6029..457d006 100644
--- a/compose/runtime/runtime/lint-baseline.xml
+++ b/compose/runtime/runtime/lint-baseline.xml
@@ -1,41 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
-
- <issue
- id="BanSuppressTag"
- message="@suppress is not allowed in documentation"
- errorLine1="fun simulateHotReload(context: Any) = HotReloader.simulateHotReload(context)"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/runtime/HotReloader.kt"/>
- </issue>
-
- <issue
- id="BanSuppressTag"
- message="@suppress is not allowed in documentation"
- errorLine1="fun invalidateGroupsWithKey(key: Int) = HotReloader.invalidateGroupsWithKey(key)"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/runtime/HotReloader.kt"/>
- </issue>
-
- <issue
- id="BanSuppressTag"
- message="@suppress is not allowed in documentation"
- errorLine1="fun currentCompositionErrors(): List<Pair<Exception, Boolean>> ="
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/runtime/HotReloader.kt"/>
- </issue>
-
- <issue
- id="BanSuppressTag"
- message="@suppress is not allowed in documentation"
- errorLine1="fun clearCompositionErrors() = HotReloader.clearErrors()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/commonMain/kotlin/androidx/compose/runtime/HotReloader.kt"/>
- </issue>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="ExperimentalPropertyAnnotation"
diff --git a/compose/ui/ui-android-stubs/build.gradle b/compose/ui/ui-android-stubs/build.gradle
index 161d6f7..168677e 100644
--- a/compose/ui/ui-android-stubs/build.gradle
+++ b/compose/ui/ui-android-stubs/build.gradle
@@ -34,7 +34,7 @@
androidx {
name = "Compose Android Stubs"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Stubs for classes in older Android APIs"
metalavaK2UastEnabled = true
diff --git a/compose/ui/ui-geometry/build.gradle b/compose/ui/ui-geometry/build.gradle
index e731a2f..7f7f827 100644
--- a/compose/ui/ui-geometry/build.gradle
+++ b/compose/ui/ui-geometry/build.gradle
@@ -95,7 +95,7 @@
androidx {
name = "Compose Geometry"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose classes related to dimensions without units"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/GeometryUtilsTest.kt b/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/GeometryUtilsTest.kt
index 4776374..78a86ca 100644
--- a/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/GeometryUtilsTest.kt
+++ b/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/GeometryUtilsTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.ui.geometry
+import androidx.compose.ui.util.floatFromBits
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -23,7 +24,6 @@
@RunWith(JUnit4::class)
class GeometryUtilsTest {
-
@Test
fun testRoundDownToNearestTenth() {
assertEquals("1.2", 1.234f.toStringAsFixed(1))
@@ -35,12 +35,12 @@
}
@Test
- fun testRoundDownToNearestHundreth() {
+ fun testRoundDownToNearestHundredth() {
assertEquals("1.23", 1.234f.toStringAsFixed(2))
}
@Test
- fun testRoundUpToNearestHundreth() {
+ fun testRoundUpToNearestHundredth() {
assertEquals("1.24", 1.235f.toStringAsFixed(2))
}
@@ -53,4 +53,17 @@
fun testRoundDownToNearestInt() {
assertEquals("0", 0.49f.toStringAsFixed(0))
}
+
+ @Test
+ fun testNaN() {
+ assertEquals("NaN", Float.NaN.toStringAsFixed(1))
+ // Arbitrary NaN
+ assertEquals("NaN", floatFromBits(0x7F824000).toStringAsFixed(1))
+ }
+
+ @Test
+ fun testInfinity() {
+ assertEquals("Infinity", Float.POSITIVE_INFINITY.toStringAsFixed(1))
+ assertEquals("-Infinity", Float.NEGATIVE_INFINITY.toStringAsFixed(1))
+ }
}
diff --git a/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/OffsetTest.kt b/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/OffsetTest.kt
index 75991f7..eac13e52 100644
--- a/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/OffsetTest.kt
+++ b/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/OffsetTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.ui.geometry
+import androidx.compose.ui.util.floatFromBits
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -24,6 +25,9 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+// A NaN that is not Float.NaN
+private val AnotherNaN = floatFromBits(0x7f800001)
+
@RunWith(JUnit4::class)
class OffsetTest {
@Test
@@ -105,6 +109,7 @@
@Test
fun testUnspecifiedEquals() {
// Verify that verifying equality here does not crash
+ @Suppress("KotlinConstantConditions")
assertTrue(Offset.Unspecified == Offset.Unspecified)
}
@@ -122,4 +127,64 @@
fun testUnspecifiedOffsetToString() {
assertEquals("Offset.Unspecified", Offset.Unspecified.toString())
}
+
+ @Test
+ fun testUnaryMinus() {
+ assertEquals(Offset(-10.0f, -20.0f), -Offset(10.0f, 20.0f))
+ assertEquals(Offset(10.0f, 20.0f), -Offset(-10.0f, -20.0f))
+ assertEquals(
+ Offset(-10.0f, Float.NEGATIVE_INFINITY),
+ -Offset(10.0f, Float.POSITIVE_INFINITY)
+ )
+ assertEquals(
+ Offset(-10.0f, Float.POSITIVE_INFINITY),
+ -Offset(10.0f, Float.NEGATIVE_INFINITY)
+ )
+
+ // behavior for -Unspecified
+ val minusUnspecified = -Offset(Float.NaN, Float.NaN)
+ assertTrue(minusUnspecified.x.isNaN())
+ assertTrue(minusUnspecified.y.isNaN())
+ assertTrue(minusUnspecified.isUnspecified)
+ assertFalse(minusUnspecified.isSpecified)
+ assertFalse(minusUnspecified.isFinite)
+ }
+
+ @Test
+ fun testIsFinite() {
+ assertTrue(Offset(10.0f, 20.0f).isFinite)
+ assertTrue(Offset(0.0f, 0.0f).isFinite)
+ assertTrue(Offset(10.0f, -20.0f).isFinite)
+
+ assertFalse(Offset(10.0f, Float.POSITIVE_INFINITY).isFinite)
+ assertFalse(Offset(10.0f, Float.NEGATIVE_INFINITY).isFinite)
+ assertFalse(Offset(Float.POSITIVE_INFINITY, 20.0f).isFinite)
+ assertFalse(Offset(Float.NEGATIVE_INFINITY, 20.0f).isFinite)
+ assertFalse(Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY).isFinite)
+
+ // isFinite should return false for unspecified/NaN values
+ assertFalse(Offset.Unspecified.isFinite)
+ assertFalse(Offset(Float.NaN, 10.0f).isFinite)
+ assertFalse(Offset(10.0f, Float.NaN).isFinite)
+ assertFalse(Offset(AnotherNaN, AnotherNaN).isFinite)
+ }
+
+ @Test
+ fun testIsValid() {
+ assertTrue(Offset(10.0f, 20.0f).isValid())
+ assertTrue(Offset(0.0f, 0.0f).isValid())
+ assertTrue(Offset(10.0f, -20.0f).isValid())
+
+ assertTrue(Offset(10.0f, Float.POSITIVE_INFINITY).isValid())
+ assertTrue(Offset(10.0f, Float.NEGATIVE_INFINITY).isValid())
+ assertTrue(Offset(Float.POSITIVE_INFINITY, 20.0f).isValid())
+ assertTrue(Offset(Float.NEGATIVE_INFINITY, 20.0f).isValid())
+ assertTrue(Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY).isValid())
+
+ // isFinite should return false for unspecified/NaN values
+ assertFalse(Offset.Unspecified.isValid())
+ assertFalse(Offset(Float.NaN, 10.0f).isValid())
+ assertFalse(Offset(10.0f, Float.NaN).isValid())
+ assertFalse(Offset(AnotherNaN, AnotherNaN).isValid())
+ }
}
diff --git a/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/SizeTest.kt b/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/SizeTest.kt
index 01d8f1d..a6b033c 100644
--- a/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/SizeTest.kt
+++ b/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/SizeTest.kt
@@ -16,7 +16,10 @@
package androidx.compose.ui.geometry
-import org.junit.Assert
+import kotlin.test.assertFails
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
@@ -27,11 +30,11 @@
@Test
fun sizeTimesInt() {
- Assert.assertEquals(
+ assertEquals(
Size(10f, 10f),
Size(2.5f, 2.5f) * 4f
)
- Assert.assertEquals(
+ assertEquals(
Size(10f, 10f),
4f * Size(2.5f, 2.5f)
)
@@ -39,7 +42,7 @@
@Test
fun sizeDivInt() {
- Assert.assertEquals(
+ assertEquals(
Size(10f, 10f),
Size(40f, 40f) / 4f
)
@@ -47,24 +50,24 @@
@Test
fun sizeTimesFloat() {
- Assert.assertEquals(Size(10f, 10f), Size(4f, 4f) * 2.5f)
- Assert.assertEquals(Size(10f, 10f), 2.5f * Size(4f, 4f))
+ assertEquals(Size(10f, 10f), Size(4f, 4f) * 2.5f)
+ assertEquals(Size(10f, 10f), 2.5f * Size(4f, 4f))
}
@Test
fun sizeDivFloat() {
- Assert.assertEquals(Size(10f, 10f), Size(40f, 40f) / 4f)
+ assertEquals(Size(10f, 10f), Size(40f, 40f) / 4f)
}
@Test
fun sizeTimesDouble() {
- Assert.assertEquals(Size(10f, 10f), Size(4f, 4f) * 2.5f)
- Assert.assertEquals(Size(10f, 10f), 2.5f * Size(4f, 4f))
+ assertEquals(Size(10f, 10f), Size(4f, 4f) * 2.5f)
+ assertEquals(Size(10f, 10f), 2.5f * Size(4f, 4f))
}
@Test
fun sizeDivDouble() {
- Assert.assertEquals(
+ assertEquals(
Size(10f, 10f),
Size(40f, 40f) / 4.0f
)
@@ -73,23 +76,23 @@
@Test
fun testSizeCopy() {
val size = Size(100f, 200f)
- Assert.assertEquals(size, size.copy())
+ assertEquals(size, size.copy())
}
@Test
fun testSizeCopyOverwriteWidth() {
val size = Size(100f, 200f)
val copy = size.copy(width = 50f)
- Assert.assertEquals(50f, copy.width)
- Assert.assertEquals(200f, copy.height)
+ assertEquals(50f, copy.width)
+ assertEquals(200f, copy.height)
}
@Test
fun testSizeCopyOverwriteHeight() {
val size = Size(100f, 200f)
val copy = size.copy(height = 300f)
- Assert.assertEquals(100f, copy.width)
- Assert.assertEquals(300f, copy.height)
+ assertEquals(100f, copy.width)
+ assertEquals(300f, copy.height)
}
@Test
@@ -137,38 +140,61 @@
fun testSizeLerp() {
val size1 = Size(100f, 200f)
val size2 = Size(300f, 500f)
- Assert.assertEquals(Size(200f, 350f), lerp(size1, size2, 0.5f))
+ assertEquals(Size(200f, 350f), lerp(size1, size2, 0.5f))
}
@Test
fun testIsSpecified() {
- Assert.assertFalse(Size.Unspecified.isSpecified)
- Assert.assertTrue(Size(1f, 1f).isSpecified)
+ assertFalse(Size.Unspecified.isSpecified)
+ assertTrue(Size(1f, 1f).isSpecified)
}
@Test
fun testIsUnspecified() {
- Assert.assertTrue(Size.Unspecified.isUnspecified)
- Assert.assertFalse(Size(1f, 1f).isUnspecified)
+ assertTrue(Size.Unspecified.isUnspecified)
+ assertFalse(Size(1f, 1f).isUnspecified)
}
@Test
fun testTakeOrElseTrue() {
- Assert.assertTrue(Size(1f, 1f).takeOrElse { Size.Unspecified }.isSpecified)
+ assertTrue(Size(1f, 1f).takeOrElse { Size.Unspecified }.isSpecified)
}
@Test
fun testTakeOrElseFalse() {
- Assert.assertTrue(Size.Unspecified.takeOrElse { Size(1f, 1f) }.isSpecified)
+ assertTrue(Size.Unspecified.takeOrElse { Size(1f, 1f) }.isSpecified)
}
@Test
fun testUnspecifiedSizeToString() {
- Assert.assertEquals("Size.Unspecified", Size.Unspecified.toString())
+ assertEquals("Size.Unspecified", Size.Unspecified.toString())
}
@Test
fun testSpecifiedSizeToString() {
- Assert.assertEquals("Size(10.0, 20.0)", Size(10f, 20f).toString())
+ assertEquals("Size(10.0, 20.0)", Size(10f, 20f).toString())
+ }
+
+ @Test
+ fun testIsEmpty() {
+ assertFalse(Size(10.0f, 20.0f).isEmpty())
+ assertFalse(Size(10.0f, Float.POSITIVE_INFINITY).isEmpty())
+ assertFalse(Size(Float.POSITIVE_INFINITY, 20.0f).isEmpty())
+
+ assertTrue(Size(0.0f, 20.0f).isEmpty())
+ assertTrue(Size(10.0f, 0.0f).isEmpty())
+ assertTrue(Size(0.0f, 0.0f).isEmpty())
+ assertTrue(Size(-10.0f, 20.0f).isEmpty())
+ assertTrue(Size(10.0f, -20.0f).isEmpty())
+ assertTrue(Size(0.0f, Float.POSITIVE_INFINITY).isEmpty())
+ assertTrue(Size(Float.POSITIVE_INFINITY, 0.0f).isEmpty())
+ assertTrue(Size(0.0f, Float.NEGATIVE_INFINITY).isEmpty())
+ assertTrue(Size(Float.NEGATIVE_INFINITY, 0.0f).isEmpty())
+ assertTrue(Size(Float.NEGATIVE_INFINITY, 20.0f).isEmpty())
+ assertTrue(Size(10.0f, Float.NEGATIVE_INFINITY).isEmpty())
+
+ assertFails {
+ Size.Unspecified.isEmpty()
+ }
}
}
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/GeometryUtils.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/GeometryUtils.kt
index de12cbb..73ede37 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/GeometryUtils.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/GeometryUtils.kt
@@ -21,6 +21,9 @@
// File of internal utility methods used for the geometry library
internal fun Float.toStringAsFixed(digits: Int): String {
+ if (isNaN()) return "NaN"
+ if (isInfinite()) return if (this < 0f) "-Infinity" else "Infinity"
+
val clampedDigits: Int = max(digits, 0) // Accept positive numbers and 0 only
val pow = 10f.pow(clampedDigits)
val shifted = this * pow // shift the given value by the corresponding power of 10
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/InlineClassHelper.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/InlineClassHelper.kt
index 27932f6..b9d0097 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/InlineClassHelper.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/InlineClassHelper.kt
@@ -19,15 +19,26 @@
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
-// Masks all float values that are infinity or NaN (i.e. any non-finite value)
-internal const val FloatNonFiniteMask = 0x7fffffffL
+// Masks everything but the sign bit
+internal const val DualUnsignedFloatMask = 0x7fffffff_7fffffffL
// Any value greater than this is a NaN
internal const val FloatInfinityBase = 0x7f800000L
+internal const val DualFloatInfinityBase = 0x7f800000_7f800000L
// Same as Offset/Size.Unspecified.packedValue, but avoids a getstatic
internal const val UnspecifiedPackedFloats = 0x7fc00000_7fc00000L // NaN_NaN
+// 0x80000000_80000000UL.toLong() but expressed as a const value
+// Mask for the sign bit of the two floats packed in a long
+internal const val DualFloatSignBit = -0x7fffffff_80000000L
+// Set the highest bit of each 32 bit chunk in a 64 bit word
+internal const val Uint64High32 = -0x7fffffff_80000000L
+// Set the lowest bit of each 32 bit chunk in a 64 bit word
+internal const val Uint64Low32 = 0x00000001_00000001L
+// Encodes the first valid NaN in each of the 32 bit chunk of a 64 bit word
+internal const val DualFirstNaN = 0x7f800001_7f800001L
+
// This function exists so we do *not* inline the throw. It keeps
// the call site much smaller and since it's the slow path anyway,
// we don't mind the extra function call
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
index 9f026f5..317ff24 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
@@ -61,24 +61,10 @@
@kotlin.jvm.JvmInline
value class Offset internal constructor(internal val packedValue: Long) {
@Stable
- val x: Float
- get() {
- // Explicitly compare against packed values to avoid auto-boxing of Size.Unspecified
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "Offset is unspecified"
- }
- return unpackFloat1(packedValue)
- }
+ val x: Float get() = unpackFloat1(packedValue)
@Stable
- val y: Float
- get() {
- // Explicitly compare against packed values to avoid auto-boxing of Size.Unspecified
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "Offset is unspecified"
- }
- return unpackFloat2(packedValue)
- }
+ val y: Float get() = unpackFloat2(packedValue)
@Stable
inline operator fun component1(): Float = x
@@ -91,7 +77,7 @@
* x or y parameter
*/
fun copy(x: Float = unpackFloat1(packedValue), y: Float = unpackFloat2(packedValue)) =
- Offset(x, y)
+ Offset(packFloats(x, y))
companion object {
/**
@@ -100,34 +86,36 @@
* This can be used to represent the origin of a coordinate space.
*/
@Stable
- val Zero = Offset(0.0f, 0.0f)
+ val Zero = Offset(0x0L)
/**
* An offset with infinite x and y components.
*
- * See also:
- *
- * * [isInfinite], which checks whether either component is infinite.
- * * [isFinite], which checks whether both components are finite.
+ * See also [isFinite] to check whether both components are finite.
*/
// This is included for completeness, because [Size.infinite] exists.
@Stable
- val Infinite = Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
+ val Infinite = Offset(DualFloatInfinityBase)
/**
* Represents an unspecified [Offset] value, usually a replacement for `null`
* when a primitive value is desired.
*/
@Stable
- val Unspecified = Offset(Float.NaN, Float.NaN)
+ val Unspecified = Offset(UnspecifiedPackedFloats)
}
+ /**
+ * Returns:
+ * - False if [x] or [y] is a NaN
+ * - True if [x] or [y] is infinite
+ * - True otherwise
+ */
@Stable
fun isValid(): Boolean {
- val convertX = (packedValue shr 32) and FloatNonFiniteMask
- val convertY = packedValue and FloatNonFiniteMask
-
- return (convertX <= FloatInfinityBase) && (convertY <= FloatInfinityBase)
+ // Take the unsigned packed floats and see if they are < InfinityBase + 1 (first NaN)
+ val v = packedValue and DualUnsignedFloatMask
+ return (v - DualFirstNaN) and v.inv() and Uint64High32 == Uint64High32
}
/**
@@ -138,9 +126,6 @@
*/
@Stable
fun getDistance(): Float {
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "Offset is unspecified"
- }
val x = unpackFloat1(packedValue)
val y = unpackFloat2(packedValue)
return sqrt(x * x + y * y)
@@ -153,9 +138,6 @@
*/
@Stable
fun getDistanceSquared(): Float {
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "Offset is unspecified"
- }
val x = unpackFloat1(packedValue)
val y = unpackFloat2(packedValue)
return x * x + y * y
@@ -171,10 +153,7 @@
*/
@Stable
operator fun unaryMinus(): Offset {
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "Offset is unspecified"
- }
- return Offset(-unpackFloat1(packedValue), -unpackFloat2(packedValue))
+ return Offset(packedValue xor DualFloatSignBit)
}
/**
@@ -186,15 +165,11 @@
*/
@Stable
operator fun minus(other: Offset): Offset {
- checkPrecondition(
- packedValue != UnspecifiedPackedFloats &&
- other.packedValue != UnspecifiedPackedFloats
- ) {
- "Offset is unspecified"
- }
return Offset(
- unpackFloat1(packedValue) - unpackFloat1(other.packedValue),
- unpackFloat2(packedValue) - unpackFloat2(other.packedValue),
+ packFloats(
+ unpackFloat1(packedValue) - unpackFloat1(other.packedValue),
+ unpackFloat2(packedValue) - unpackFloat2(other.packedValue)
+ )
)
}
@@ -207,15 +182,11 @@
*/
@Stable
operator fun plus(other: Offset): Offset {
- checkPrecondition(
- packedValue != UnspecifiedPackedFloats &&
- other.packedValue != UnspecifiedPackedFloats
- ) {
- "Offset is unspecified"
- }
return Offset(
- unpackFloat1(packedValue) + unpackFloat1(other.packedValue),
- unpackFloat2(packedValue) + unpackFloat2(other.packedValue),
+ packFloats(
+ unpackFloat1(packedValue) + unpackFloat1(other.packedValue),
+ unpackFloat2(packedValue) + unpackFloat2(other.packedValue)
+ )
)
}
@@ -228,12 +199,11 @@
*/
@Stable
operator fun times(operand: Float): Offset {
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "Offset is unspecified"
- }
return Offset(
- unpackFloat1(packedValue) * operand,
- unpackFloat2(packedValue) * operand,
+ packFloats(
+ unpackFloat1(packedValue) * operand,
+ unpackFloat2(packedValue) * operand
+ )
)
}
@@ -246,12 +216,11 @@
*/
@Stable
operator fun div(operand: Float): Offset {
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "Offset is unspecified"
- }
return Offset(
- unpackFloat1(packedValue) / operand,
- unpackFloat2(packedValue) / operand,
+ packFloats(
+ unpackFloat1(packedValue) / operand,
+ unpackFloat2(packedValue) / operand
+ )
)
}
@@ -264,12 +233,11 @@
*/
@Stable
operator fun rem(operand: Float): Offset {
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "Offset is unspecified"
- }
return Offset(
- unpackFloat1(packedValue) % operand,
- unpackFloat2(packedValue) % operand,
+ packFloats(
+ unpackFloat1(packedValue) % operand,
+ unpackFloat2(packedValue) % operand
+ )
)
}
@@ -299,42 +267,40 @@
*/
@Stable
fun lerp(start: Offset, stop: Offset, fraction: Float): Offset {
- checkPrecondition(
- start.packedValue != UnspecifiedPackedFloats &&
- stop.packedValue != UnspecifiedPackedFloats
- ) {
- "Offset is unspecified"
- }
return Offset(
- lerp(unpackFloat1(start.packedValue), unpackFloat1(stop.packedValue), fraction),
- lerp(unpackFloat2(start.packedValue), unpackFloat2(stop.packedValue), fraction)
+ packFloats(
+ lerp(unpackFloat1(start.packedValue), unpackFloat1(stop.packedValue), fraction),
+ lerp(unpackFloat2(start.packedValue), unpackFloat2(stop.packedValue), fraction)
+ )
)
}
/**
- * True if both x and y values of the [Offset] are finite
+ * True if both x and y values of the [Offset] are finite. NaN values are not
+ * considered finite.
*/
@Stable
val Offset.isFinite: Boolean get() {
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "Offset is unspecified"
- }
- val x = (packedValue shr 32) and FloatInfinityBase
- val y = packedValue and FloatInfinityBase
- return x != FloatInfinityBase && y != FloatInfinityBase
+ // Mask out the sign bit and do an equality check in each 32-bit lane
+ // against the "infinity base" mask (to check whether each packed float
+ // is infinite or not).
+ val v = (packedValue and DualFloatInfinityBase) xor DualFloatInfinityBase
+ return (v - Uint64Low32) and v.inv() and Uint64High32 == 0L
}
/**
* `false` when this is [Offset.Unspecified].
*/
@Stable
-val Offset.isSpecified: Boolean get() = packedValue != UnspecifiedPackedFloats
+val Offset.isSpecified: Boolean
+ get() = packedValue and DualUnsignedFloatMask != UnspecifiedPackedFloats
/**
* `true` when this is [Offset.Unspecified].
*/
@Stable
-val Offset.isUnspecified: Boolean get() = packedValue == UnspecifiedPackedFloats
+val Offset.isUnspecified: Boolean
+ get() = packedValue and DualUnsignedFloatMask == UnspecifiedPackedFloats
/**
* If this [Offset] [isSpecified] then this is returned, otherwise [block] is executed
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt
index 8bd6c27..df0c802 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt
@@ -74,7 +74,7 @@
* width or height parameter
*/
fun copy(width: Float = unpackFloat1(packedValue), height: Float = unpackFloat2(packedValue)) =
- Size(width, height)
+ Size(packFloats(width, height))
companion object {
@@ -82,7 +82,7 @@
* An empty size, one with a zero width and a zero height.
*/
@Stable
- val Zero = Size(0.0f, 0.0f)
+ val Zero = Size(0x0L)
/**
* A size whose [width] and [height] are unspecified. This is a sentinel
@@ -90,7 +90,7 @@
* Access to width or height on an unspecified size is not allowed.
*/
@Stable
- val Unspecified = Size(Float.NaN, Float.NaN)
+ val Unspecified = Size(UnspecifiedPackedFloats)
}
/**
@@ -103,7 +103,17 @@
if (packedValue == UnspecifiedPackedFloats) {
throwIllegalStateException("Size is unspecified")
}
- return unpackFloat1(packedValue) <= 0.0f || unpackFloat2(packedValue) <= 0.0f
+ // Mask the sign bits, shift them to the right and replicate them by multiplying by -1.
+ // This will give us a mask of 0xffff_ffff for negative packed floats, and 0x0000_0000
+ // for positive packed floats. We invert the mask and do an and operation with the
+ // original value to set any negative float to 0.0f.
+ val v = packedValue and ((packedValue and DualFloatSignBit ushr 31) * -0x1).inv()
+ // At this point any negative float is set to 0, so the sign bit is always 0.
+ // We take the 2 packed floats and "and" them together: if any of the two floats
+ // is 0.0f (either because the original value is 0.0f or because it was negative and
+ // we turned it into 0.0f with the line above), the result of the and operation will
+ // be 0 and we know our Size is empty.
+ return ((v ushr 32) and (v and 0xffffffffL)) == 0L
}
/**
@@ -119,8 +129,10 @@
throwIllegalStateException("Size is unspecified")
}
return Size(
- unpackFloat1(packedValue) * operand,
- unpackFloat2(packedValue) * operand,
+ packFloats(
+ unpackFloat1(packedValue) * operand,
+ unpackFloat2(packedValue) * operand
+ )
)
}
@@ -137,8 +149,10 @@
throwIllegalStateException("Size is unspecified")
}
return Size(
- unpackFloat1(packedValue) / operand,
- unpackFloat2(packedValue) / operand,
+ packFloats(
+ unpackFloat1(packedValue) / operand,
+ unpackFloat2(packedValue) / operand
+ )
)
}
@@ -221,8 +235,10 @@
throwIllegalStateException("Offset is unspecified")
}
return Size(
- lerp(unpackFloat1(start.packedValue), unpackFloat1(stop.packedValue), fraction),
- lerp(unpackFloat2(start.packedValue), unpackFloat2(stop.packedValue), fraction)
+ packFloats(
+ lerp(unpackFloat1(start.packedValue), unpackFloat1(stop.packedValue), fraction),
+ lerp(unpackFloat2(start.packedValue), unpackFloat2(stop.packedValue), fraction)
+ )
)
}
diff --git a/compose/ui/ui-graphics/api/current.txt b/compose/ui/ui-graphics/api/current.txt
index 515e4f5..41516b7 100644
--- a/compose/ui/ui-graphics/api/current.txt
+++ b/compose/ui/ui-graphics/api/current.txt
@@ -918,7 +918,7 @@
method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.Shadow lerp(androidx.compose.ui.graphics.Shadow start, androidx.compose.ui.graphics.Shadow stop, float fraction);
}
- @androidx.compose.runtime.Immutable public interface Shape {
+ @androidx.compose.runtime.Stable public interface Shape {
method public androidx.compose.ui.graphics.Outline createOutline(long size, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.unit.Density density);
}
diff --git a/compose/ui/ui-graphics/api/restricted_current.txt b/compose/ui/ui-graphics/api/restricted_current.txt
index cd5b143..9da68f8 100644
--- a/compose/ui/ui-graphics/api/restricted_current.txt
+++ b/compose/ui/ui-graphics/api/restricted_current.txt
@@ -193,6 +193,7 @@
}
public final class BezierKt {
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static long computeCubicVerticalBounds(float p0y, float p1y, float p2y, float p3y, float[] roots, optional int index);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static long computeHorizontalBounds(androidx.compose.ui.graphics.PathSegment segment, float[] roots, optional int index);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static float evaluateCubic(float p1, float p2, float t);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static float evaluateY(androidx.compose.ui.graphics.PathSegment segment, float t);
@@ -989,7 +990,7 @@
method @androidx.compose.runtime.Stable public static androidx.compose.ui.graphics.Shadow lerp(androidx.compose.ui.graphics.Shadow start, androidx.compose.ui.graphics.Shadow stop, float fraction);
}
- @androidx.compose.runtime.Immutable public interface Shape {
+ @androidx.compose.runtime.Stable public interface Shape {
method public androidx.compose.ui.graphics.Outline createOutline(long size, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.unit.Density density);
}
diff --git a/compose/ui/ui-graphics/build.gradle b/compose/ui/ui-graphics/build.gradle
index e95cbdd..0c82d78 100644
--- a/compose/ui/ui-graphics/build.gradle
+++ b/compose/ui/ui-graphics/build.gradle
@@ -76,7 +76,7 @@
// This has stub APIs for access to legacy Android APIs, so we don't want
// any dependency on this module.
compileOnly(project(":compose:ui:ui-android-stubs"))
- implementation("androidx.graphics:graphics-path:1.0.0-beta02")
+ implementation("androidx.graphics:graphics-path:1.0.1")
implementation libs.androidx.core
api("androidx.annotation:annotation-experimental:1.4.0")
}
@@ -139,7 +139,7 @@
androidx {
name = "Compose Graphics"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose graphics"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/OutlineTest.kt b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/OutlineTest.kt
index 776c8d5..c106240 100644
--- a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/OutlineTest.kt
+++ b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/OutlineTest.kt
@@ -22,6 +22,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,4 +56,44 @@
)
assertEquals(Rect(0f, 15f, 100f, 200f), pathOutline.bounds)
}
+
+ @Test
+ fun testRectOutlineEquality() {
+ val outlineRect = Outline.Rectangle(Rect(1f, 2f, 3f, 4f))
+ val equalOutlineRect = Outline.Rectangle(Rect(1f, 2f, 3f, 4f))
+ val differentOutlineRect = Outline.Rectangle(Rect(4f, 3f, 2f, 1f))
+ assertEquals(outlineRect, equalOutlineRect)
+ assertNotEquals(outlineRect, differentOutlineRect)
+ }
+
+ @Test
+ fun testRoundRectOutlineEquality() {
+ val roundRectOutline = Outline.Rounded(
+ RoundRect(5f, 10f, 15f, 20f, CornerRadius(7f))
+ )
+ val equalRoundRectOutline = Outline.Rounded(
+ RoundRect(5f, 10f, 15f, 20f, CornerRadius(7f))
+ )
+ val differentRoundRectOutline = Outline.Rounded(
+ RoundRect(20f, 15f, 10f, 5f, CornerRadius(3f))
+ )
+ assertEquals(roundRectOutline, equalRoundRectOutline)
+ assertNotEquals(roundRectOutline, differentRoundRectOutline)
+ }
+
+ @Test
+ fun testPathOutlineEquality() {
+ val path = Path().apply {
+ moveTo(5f, 15f)
+ lineTo(100f, 200f)
+ lineTo(0f, 200f)
+ close()
+ }
+ val pathOutline = Outline.Generic(path)
+ val pathOutline2 = Outline.Generic(path)
+
+ // Generic outlines should only be referentially equal, as the path can change over time
+ assertEquals(pathOutline, pathOutline)
+ assertNotEquals(pathOutline, pathOutline2)
+ }
}
diff --git a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
index bf3b965..6f14deb 100644
--- a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
+++ b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
@@ -21,9 +21,6 @@
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.os.Build
-import android.os.Handler
-import android.os.HandlerThread
-import android.os.Looper
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
@@ -51,7 +48,6 @@
import androidx.compose.ui.graphics.drawscope.inset
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.nativeCanvas
-import androidx.compose.ui.graphics.throwIllegalArgumentException
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.graphics.toPixelMap
import androidx.compose.ui.unit.Density
@@ -71,10 +67,7 @@
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
import kotlin.test.assertNotNull
-import kotlinx.coroutines.android.asCoroutineDispatcher
import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withContext
-import org.junit.After
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@@ -92,61 +85,12 @@
val TEST_SIZE = IntSize(TEST_WIDTH, TEST_HEIGHT)
}
- private var waitThread: HandlerThread? = null
- private var waitHandler: Handler? = null
-
- private fun obtainWaitHandler(): Handler {
- synchronized(this) {
- var thread = waitThread
- if (thread == null) {
- thread = HandlerThread("waitThread").also {
- it.start()
- waitThread = it
- }
- }
-
- var handler = waitHandler
- if (handler == null) {
- handler = Handler(thread.looper)
- }
- return handler
- }
- }
-
- /**
- * Helper method used to synchronously obtain an [ImageBitmap] from a [GraphicsLayer]
- */
- private fun GraphicsLayer.toImageBitmap(handler: Handler): ImageBitmap {
- if (Looper.myLooper() === handler.looper) {
- throwIllegalArgumentException("Handler looper cannot be the same as the current " +
- "looper: ${Looper.myLooper()?.thread?.name} ${handler.looper.thread.name}")
- }
- val latch = CountDownLatch(1)
- var bitmap: ImageBitmap?
- runBlocking {
- withContext(handler.asCoroutineDispatcher().immediate) {
- bitmap = toImageBitmap()
- latch.countDown()
- }
- latch.await()
- }
- return bitmap!!
- }
-
- @After
- fun teardown() {
- synchronized(this) {
- waitThread?.quit()
- waitThread = null
- }
- }
-
@Test
fun testGraphicsLayerBitmap() {
- var bitmap: ImageBitmap? = null
+ lateinit var layer: GraphicsLayer
graphicsLayerTest(
block = { graphicsContext ->
- graphicsContext.createGraphicsLayer().apply {
+ layer = graphicsContext.createGraphicsLayer().apply {
assertEquals(IntSize.Zero, this.size)
record {
drawRect(
@@ -169,13 +113,13 @@
size = size / 2f
)
}
- bitmap = toImageBitmap(obtainWaitHandler())
}
},
verify = {
+ val bitmap: ImageBitmap = layer.toImageBitmap()
assertNotNull(bitmap)
- assertEquals(TEST_SIZE, IntSize(bitmap!!.width, bitmap!!.height))
- bitmap!!.toPixelMap().verifyQuadrants(
+ assertEquals(TEST_SIZE, IntSize(bitmap.width, bitmap.height))
+ bitmap.toPixelMap().verifyQuadrants(
Color.Red,
Color.Blue,
Color.Green,
@@ -206,7 +150,7 @@
// Nulling out the dependency here should be safe despite attempting to obtain an
// ImageBitmap afterwards
provider = null
- graphicsLayer!!.toImageBitmap(obtainWaitHandler()).toPixelMap().verifyQuadrants(
+ graphicsLayer!!.toImageBitmap().toPixelMap().verifyQuadrants(
Color.Red,
Color.Red,
Color.Red,
@@ -238,6 +182,28 @@
}
@Test
+ fun testDrawAfterDiscard() {
+ var layer: GraphicsLayer? = null
+ graphicsLayerTest(
+ block = { graphicsContext ->
+ layer = graphicsContext.createGraphicsLayer().apply {
+ assertEquals(IntSize.Zero, this.size)
+ record {
+ drawRect(Color.Red)
+ }
+ discardDisplayList()
+ }
+ drawLayer(layer!!)
+ },
+ verify = {
+ assertEquals(TEST_SIZE, layer!!.size)
+ assertEquals(IntOffset.Zero, layer!!.topLeft)
+ it.verifyQuadrants(Color.Red, Color.Red, Color.Red, Color.Red)
+ }
+ )
+ }
+
+ @Test
fun testRecordLayerWithSize() {
graphicsLayerTest(
block = { graphicsContext ->
@@ -1290,7 +1256,9 @@
val path = Path().also { it.addOval(Rect(1f, 2f, 3f, 4f)) }
val generic = Outline.Generic(path)
layer.setOutline(generic)
- assertEquals(generic, layer.outline)
+ // We wrap the path in a different Outline object from what we pass in, so compare
+ // the paths instead of the outline instances
+ assertEquals(generic.path, (layer.outline as Outline.Generic).path)
}
)
}
@@ -1355,7 +1323,7 @@
private fun graphicsLayerTest(
block: DrawScope.(GraphicsContext) -> Unit,
- verify: ((PixelMap) -> Unit)? = null,
+ verify: (suspend (PixelMap) -> Unit)? = null,
entireScene: Boolean = false,
usePixelCopy: Boolean = false
) {
@@ -1399,7 +1367,7 @@
resumed.countDown()
}
}
- Assert.assertTrue(resumed.await(300000, TimeUnit.MILLISECONDS))
+ assertTrue(resumed.await(3000, TimeUnit.MILLISECONDS))
if (verify != null) {
val target = if (entireScene) {
@@ -1407,8 +1375,8 @@
} else {
contentView!!
}
- if (usePixelCopy && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- verify(target.captureToImage().toPixelMap())
+ val pixelMap = if (usePixelCopy && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ target.captureToImage().toPixelMap()
} else {
val recordLatch = CountDownLatch(1)
testActivity!!.runOnUiThread {
@@ -1423,11 +1391,14 @@
}
recordLatch.countDown()
}
- assertTrue(recordLatch.await(30000, TimeUnit.MILLISECONDS))
+ assertTrue(recordLatch.await(3000, TimeUnit.MILLISECONDS))
val bitmap = runBlocking {
rootGraphicsLayer!!.toImageBitmap()
}
- verify(bitmap.toPixelMap())
+ bitmap.toPixelMap()
+ }
+ runBlocking {
+ verify(pixelMap)
}
}
} finally {
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
index 5713996..d9f7b38 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
@@ -16,21 +16,22 @@
package androidx.compose.ui.graphics
+import android.content.ComponentCallbacks2
+import android.content.Context
+import android.content.res.Configuration
import android.os.Build
import android.view.View
+import android.view.View.OnAttachStateChangeListener
import android.view.ViewGroup
+import android.view.ViewTreeObserver
import androidx.annotation.RequiresApi
-import androidx.compose.ui.graphics.drawscope.DefaultDensity
import androidx.compose.ui.graphics.layer.GraphicsLayer
-import androidx.compose.ui.graphics.layer.GraphicsLayerImpl
import androidx.compose.ui.graphics.layer.GraphicsLayerV23
import androidx.compose.ui.graphics.layer.GraphicsLayerV29
import androidx.compose.ui.graphics.layer.GraphicsViewLayer
import androidx.compose.ui.graphics.layer.LayerManager
import androidx.compose.ui.graphics.layer.view.DrawChildContainer
import androidx.compose.ui.graphics.layer.view.ViewLayerContainer
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
/**
* Create a new [GraphicsContext] with the provided [ViewGroup] to contain [View] based layers.
@@ -46,6 +47,79 @@
private val lock = Any()
private val layerManager = LayerManager(CanvasHolder())
private var viewLayerContainer: DrawChildContainer? = null
+ private var componentCallbackRegistered = false
+ private var predrawListenerRegistered = false
+
+ private val componentCallback = object : ComponentCallbacks2 {
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ // NO-OP
+ }
+
+ override fun onLowMemory() {
+ // NO-OP
+ }
+
+ override fun onTrimMemory(level: Int) {
+ // See CacheManager.cpp. HWUI releases graphics resources whenever the trim memory
+ // callback exceed the level of TRIM_MEMORY_BACKGROUND so do the same here to
+ // release and recreate the internal ImageReader used to increment the ref count
+ // of internal RenderNodes
+ // Some devices skip straight to TRIM_COMPLETE so ensure we persist layers if
+ // we receive any trim memory callback that exceeds TRIM_MEMORY_BACKGROUND
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
+ // HardwareRenderer instances would be discarded by HWUI so we need to discard
+ // the existing underlying ImageReader instance and do a placeholder render
+ // to increment the refcount of any outstanding layers again the next time the
+ // content is drawn
+ if (!predrawListenerRegistered) {
+ layerManager.destroy()
+ ownerView.viewTreeObserver.addOnPreDrawListener(
+ object : ViewTreeObserver.OnPreDrawListener {
+ override fun onPreDraw(): Boolean {
+ layerManager.updateLayerPersistence()
+ ownerView.viewTreeObserver.removeOnPreDrawListener(this)
+ predrawListenerRegistered = false
+ return true
+ }
+ })
+ predrawListenerRegistered = true
+ }
+ }
+ }
+ }
+
+ init {
+ // Register the component callbacks when the GraphicsContext is created
+ if (ownerView.isAttachedToWindow) {
+ registerComponentCallback(ownerView.context)
+ }
+ ownerView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ // If the View is attached to the window again, re-add the component callbacks
+ registerComponentCallback(v.context)
+ }
+
+ override fun onViewDetachedFromWindow(v: View) {
+ // When the View is detached from the window, remove the component callbacks
+ // used to listen to trim memory signals
+ unregisterComponentCallback(v.context)
+ }
+ })
+ }
+
+ private fun registerComponentCallback(context: Context) {
+ if (!componentCallbackRegistered) {
+ context.applicationContext.registerComponentCallbacks(componentCallback)
+ componentCallbackRegistered = true
+ }
+ }
+
+ private fun unregisterComponentCallback(context: Context) {
+ if (componentCallbackRegistered) {
+ context.applicationContext.unregisterComponentCallbacks(componentCallback)
+ componentCallbackRegistered = false
+ }
+ }
override fun createGraphicsLayer(): GraphicsLayer {
synchronized(lock) {
@@ -73,27 +147,7 @@
)
}
return GraphicsLayer(layerImpl, layerManager).also { layer ->
- // Do a placeholder recording of drawing instructions to avoid errors when doing a
- // persistence render.
- // This will be overridden by the consumer of the created GraphicsLayer
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P &&
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- // Only API levels between M (inclusive) and P (exclusive) require a placeholder
- // displaylist for persistence rendering. On some API levels like (ex. API 28)
- // actually doing a placeholder render before the activity is setup
- // (ex in unit tests) causes the emulator to crash with an NPE in native code
- // on the HWUI canvas implementation
- layer.record(
- DefaultDensity,
- LayoutDirection.Ltr,
- IntSize(1, 1),
- GraphicsLayerImpl.DefaultDrawBlock
- )
- }
layerManager.persist(layer)
- // Reset the size to zero so that immediately after GraphicsLayer creation
- // we do not advertise a size of 1 x 1
- layer.size = IntSize.Zero
}
}
}
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt
index 330d8ec..4356050 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt
@@ -421,7 +421,10 @@
this.layoutDirection = layoutDirection
this.drawBlock = block
impl.isInvalidated = true
+ recordInternal()
+ }
+ private fun recordInternal() {
childDependenciesTracker.withTracking(
onDependencyRemoved = { it.onRemovedFromParentLayer() }
) {
@@ -472,6 +475,10 @@
androidCanvas.concat(impl.calculateMatrix())
}
+ internal fun drawForPersistence(canvas: Canvas) {
+ impl.draw(canvas)
+ }
+
/**
* Draw the contents of this [GraphicsLayer] into the specified [Canvas]
*/
@@ -479,6 +486,26 @@
if (isReleased) {
return
}
+
+ // If the displaylist has been discarded from underneath us, attempt to recreate it.
+ // This can happen if the application resumes from a background state after a trim memory
+ // callback has been invoked with a level greater than or equal to hidden. During which
+ // HWUI attempts to cull out resources that can be recreated quickly.
+ // Because recording instructions invokes the draw lambda again, there can be the potential
+ // for the objects referenced to be invalid for example in the case of a lazylist removal
+ // animation for a Composable that has been disposed, but the GraphicsLayer is drawn
+ // for a transient animation. However, when the application is backgrounded, animations are
+ // stopped anyway so attempts to recreate the displaylist from the draw lambda should
+ // be safe as the draw lambdas should still be valid. If not catch potential exceptions
+ // and continue as UI state would be recreated on resume anyway.
+ if (!impl.hasDisplayList) {
+ try {
+ recordInternal()
+ } catch (_: Throwable) {
+ // NO-OP
+ }
+ }
+
if (pivotOffset.isUnspecified) {
impl.pivotOffset = Offset(size.width / 2f, size.height / 2f)
}
@@ -951,6 +978,9 @@
block: DrawScope.() -> Unit
)
+ val hasDisplayList: Boolean
+ get() = true
+
/**
* @see GraphicsLayer.discardDisplayList
*/
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt
index f240a88..7860a3a 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt
@@ -57,6 +57,7 @@
private var layerPaint: android.graphics.Paint? = null
private var matrix: android.graphics.Matrix? = null
private var outlineIsProvided = false
+ private var recordWasCalled = false
private fun obtainLayerPaint(): android.graphics.Paint =
layerPaint ?: android.graphics.Paint().also { layerPaint = it }
@@ -276,12 +277,16 @@
override var isInvalidated: Boolean = true
+ override val hasDisplayList: Boolean
+ get() = renderNode.isValid
+
override fun record(
density: Density,
layoutDirection: LayoutDirection,
layer: GraphicsLayer,
block: DrawScope.() -> Unit
) {
+ recordWasCalled = true
val recordingCanvas = renderNode.start(size.width, size.height)
canvasHolder.drawInto(recordingCanvas) {
canvasDrawScope.draw(
@@ -298,6 +303,14 @@
}
override fun draw(canvas: androidx.compose.ui.graphics.Canvas) {
+ if (!recordWasCalled) {
+ recordWasCalled = true
+ // Do a placeholder recording of drawing instructions to avoid errors when doing a
+ // persistence render.
+ // This will be overridden by the consumer of the created GraphicsLayer
+ val recordingCanvas = renderNode.start(1, 1)
+ renderNode.end(recordingCanvas)
+ }
(canvas.nativeCanvas as DisplayListCanvas).drawRenderNode(renderNode)
}
@@ -327,6 +340,7 @@
}
private fun discardDisplayListInternal() {
+ recordWasCalled = false
// See b/216660268. RenderNode#discardDisplayList was originally called
// destroyDisplayListData on Android M and below. Make sure we gate on the corresponding
// API level and call the original method name on these API levels, otherwise invoke
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt
index 2d89639..a27f0a4 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt
@@ -247,6 +247,9 @@
return m
}
+ override val hasDisplayList: Boolean
+ get() = renderNode.hasDisplayList()
+
override fun discardDisplayList() {
renderNode.discardDisplayList()
}
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerManager.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerManager.android.kt
index 23b14d9..f2833bd 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerManager.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerManager.android.kt
@@ -19,11 +19,14 @@
import android.graphics.PixelFormat
import android.media.ImageReader
import android.os.Build
+import android.os.Looper
+import android.os.Message
import android.view.Surface
import androidx.annotation.RequiresApi
import androidx.collection.ObjectList
import androidx.collection.mutableObjectListOf
import androidx.compose.ui.graphics.CanvasHolder
+import androidx.core.os.HandlerCompat
/**
* Class responsible for managing the layer lifecycle to support
@@ -44,10 +47,23 @@
*/
private var imageReader: ImageReader? = null
+ private val handler = HandlerCompat.createAsync(Looper.getMainLooper()) {
+ persistLayers(layerList)
+ true
+ }
+
fun persist(layer: GraphicsLayer) {
if (!layerList.contains(layer)) {
layerList.add(layer)
- persistLayers(layerList)
+ if (!handler.hasMessages(0)) {
+ // we don't run persistLayers() synchronously in order to do less work as there
+ // might be a lot of new layers created during one frame. however we also want
+ // to execute it as soon as possible to be able to persist the layers before
+ // they discard their content. it is possible that there is some other work
+ // scheduled on the main thread which is going to change what layers are drawn.
+ // we use sendMessageAtFrontOfQueue() in order to be executed before that.
+ handler.sendMessageAtFrontOfQueue(Message.obtain())
+ }
}
}
@@ -65,20 +81,27 @@
* another internal CanvasContext instance owned by the internal HwuiContext instance of
* a Surface. This is only necessary for Android M and above.
*/
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && layers.isNotEmpty()) {
val reader = imageReader ?: ImageReader.newInstance(
1,
1,
PixelFormat.RGBA_8888,
1
- ).also { imageReader = it }
+ ).apply {
+ // We don't care about the result, but release the buffer back to the queue
+ // for subsequent renders to ensure the RenderThread is free as much as possible
+ setOnImageAvailableListener({ it?.acquireLatestImage()?.close() }, handler)
+ }.also { imageReader = it }
val surface = reader.surface
val canvas = LockHardwareCanvasHelper.lockHardwareCanvas(surface)
// on Robolectric even this canvas is not hardware accelerated and drawing render nodes
// are not supported
if (canvas.isHardwareAccelerated) {
canvasHolder.drawInto(canvas) {
- layers.forEach { layer -> layer.draw(this, null) }
+ canvas.save()
+ canvas.clipRect(0, 0, 1, 1)
+ layers.forEach { layer -> layer.drawForPersistence(this) }
+ canvas.restore()
}
}
surface.unlockCanvasAndPost(canvas)
@@ -89,6 +112,17 @@
imageReader?.close()
imageReader = null
}
+
+ /**
+ * Discards the corresponding ImageReader used to increment the ref count of each layer
+ * and persists the current layer list creating a new ImageReader. This is useful in scenarios
+ * where HWUI releases graphics resources in response to onTrimMemory often when the application
+ * is backgrounded
+ */
+ fun updateLayerPersistence() {
+ destroy()
+ persistLayers(layerList)
+ }
}
@RequiresApi(Build.VERSION_CODES.M)
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt
index acf82f8..45b6ed7 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt
@@ -421,9 +421,11 @@
*/
private fun findDerivativeRoots(
segment: PathSegment,
+ horizontal: Boolean,
roots: FloatArray,
- index: Int = 0,
+ index: Int
): Int {
+ val offset = if (horizontal) 0 else 1
val points = segment.points
return when (segment.type) {
PathSegment.Type.Move -> 0
@@ -434,8 +436,8 @@
// Line derivative of a quadratic function
// We do the computation inline to avoid using arrays of other data
// structures to return the result
- val d0 = 2 * (points[2] - points[0])
- val d1 = 2 * (points[4] - points[2])
+ val d0 = 2 * (points[offset + 2] - points[offset + 0])
+ val d1 = 2 * (points[offset + 4] - points[offset + 2])
findLineRoot(d0, d1, roots, index)
}
@@ -446,9 +448,9 @@
// Quadratic derivative of a cubic function
// We do the computation inline to avoid using arrays of other data
// structures to return the result
- val d0 = 3.0f * (points[2] - points[0])
- val d1 = 3.0f * (points[4] - points[2])
- val d2 = 3.0f * (points[6] - points[4])
+ val d0 = 3.0f * (points[offset + 2] - points[offset + 0])
+ val d1 = 3.0f * (points[offset + 4] - points[offset + 2])
+ val d2 = 3.0f * (points[offset + 6] - points[offset + 4])
val count = findQuadraticRoots(d0, d1, d2, roots, index)
// Compute the second derivative as a line
@@ -477,7 +479,7 @@
roots: FloatArray,
index: Int = 0
): FloatFloatPair {
- val count = findDerivativeRoots(segment, roots, index)
+ val count = findDerivativeRoots(segment, true, roots, index)
var minX = min(segment.startX, segment.endX)
var maxX = max(segment.startX, segment.endX)
@@ -504,18 +506,53 @@
roots: FloatArray,
index: Int = 0
): FloatFloatPair {
- val count = findDerivativeRoots(segment, roots, index)
- var minX = min(segment.startY, segment.endY)
- var maxX = max(segment.startY, segment.endY)
+ val count = findDerivativeRoots(segment, false, roots, index)
+ var minY = min(segment.startY, segment.endY)
+ var maxY = max(segment.startY, segment.endY)
for (i in 0 until count) {
val t = roots[i]
val x = evaluateY(segment, t)
- minX = min(minX, x)
- maxX = max(maxX, x)
+ minY = min(minY, x)
+ maxY = max(maxY, x)
}
- return FloatFloatPair(minX, maxX)
+ return FloatFloatPair(minY, maxY)
+}
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun computeCubicVerticalBounds(
+ p0y: Float,
+ p1y: Float,
+ p2y: Float,
+ p3y: Float,
+ roots: FloatArray,
+ index: Int = 0
+): FloatFloatPair {
+ // Quadratic derivative of a cubic function
+ // We do the computation inline to avoid using arrays of other data
+ // structures to return the result
+ val d0 = 3.0f * (p1y - p0y)
+ val d1 = 3.0f * (p2y - p1y)
+ val d2 = 3.0f * (p3y - p2y)
+ var count = findQuadraticRoots(d0, d1, d2, roots, index)
+
+ // Compute the second derivative as a line
+ val dd0 = 2.0f * (d1 - d0)
+ val dd1 = 2.0f * (d2 - d1)
+ count += findLineRoot(dd0, dd1, roots, index + count)
+
+ var minY = min(p0y, p3y)
+ var maxY = max(p0y, p3y)
+
+ for (i in 0 until count) {
+ val t = roots[i]
+ val y = evaluateCubic(p0y, p1y, p2y, p3y, t)
+ minY = min(minY, y)
+ maxY = max(maxY, y)
+ }
+
+ return FloatFloatPair(minY, maxY)
}
@Suppress("NOTHING_TO_INLINE")
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt
index d327689..d9faada 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt
@@ -135,6 +135,7 @@
* Add a rotation (in radians clockwise) to the current transform at the given pivot point.
* The pivot coordinate remains unchanged by the rotation transformation
*
+ * @param radians Rotation transform to apply to the [Canvas]
* @param pivotX The x-coord for the pivot point
* @param pivotY The y-coord for the pivot point
*/
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
index 469f415..9a77328 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
@@ -335,6 +335,7 @@
* 240 is blue
* @param saturation The amount of [hue] represented in the color in the range (0..1),
* where 0 has no color and 1 is fully saturated.
+ * @param alpha Alpha channel to apply to the computed color
* @param value The strength of the color, where 0 is black.
* @param colorSpace The RGB color space used to calculate the Color from the HSV values.
*/
@@ -370,6 +371,7 @@
* where 0 has no color and 1 is fully saturated.
* @param lightness A range of (0..1) where 0 is black, 0.5 is fully colored, and 1 is
* white.
+ * @param alpha Alpha channel to apply to the computed color
* @param colorSpace The RGB color space used to calculate the Color from the HSL values.
*/
fun hsl(
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ColorMatrix.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ColorMatrix.kt
index bb9218b..70223b4 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ColorMatrix.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/ColorMatrix.kt
@@ -92,6 +92,7 @@
* is represented as a 4 x 5 matrix
* @param column Column index to query the ColorMatrix value. Range is from 0 to 4 as
* [ColorMatrix] is represented as a 4 x 5 matrix
+ * @param v value to update at the given [row] and [column]
*/
inline operator fun set(row: Int, column: Int, v: Float) {
values[(row * 5) + column] = v
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt
index b3c7de3..0c20ed5 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt
@@ -106,18 +106,8 @@
override val bounds: Rect
get() = path.getBounds()
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is Generic) return false
-
- if (path != other.path) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- return path.hashCode()
- }
+ // No equals or hashcode, two different outlines using the same path shouldn't be considered
+ // equal as the path may have changed since the previous outline was rendered
}
/**
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
index a624bad..bffc12d 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Path.kt
@@ -367,6 +367,7 @@
*
* @param path1 The first operand (for difference, the minuend)
* @param path2 The second operand (for difference, the subtrahend)
+ * @param operation [PathOperation] to apply to the 2 specified paths
*
* @return True if operation succeeded, false otherwise and this path remains unmodified.
*/
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/RenderEffect.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/RenderEffect.kt
index 30ebfe3..3e2848c 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/RenderEffect.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/RenderEffect.kt
@@ -50,6 +50,7 @@
* [RenderEffect] that will blur the contents of an optional input [RenderEffect]. If no
* input [RenderEffect] is provided, the drawing commands on the [GraphicsLayerScope] this
* [RenderEffect] is configured on will be blurred.
+ * @param renderEffect Optional input [RenderEffect] to be blurred
* @param radiusX Blur radius in the horizontal direction
* @param radiusY Blur radius in the vertical direction
* @param edgeTreatment Strategy used to render pixels outside of bounds of the original input
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shape.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shape.kt
index e174944..0ce177b 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shape.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Shape.kt
@@ -16,7 +16,7 @@
package androidx.compose.ui.graphics
-import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
@@ -24,7 +24,7 @@
/**
* Defines a generic shape.
*/
-@Immutable
+@Stable
interface Shape {
/**
* Creates [Outline] of this shape for the given [size].
diff --git a/compose/ui/ui-inspection/src/main/cpp/CMakeLists.txt b/compose/ui/ui-inspection/src/main/cpp/CMakeLists.txt
index 417fea7..81e99e1 100644
--- a/compose/ui/ui-inspection/src/main/cpp/CMakeLists.txt
+++ b/compose/ui/ui-inspection/src/main/cpp/CMakeLists.txt
@@ -14,7 +14,7 @@
# the License.
#
-cmake_minimum_required(VERSION 3.4.1)
+cmake_minimum_required(VERSION 3.22.1)
project(compose_ui_inspection)
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index e374fff..4e5c0b0 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -571,9 +571,9 @@
method public static void click(androidx.compose.ui.test.TouchInjectionScope, optional long position);
method public static void doubleClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long delayMillis);
method public static void longClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long durationMillis);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void multiTouchSwipe(androidx.compose.ui.test.TouchInjectionScope, java.util.List<? extends kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset>> curves, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void multiTouchSwipe(androidx.compose.ui.test.TouchInjectionScope, java.util.List<? extends kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset>> curves, optional long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
method public static void pinch(androidx.compose.ui.test.TouchInjectionScope, long start0, long end0, long start1, long end1, optional long durationMillis);
- method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
+ method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, optional long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, long start, long end, optional long durationMillis);
method public static void swipeDown(androidx.compose.ui.test.TouchInjectionScope, optional float startY, optional float endY, optional long durationMillis);
method public static void swipeLeft(androidx.compose.ui.test.TouchInjectionScope, optional float startX, optional float endX, optional long durationMillis);
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index fb5c367..fce6f17 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -572,9 +572,9 @@
method public static void click(androidx.compose.ui.test.TouchInjectionScope, optional long position);
method public static void doubleClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long delayMillis);
method public static void longClick(androidx.compose.ui.test.TouchInjectionScope, optional long position, optional long durationMillis);
- method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void multiTouchSwipe(androidx.compose.ui.test.TouchInjectionScope, java.util.List<? extends kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset>> curves, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
+ method @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public static void multiTouchSwipe(androidx.compose.ui.test.TouchInjectionScope, java.util.List<? extends kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset>> curves, optional long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
method public static void pinch(androidx.compose.ui.test.TouchInjectionScope, long start0, long end0, long start1, long end1, optional long durationMillis);
- method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
+ method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, kotlin.jvm.functions.Function1<? super java.lang.Long,androidx.compose.ui.geometry.Offset> curve, optional long durationMillis, optional java.util.List<java.lang.Long> keyTimes);
method public static void swipe(androidx.compose.ui.test.TouchInjectionScope, long start, long end, optional long durationMillis);
method public static void swipeDown(androidx.compose.ui.test.TouchInjectionScope, optional float startY, optional float endY, optional long durationMillis);
method public static void swipeLeft(androidx.compose.ui.test.TouchInjectionScope, optional float startX, optional float endX, optional long durationMillis);
diff --git a/compose/ui/ui-test/lint-baseline.xml b/compose/ui/ui-test/lint-baseline.xml
index 40df33c..199ff09 100644
--- a/compose/ui/ui-test/lint-baseline.xml
+++ b/compose/ui/ui-test/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
diff --git a/compose/ui/ui-test/samples/src/main/java/androidx/compose/ui/test/samples/TouchInjectionScopeSamples.kt b/compose/ui/ui-test/samples/src/main/java/androidx/compose/ui/test/samples/TouchInjectionScopeSamples.kt
index 6b948c8..29cd365 100644
--- a/compose/ui/ui-test/samples/src/main/java/androidx/compose/ui/test/samples/TouchInjectionScopeSamples.kt
+++ b/compose/ui/ui-test/samples/src/main/java/androidx/compose/ui/test/samples/TouchInjectionScopeSamples.kt
@@ -17,6 +17,8 @@
package androidx.compose.ui.test.samples
import androidx.annotation.Sampled
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.click
import androidx.compose.ui.test.onNodeWithTag
@@ -76,3 +78,35 @@
up()
}
}
+
+@Sampled
+fun touchInputMultiTouchWithHistory() {
+ // Move two fingers in a horizontal line, one on y=100 and one on y=500
+ composeTestRule.onNodeWithTag("myComponent")
+ .performTouchInput {
+ // First, make contact with the screen with both pointers:
+ down(0, Offset(300f, 100f))
+ down(1, Offset(300f, 500f))
+ // Update the pointer locations for the next event
+ updatePointerTo(0, Offset(400f, 100f))
+ updatePointerTo(1, Offset(400f, 500f))
+ // And send the move event with historical data
+ @OptIn(ExperimentalTestApi::class)
+ moveWithHistoryMultiPointer(
+ // Let's add 3 historical events
+ relativeHistoricalTimes = listOf(-12, -8, -4),
+ // Now, for each pointer we supply the historical coordinates
+ historicalCoordinates = listOf(
+ // Pointer 0 moves along y=100
+ listOf(Offset(325f, 100f), Offset(350f, 100f), Offset(375f, 100f)),
+ // Pointer 1 moves along y=500
+ listOf(Offset(325f, 500f), Offset(350f, 500f), Offset(375f, 500f)),
+ ),
+ // The actual move event will be sent 16ms after the previous event
+ delayMillis = 16
+ )
+ // And finish the gesture by lifting both fingers. Can be done in any order
+ up(1)
+ up(0)
+ }
+}
diff --git a/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/key/LockKeysTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/key/LockKeysTest.kt
index 028adab..c93ee2a 100644
--- a/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/key/LockKeysTest.kt
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/key/LockKeysTest.kt
@@ -150,6 +150,31 @@
}
}
+ @Test
+ fun withKeyToggled_keyIsOff_afterThrow() {
+ rule.performKeyInput {
+ try {
+ // When an exception is thrown during withKeyToggled
+ withKeyToggled(Key.CapsLock) { error("") }
+ } catch (e: Exception) { /* ignore */ }
+ // The key was restored to turned off
+ assertFalse(isCapsLockOn)
+ }
+ }
+
+ @Test
+ fun withKeysToggled_keysAreOff_afterThrow() {
+ rule.performKeyInput {
+ try {
+ // When an exception is thrown during withKeyToggled
+ withKeysToggled(listOf(Key.CapsLock, Key.NumLock)) { error("") }
+ } catch (e: Exception) { /* ignore */ }
+ // The keys were restored to turned off
+ assertFalse(isCapsLockOn)
+ assertFalse(isNumLockOn)
+ }
+ }
+
private fun toggleAllLockKeys() {
rule.performKeyInput {
pressKey(Key.CapsLock)
diff --git a/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/key/MetaKeysTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/key/MetaKeysTest.kt
index 606bc6a..68e97e8 100644
--- a/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/key/MetaKeysTest.kt
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/key/MetaKeysTest.kt
@@ -134,6 +134,31 @@
}
}
+ @Test
+ fun withKeyDown_keyIsUp_afterThrow() {
+ rule.performKeyInput {
+ try {
+ // When an exception is thrown during withKeyDown
+ withKeyDown(Key.ShiftLeft) { error("") }
+ } catch (e: Exception) { /* ignore */ }
+ // The key was restored to up
+ assertFalse(isShiftDown)
+ }
+ }
+
+ @Test
+ fun withKeysDown_keysAreUp_afterThrow() {
+ rule.performKeyInput {
+ try {
+ // When an exception is thrown during withKeysDown
+ withKeysDown(listOf(Key.ShiftLeft, Key.AltRight)) { error("") }
+ } catch (e: Exception) { /* ignore */ }
+ // The keys were restored to up
+ assertFalse(isShiftDown)
+ assertFalse(isAltDown)
+ }
+ }
+
private fun pressAllMetaKeysDown() {
rule.performKeyInput {
keyDown(Key.Function)
diff --git a/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/touch/SwipeMultiTouchTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/touch/SwipeMultiTouchTest.kt
new file mode 100644
index 0000000..3b0a4b8
--- /dev/null
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/injectionscope/touch/SwipeMultiTouchTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.test.injectionscope.touch
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventType.Companion.Move
+import androidx.compose.ui.input.pointer.PointerEventType.Companion.Press
+import androidx.compose.ui.input.pointer.PointerEventType.Companion.Release
+import androidx.compose.ui.input.pointer.PointerId
+import androidx.compose.ui.input.pointer.PointerType.Companion.Touch
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.multiTouchSwipe
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.runComposeUiTest
+import androidx.compose.ui.test.util.ClickableTestBox
+import androidx.compose.ui.test.util.SinglePointerInputRecorder
+import androidx.compose.ui.test.util.verify
+import androidx.compose.ui.test.util.verifyEvents
+import androidx.test.filters.MediumTest
+import org.junit.Test
+
+@MediumTest
+@OptIn(ExperimentalTestApi::class)
+class SwipeMultiTouchTest {
+ companion object {
+ private const val TAG = "widget"
+ // Duration is 4 * eventPeriod to get easily predictable results
+ private const val DURATION = 64L
+ }
+
+ private val recorder = SinglePointerInputRecorder()
+
+ @Test
+ fun test() = runComposeUiTest {
+ setContent {
+ Box(Modifier.fillMaxSize()) {
+ ClickableTestBox(modifier = recorder, tag = TAG)
+ }
+ }
+
+ // Move three fingers over the box from left to right simultaneously
+ // With a duration that is exactly 4 times the eventPeriod, each pointer will be sampled
+ // at t = 0, 16, 32, 48 and 64. That corresponds to x values of 10, 30, 50, 70 and 90.
+
+ val curve1 = line(fromX = 10f, toX = 90f, y = 20f, DURATION)
+ val curve2 = line(fromX = 10f, toX = 90f, y = 50f, DURATION)
+ val curve3 = line(fromX = 10f, toX = 90f, y = 80f, DURATION)
+
+ onNodeWithTag(TAG).performTouchInput {
+ multiTouchSwipe(
+ curves = listOf(curve1, curve2, curve3),
+ durationMillis = DURATION
+ )
+ }
+
+ val pointer1 = PointerId(0)
+ val pointer2 = PointerId(1)
+ val pointer3 = PointerId(2)
+
+ runOnIdle {
+ recorder.apply {
+ verifyEvents(
+ // pointer1 down
+ { verify(0L, pointer1, true, Offset(10f, 20f), Touch, Press) },
+ // pointer2 down
+ { verify(0L, pointer1, true, Offset(10f, 20f), Touch, Press) },
+ { verify(0L, pointer2, true, Offset(10f, 50f), Touch, Press) },
+ // pointer3 down
+ { verify(0L, pointer1, true, Offset(10f, 20f), Touch, Press) },
+ { verify(0L, pointer2, true, Offset(10f, 50f), Touch, Press) },
+ { verify(0L, pointer3, true, Offset(10f, 80f), Touch, Press) },
+ // first move
+ { verify(16L, pointer1, true, Offset(30f, 20f), Touch, Move) },
+ { verify(16L, pointer2, true, Offset(30f, 50f), Touch, Move) },
+ { verify(16L, pointer3, true, Offset(30f, 80f), Touch, Move) },
+ // second move
+ { verify(32L, pointer1, true, Offset(50f, 20f), Touch, Move) },
+ { verify(32L, pointer2, true, Offset(50f, 50f), Touch, Move) },
+ { verify(32L, pointer3, true, Offset(50f, 80f), Touch, Move) },
+ // third move
+ { verify(48L, pointer1, true, Offset(70f, 20f), Touch, Move) },
+ { verify(48L, pointer2, true, Offset(70f, 50f), Touch, Move) },
+ { verify(48L, pointer3, true, Offset(70f, 80f), Touch, Move) },
+ // last move
+ { verify(64L, pointer1, true, Offset(90f, 20f), Touch, Move) },
+ { verify(64L, pointer2, true, Offset(90f, 50f), Touch, Move) },
+ { verify(64L, pointer3, true, Offset(90f, 80f), Touch, Move) },
+ // pointer1 up
+ { verify(64L, pointer1, false, Offset(90f, 20f), Touch, Release) },
+ { verify(64L, pointer2, true, Offset(90f, 50f), Touch, Release) },
+ { verify(64L, pointer3, true, Offset(90f, 80f), Touch, Release) },
+ // pointer2 up
+ { verify(64L, pointer2, false, Offset(90f, 50f), Touch, Release) },
+ { verify(64L, pointer3, true, Offset(90f, 80f), Touch, Release) },
+ // pointer3 up
+ { verify(64L, pointer3, false, Offset(90f, 80f), Touch, Release) },
+ )
+ }
+ }
+ }
+
+ @Suppress("SameParameterValue")
+ private fun line(fromX: Float, toX: Float, y: Float, durationMillis: Long): (Long) -> Offset {
+ return { Offset(fromX + (toX - fromX) * it / durationMillis, y) }
+ }
+}
diff --git a/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyAndMouseEventsTest.kt b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyAndMouseEventsTest.kt
index 71d02be..2f412d9 100644
--- a/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyAndMouseEventsTest.kt
+++ b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyAndMouseEventsTest.kt
@@ -438,7 +438,8 @@
Offset.Zero, 0, expectedMetaState = expectedMetaState)
// Key Toggle Off
- recorder.events[8].verifyKeyEvent(keyDown, key.nativeKeyCode)
+ recorder.events[8].verifyKeyEvent(keyDown, key.nativeKeyCode,
+ expectedMetaState = expectedMetaState)
recorder.events[9].verifyKeyEvent(keyUp, key.nativeKeyCode)
// Mouse Press
diff --git a/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyEventsTest.kt b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyEventsTest.kt
index 0216a50..4d1f794 100644
--- a/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyEventsTest.kt
+++ b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyEventsTest.kt
@@ -514,7 +514,8 @@
recorder.events[1].verifyKeyEvent(
keyUp, key.nativeKeyCode, expectedMetaState = expectedMetaState
)
- recorder.events[2].verifyKeyEvent(keyDown, key.nativeKeyCode)
+ recorder.events[2].verifyKeyEvent(keyDown, key.nativeKeyCode,
+ expectedMetaState = expectedMetaState)
recorder.events[3].verifyKeyEvent(keyUp, key.nativeKeyCode)
}
}
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
index 1723384..1d17c65 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/InputDispatcher.kt
@@ -705,6 +705,30 @@
protected abstract fun KeyInputState.enqueueUp(key: Key)
+ /**
+ * Used to control lock key toggling behaviour on different platforms. Defaults to Android-style
+ * toggling. To change toggling behaviour, override this method and switch to using
+ * [LockKeyState.isLockKeyOnExcludingOffPress], or implement a different toggling behaviour.
+ */
+ protected open val KeyInputState.capsLockOn: Boolean
+ get() = capsLockState.isLockKeyOnIncludingOffPress
+
+ /**
+ * Used to control lock key toggling behaviour on different platforms. Defaults to Android-style
+ * toggling. To change toggling behaviour, override this method and switch to using
+ * [LockKeyState.isLockKeyOnExcludingOffPress], or implement a different toggling behaviour.
+ */
+ protected open val KeyInputState.numLockOn: Boolean
+ get() = numLockState.isLockKeyOnIncludingOffPress
+
+ /**
+ * Used to control lock key toggling behaviour on different platforms. Defaults to Android-style
+ * toggling. To change toggling behaviour, override this method and switch to using
+ * [LockKeyState.isLockKeyOnExcludingOffPress], or implement a different toggling behaviour.
+ */
+ protected open val KeyInputState.scrollLockOn: Boolean
+ get() = scrollLockState.isLockKeyOnIncludingOffPress
+
@OptIn(ExperimentalTestApi::class)
protected abstract fun MouseInputState.enqueueScroll(delta: Float, scrollWheel: ScrollWheel)
@@ -778,6 +802,56 @@
}
/**
+ * Toggling states for lock keys.
+ *
+ * Note that lock keys may not be toggled in the same way across all platforms.
+ *
+ * Take caps lock as an example; consistently, all platforms turn caps lock on upon the first
+ * key down event, and it stays on after the subsequent key up. However, on some platforms caps
+ * lock will turn off immediately upon the next key down event (MacOS for example), whereas
+ * other platforms (e.g. Linux, Android) wait for the next key up event before turning caps
+ * lock off.
+ *
+ * This enum breaks the lock key state down into four possible options - depending upon the
+ * interpretation of these four states, Android-like or MacOS-like behaviour can both be achieved.
+ *
+ * To get Android-like behaviour, use [isLockKeyOnIncludingOffPress],
+ * whereas for MacOS-style behaviour, use [isLockKeyOnExcludingOffPress].
+ */
+internal enum class LockKeyState(val state: Int) {
+ UP_AND_OFF(0),
+ DOWN_AND_ON(1),
+ UP_AND_ON(2),
+ DOWN_AND_OPTIONAL(3);
+
+ /**
+ * Whether or not the lock key is on. The lock key is considered on from the start of the
+ * "on press" until the end of the "off press", i.e. from the first key down event to the
+ * second key up event of the corresponding lock key.
+ */
+ val isLockKeyOnIncludingOffPress get() = state > 0
+
+ /**
+ * Whether or not the lock key is on. The lock key is considered on from the start of the
+ * "on press" until the start of the "off press", i.e. from the first key down event to the
+ * second key down event of the corresponding lock key.
+ */
+ val isLockKeyOnExcludingOffPress get() = this == DOWN_AND_ON || this == UP_AND_ON
+
+ /**
+ * Returns the next state in the cycle of lock key states.
+ */
+ fun next(): LockKeyState {
+ return when (this) {
+ UP_AND_OFF -> DOWN_AND_ON
+ DOWN_AND_ON -> UP_AND_ON
+ UP_AND_ON -> DOWN_AND_OPTIONAL
+ DOWN_AND_OPTIONAL -> UP_AND_OFF
+ }
+ }
+}
+
+/**
* The current key input state. Contains the keys that are pressed, the down time of the
* keyboard (which is the time of the last key down event), the state of the lock keys and
* the device ID.
@@ -789,9 +863,9 @@
var repeatKey: Key? = null
var repeatCount = 0
var lastRepeatTime = downTime
- var capsLockOn = false
- var numLockOn = false
- var scrollLockOn = false
+ var capsLockState: LockKeyState = LockKeyState.UP_AND_OFF
+ var numLockState: LockKeyState = LockKeyState.UP_AND_OFF
+ var scrollLockState: LockKeyState = LockKeyState.UP_AND_OFF
fun isKeyDown(key: Key): Boolean = downKeys.contains(key)
@@ -801,6 +875,7 @@
repeatKey = null
repeatCount = 0
}
+ updateLockKeys(key)
}
fun setKeyDown(key: Key) {
@@ -812,23 +887,12 @@
/**
* Updates lock key state values.
- *
- * Note that lock keys may not be toggled in the same way across all platforms.
- *
- * Take caps lock as an example; consistently, all platforms turn caps lock on upon the first
- * key down event, and it stays on after the subsequent key up. However, on some platforms caps
- * lock will turn off immediately upon the next key down event (MacOS for example), whereas
- * other platforms (e.g. linux) wait for the next key up event before turning caps lock off.
- *
- * By calling this function whenever a lock key is pressed down, MacOS-like behaviour is
- * achieved.
*/
- // TODO(Onadim): Investigate how lock key toggling is handled in Android, ChromeOS and Windows.
private fun updateLockKeys(key: Key) {
when (key) {
- Key.CapsLock -> capsLockOn = !capsLockOn
- Key.NumLock -> numLockOn = !numLockOn
- Key.ScrollLock -> scrollLockOn = !scrollLockOn
+ Key.CapsLock -> capsLockState = capsLockState.next()
+ Key.NumLock -> numLockState = numLockState.next()
+ Key.ScrollLock -> scrollLockState = scrollLockState.next()
}
}
}
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInjectionScope.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInjectionScope.kt
index d436a6d..02efdb9 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInjectionScope.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/KeyInjectionScope.kt
@@ -161,8 +161,11 @@
*/
fun KeyInjectionScope.withKeyDown(key: Key, block: KeyInjectionScope.() -> Unit) {
keyDown(key)
- block.invoke(this)
- keyUp(key)
+ try {
+ block.invoke(this)
+ } finally {
+ keyUp(key)
+ }
}
/**
@@ -178,8 +181,11 @@
// TODO(b/234011835): Refactor this and all functions that take List<Keys> to use vararg instead.
fun KeyInjectionScope.withKeysDown(keys: List<Key>, block: KeyInjectionScope.() -> Unit) {
keys.forEach { keyDown(it) }
- block.invoke(this)
- keys.forEach { keyUp(it) }
+ try {
+ block.invoke(this)
+ } finally {
+ keys.forEach { keyUp(it) }
+ }
}
/**
@@ -194,8 +200,11 @@
*/
fun KeyInjectionScope.withKeyToggled(key: Key, block: KeyInjectionScope.() -> Unit) {
pressKey(key)
- block.invoke(this)
- pressKey(key)
+ try {
+ block.invoke(this)
+ } finally {
+ pressKey(key)
+ }
}
/**
@@ -210,8 +219,11 @@
*/
fun KeyInjectionScope.withKeysToggled(keys: List<Key>, block: KeyInjectionScope.() -> Unit) {
pressKeys(keys)
- block.invoke(this)
- pressKeys(keys)
+ try {
+ block.invoke(this)
+ } finally {
+ pressKeys(keys)
+ }
}
/**
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TouchInjectionScope.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TouchInjectionScope.kt
index d814f96..5992018 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TouchInjectionScope.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TouchInjectionScope.kt
@@ -234,15 +234,18 @@
/**
* Sends a move event [delayMillis] after the last sent event without updating any of the
- * pointer positions.
+ * pointer positions, while adding the [historicalCoordinates] at the [relativeHistoricalTimes]
+ * to the move event. This corresponds to the scenario where a touch screen generates touch
+ * events quicker than can be dispatched and batches them together.
*
- * This overload supports gestures with multiple pointers.
+ * @sample androidx.compose.ui.test.samples.touchInputMultiTouchWithHistory
*
* @param relativeHistoricalTimes Time of each historical event, as a millisecond relative to
* the time the actual event is sent. For example, -10L means 10ms earlier.
* @param historicalCoordinates Coordinates of each historical event, in the same coordinate
* space as [moveTo]. The outer list must have the same size as the number of pointers in the
- * event, and each inner list must have the same size as [relativeHistoricalTimes].
+ * event, and each inner list must have the same size as [relativeHistoricalTimes]. The `i`th
+ * pointer is assigned the `i`th history, with the pointers sorted on ascending pointerId.
* @param delayMillis The time between the last sent event and this event.
* [eventPeriodMillis] by default.
*/
@@ -255,7 +258,9 @@
/**
* Sends a move event [delayMillis] after the last sent event without updating any of the
- * pointer positions.
+ * pointer positions, while adding the [historicalCoordinates] at the [relativeHistoricalTimes]
+ * to the move event. This corresponds to the scenario where a touch screen generates touch
+ * events quicker than can be dispatched and batches them together.
*
* This overload is a convenience method for the common case where the gesture only has one
* pointer.
@@ -464,7 +469,7 @@
*/
fun TouchInjectionScope.swipe(
curve: (Long) -> Offset,
- durationMillis: Long,
+ durationMillis: Long = 200,
keyTimes: List<Long> = emptyList()
) {
@OptIn(ExperimentalTestApi::class)
@@ -491,7 +496,7 @@
@ExperimentalTestApi
fun TouchInjectionScope.multiTouchSwipe(
curves: List<(Long) -> Offset>,
- durationMillis: Long,
+ durationMillis: Long = 200,
keyTimes: List<Long> = emptyList()
) {
val startTime = 0L
diff --git a/compose/ui/ui-text-google-fonts/build.gradle b/compose/ui/ui-text-google-fonts/build.gradle
index cda3047..e932fe0 100644
--- a/compose/ui/ui-text-google-fonts/build.gradle
+++ b/compose/ui/ui-text-google-fonts/build.gradle
@@ -48,7 +48,7 @@
androidx {
name = "Compose Google Fonts integration"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2022"
description = "Compose Downloadable Fonts integration for Google Fonts"
metalavaK2UastEnabled = true
diff --git a/compose/ui/ui-text-google-fonts/lint-baseline.xml b/compose/ui/ui-text-google-fonts/lint-baseline.xml
deleted file mode 100644
index 0ac1c03..0000000
--- a/compose/ui/ui-text-google-fonts/lint-baseline.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.2.0-alpha15" type="baseline" client="gradle" dependencies="false" name="AGP (8.2.0-alpha15)" variant="all" version="8.2.0-alpha15">
-
- <issue
- id="RestrictedApiAndroidX"
- message="FontsContractCompat.requestFont can only be called from within the same library (androidx.core:core)"
- errorLine1=" FontsContractCompat.requestFont("
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/compose/ui/text/googlefonts/GoogleFont.kt"/>
- </issue>
-
-</issues>
diff --git a/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/GoogleFont.kt b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/GoogleFont.kt
index 97ee309..c6a24ab 100644
--- a/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/GoogleFont.kt
+++ b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/GoogleFont.kt
@@ -21,6 +21,7 @@
import android.content.Context
import android.graphics.Typeface
+import android.os.Build
import android.os.Handler
import android.os.Looper
import androidx.annotation.ArrayRes
@@ -291,8 +292,7 @@
return suspendCancellableCoroutine { continuation ->
val callback = object : FontRequestCallback() {
override fun onTypefaceRetrieved(typeface: Typeface?) {
- // this is entered from any thread
- continuation.resume(typeface)
+ continuation.resume(typeface.recreateWithStyle(typefaceStyle))
}
override fun onTypefaceRequestFailed(reason: Int) {
@@ -321,6 +321,23 @@
}
/**
+ * Basically Typeface.create(this, typefaceStyle).
+ *
+ * Can be called from any thread.
+ */
+private fun Typeface?.recreateWithStyle(typefaceStyle: Int): Typeface {
+ return if (Build.VERSION.SDK_INT >= 28) {
+ // typeface create is thread safe
+ return Typeface.create(this, typefaceStyle)
+ } else {
+ // typeface.create has a timing condition
+ synchronized(GoogleFontTypefaceLoader) {
+ Typeface.create(this, typefaceStyle)
+ }
+ }
+}
+
+/**
* To allow mocking for tests
*/
internal interface FontsContractCompatLoader {
@@ -347,11 +364,8 @@
FontsContractCompat.requestFont(
context,
fontRequest,
- typefaceStyle,
- false, /* isBlockingFetch*/
- 0, /* timeout - not used when isBlockingFetch=false */
- handler,
- callback
+ callback,
+ handler
)
}
}
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index 1f1425a..76206b2 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -170,7 +170,7 @@
}
public fun interface LinkInteractionListener {
- method public void onClicked(androidx.compose.ui.text.LinkAnnotation link);
+ method public void onClick(androidx.compose.ui.text.LinkAnnotation link);
}
public final class MultiParagraph {
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index 01a1f6f..2156b8f 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -170,7 +170,7 @@
}
public fun interface LinkInteractionListener {
- method public void onClicked(androidx.compose.ui.text.LinkAnnotation link);
+ method public void onClick(androidx.compose.ui.text.LinkAnnotation link);
}
public final class MultiParagraph {
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index f15b65d..a824148 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -153,7 +153,7 @@
androidx {
name = "Compose UI Text"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Compose Text primitives and utilities"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt
index 137833f..790beec 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt
@@ -76,6 +76,6 @@
private class ComposeClickableSpan(private val link: LinkAnnotation) : ClickableSpan() {
override fun onClick(widget: View) {
- link.linkInteractionListener?.onClicked(link)
+ link.linkInteractionListener?.onClick(link)
}
}
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt
index 86d1c54..3a1c75a 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt
@@ -52,7 +52,7 @@
/**
* An annotation that contains a [url] string. When clicking on the text to which this annotation
* is attached, the app will try to open the url using [androidx.compose.ui.platform.UriHandler].
- * However, if [linkInteractionListener] is provided, its [LinkInteractionListener.onClicked]
+ * However, if [linkInteractionListener] is provided, its [LinkInteractionListener.onClick]
* method will be called instead and so you need to then handle opening url manually (for
* example by calling [androidx.compose.ui.platform.UriHandler]).
*/
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkInteractionListener.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkInteractionListener.kt
index dde0f0e..dc1aa74 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkInteractionListener.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/LinkInteractionListener.kt
@@ -22,5 +22,5 @@
fun interface LinkInteractionListener {
/** Triggered when a user clicks on the [link] */
- fun onClicked(link: LinkAnnotation)
+ fun onClick(link: LinkAnnotation)
}
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/LruCache.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/LruCache.kt
index 7cbb111..bd3fb7f 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/LruCache.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/LruCache.kt
@@ -241,6 +241,8 @@
*
* @param evicted `true` if the entry is being removed to make space, `false`
* if the removal was caused by a [put] or [remove].
+ * @param key key of the entry that was evicted or removed.
+ * @param oldValue the original value of the entry that was evicted removed.
* @param newValue the new value for [key], if it exists. If non-null,
* this removal was caused by a [put]. Otherwise it was caused by
* an eviction or a [remove].
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
index 8cf9efd..b5f6bde 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
@@ -208,10 +208,10 @@
* @param textLayoutResult the text field's [TextLayoutResult]
* @param textFieldToRootTransform function that modifies a matrix to be a transformation matrix
* from local coordinates to the root composable coordinates
- * @param innerTextFieldBounds visible bounds of the text field in local coordinates, or an
- * empty rectangle if the text field is not visible
- * @param decorationBoxBounds visible bounds of the decoration box in local coordinates, or an
- * empty rectangle if the decoration box is not visible
+ * @param innerTextFieldBounds visible bounds of the text field in text layout coordinates, or
+ * an empty rectangle if the text field is not visible
+ * @param decorationBoxBounds visible bounds of the decoration box in text layout coordinates,
+ * or an empty rectangle if the decoration box is not visible
*/
fun updateTextLayoutResult(
textFieldValue: TextFieldValue,
diff --git a/compose/ui/ui-tooling-data/build.gradle b/compose/ui/ui-tooling-data/build.gradle
index f813317..dffb37e 100644
--- a/compose/ui/ui-tooling-data/build.gradle
+++ b/compose/ui/ui-tooling-data/build.gradle
@@ -123,7 +123,7 @@
androidx {
name = "Compose Tooling Data"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "Compose tooling library data. This library provides data about compose" +
" for different tooling purposes."
diff --git a/compose/ui/ui-tooling-preview/build.gradle b/compose/ui/ui-tooling-preview/build.gradle
index b600b85..3922e69 100644
--- a/compose/ui/ui-tooling-preview/build.gradle
+++ b/compose/ui/ui-tooling-preview/build.gradle
@@ -105,7 +105,7 @@
androidx {
name = "Compose UI Preview Tooling"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "Compose tooling library API. This library provides the API required to declare" +
" @Preview composables in user apps."
diff --git a/compose/ui/ui-tooling/build.gradle b/compose/ui/ui-tooling/build.gradle
index 98f9161..2307370 100644
--- a/compose/ui/ui-tooling/build.gradle
+++ b/compose/ui/ui-tooling/build.gradle
@@ -133,7 +133,7 @@
androidx {
name = "Compose Tooling"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Compose tooling library. This library exposes information to our tools for better IDE support."
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/ui/ui-unit/build.gradle b/compose/ui/ui-unit/build.gradle
index ba792904..32b4d80 100644
--- a/compose/ui/ui-unit/build.gradle
+++ b/compose/ui/ui-unit/build.gradle
@@ -110,7 +110,7 @@
androidx {
name = "Compose Unit"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose classes for simple units"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
index b63b301..5c5102f 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
@@ -257,27 +257,13 @@
* The horizontal aspect of the offset in [Dp]
*/
@Stable
- val x: Dp
- get() {
- // Explicitly compare against packed values to avoid auto-boxing of DpOffset.Unspecified
- checkPrecondition(this.packedValue != UnspecifiedPackedFloats) {
- "DpOffset is unspecified"
- }
- return unpackFloat1(packedValue).dp
- }
+ val x: Dp get() = unpackFloat1(packedValue).dp
/**
* The vertical aspect of the offset in [Dp]
*/
@Stable
- val y: Dp
- get() {
- // Explicitly compare against packed values to avoid auto-boxing of DpOffset.Unspecified
- checkPrecondition(this.packedValue != UnspecifiedPackedFloats) {
- "DpOffset is unspecified"
- }
- return unpackFloat2(packedValue).dp
- }
+ val y: Dp get() = unpackFloat2(packedValue).dp
/**
* Returns a copy of this [DpOffset] instance optionally overriding the
@@ -374,27 +360,13 @@
* The horizontal aspect of the Size in [Dp]
*/
@Stable
- val width: Dp
- get() {
- // Explicitly compare against packed values to avoid auto-boxing of DpSize.Unspecified
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "DpSize is unspecified"
- }
- return unpackFloat1(packedValue).dp
- }
+ val width: Dp get() = unpackFloat1(packedValue).dp
/**
* The vertical aspect of the Size in [Dp]
*/
@Stable
- val height: Dp
- get() {
- // Explicitly compare against packed values to avoid auto-boxing of DpSize.Unspecified
- checkPrecondition(packedValue != UnspecifiedPackedFloats) {
- "DpSize is unspecified"
- }
- return unpackFloat2(packedValue).dp
- }
+ val height: Dp get() = unpackFloat2(packedValue).dp
/**
* Returns a copy of this [DpSize] instance optionally overriding the
diff --git a/compose/ui/ui-util/build.gradle b/compose/ui/ui-util/build.gradle
index 1639263..082d2e32 100644
--- a/compose/ui/ui-util/build.gradle
+++ b/compose/ui/ui-util/build.gradle
@@ -95,7 +95,7 @@
androidx {
name = "Compose Util"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Internal Compose utilities used by other modules"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MathHelpers.kt b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MathHelpers.kt
index 21d84a2..c0e5ba0 100644
--- a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MathHelpers.kt
+++ b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/MathHelpers.kt
@@ -41,7 +41,7 @@
/**
* Returns the smaller of the given values. If any value is NaN, returns NaN.
* Preferred over `kotlin.comparisons.minfOf()` for 4 arguments as it avoids
- * allocaing an array because of the varargs.
+ * allocating an array because of the varargs.
*/
@Suppress("NOTHING_TO_INLINE")
inline fun fastMinOf(a: Float, b: Float, c: Float, d: Float): Float {
@@ -52,7 +52,7 @@
/**
* Returns the largest of the given values. If any value is NaN, returns NaN.
* Preferred over `kotlin.comparisons.maxOf()` for 4 arguments as it avoids
- * allocaing an array because of the varargs.
+ * allocating an array because of the varargs.
*/
@Suppress("NOTHING_TO_INLINE")
inline fun fastMaxOf(a: Float, b: Float, c: Float, d: Float): Float {
diff --git a/compose/ui/ui-viewbinding/build.gradle b/compose/ui/ui-viewbinding/build.gradle
index 550edf7..5e60265 100644
--- a/compose/ui/ui-viewbinding/build.gradle
+++ b/compose/ui/ui-viewbinding/build.gradle
@@ -50,7 +50,7 @@
androidx {
name = "Compose ViewBinding"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose integration with ViewBinding"
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index d44f325..02a8692 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2176,24 +2176,24 @@
@kotlin.jvm.JvmDefaultWithCompatibility public interface LayoutCoordinates {
method public operator int get(androidx.compose.ui.layout.AlignmentLine alignmentLine);
+ method public default boolean getIntroducesFrameOfReference();
method public androidx.compose.ui.layout.LayoutCoordinates? getParentCoordinates();
method public androidx.compose.ui.layout.LayoutCoordinates? getParentLayoutCoordinates();
method public java.util.Set<androidx.compose.ui.layout.AlignmentLine> getProvidedAlignmentLines();
method public long getSize();
method public boolean isAttached();
- method public default boolean isPositionedByParentWithDirectManipulation();
method public androidx.compose.ui.geometry.Rect localBoundingBoxOf(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, optional boolean clipBounds);
method public long localPositionOf(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, long relativeToSource);
- method public default long localPositionOf(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, long relativeToSource, boolean excludeDirectManipulationOffset);
method public long localToRoot(long relativeToLocal);
method public default long localToScreen(long relativeToLocal);
method public long localToWindow(long relativeToLocal);
+ method public default long positionInLocalFrameOfReference(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, long relativeToSource);
method public default long screenToLocal(long relativeToScreen);
method public default void transformFrom(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, float[] matrix);
method public default void transformToScreen(float[] matrix);
method public long windowToLocal(long relativeToWindow);
+ property public default boolean introducesFrameOfReference;
property public abstract boolean isAttached;
- property public default boolean isPositionedByParentWithDirectManipulation;
property public abstract androidx.compose.ui.layout.LayoutCoordinates? parentCoordinates;
property public abstract androidx.compose.ui.layout.LayoutCoordinates? parentLayoutCoordinates;
property public abstract java.util.Set<androidx.compose.ui.layout.AlignmentLine> providedAlignmentLines;
@@ -2268,8 +2268,9 @@
public interface LookaheadScope {
method public androidx.compose.ui.layout.LayoutCoordinates getLookaheadScopeCoordinates(androidx.compose.ui.layout.Placeable.PlacementScope);
- method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates coordinates, optional boolean excludeDirectManipulationOffset);
- method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
+ method public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, optional long relativeToSource);
+ method public default long positionInLocalLookaheadFrameOfReference(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, optional long relativeToSource);
+ method public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
}
public final class LookaheadScopeKt {
@@ -2418,7 +2419,7 @@
method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, long position, androidx.compose.ui.graphics.layer.GraphicsLayer layer, optional float zIndex);
method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, long position, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
- method public final void withDirectManipulationPlacement(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Placeable.PlacementScope,kotlin.Unit> block);
+ method public final void withCurrentFrameOfReferencePlacement(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Placeable.PlacementScope,kotlin.Unit> block);
property public androidx.compose.ui.layout.LayoutCoordinates? coordinates;
property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
property protected abstract int parentWidth;
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index d152116..b324f0e 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2176,24 +2176,24 @@
@kotlin.jvm.JvmDefaultWithCompatibility public interface LayoutCoordinates {
method public operator int get(androidx.compose.ui.layout.AlignmentLine alignmentLine);
+ method public default boolean getIntroducesFrameOfReference();
method public androidx.compose.ui.layout.LayoutCoordinates? getParentCoordinates();
method public androidx.compose.ui.layout.LayoutCoordinates? getParentLayoutCoordinates();
method public java.util.Set<androidx.compose.ui.layout.AlignmentLine> getProvidedAlignmentLines();
method public long getSize();
method public boolean isAttached();
- method public default boolean isPositionedByParentWithDirectManipulation();
method public androidx.compose.ui.geometry.Rect localBoundingBoxOf(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, optional boolean clipBounds);
method public long localPositionOf(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, long relativeToSource);
- method public default long localPositionOf(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, long relativeToSource, boolean excludeDirectManipulationOffset);
method public long localToRoot(long relativeToLocal);
method public default long localToScreen(long relativeToLocal);
method public long localToWindow(long relativeToLocal);
+ method public default long positionInLocalFrameOfReference(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, long relativeToSource);
method public default long screenToLocal(long relativeToScreen);
method public default void transformFrom(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, float[] matrix);
method public default void transformToScreen(float[] matrix);
method public long windowToLocal(long relativeToWindow);
+ property public default boolean introducesFrameOfReference;
property public abstract boolean isAttached;
- property public default boolean isPositionedByParentWithDirectManipulation;
property public abstract androidx.compose.ui.layout.LayoutCoordinates? parentCoordinates;
property public abstract androidx.compose.ui.layout.LayoutCoordinates? parentLayoutCoordinates;
property public abstract java.util.Set<androidx.compose.ui.layout.AlignmentLine> providedAlignmentLines;
@@ -2271,8 +2271,9 @@
public interface LookaheadScope {
method public androidx.compose.ui.layout.LayoutCoordinates getLookaheadScopeCoordinates(androidx.compose.ui.layout.Placeable.PlacementScope);
- method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates coordinates, optional boolean excludeDirectManipulationOffset);
- method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
+ method public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, optional long relativeToSource);
+ method public default long positionInLocalLookaheadFrameOfReference(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, optional long relativeToSource);
+ method public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
}
public final class LookaheadScopeKt {
@@ -2425,7 +2426,7 @@
method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, long position, androidx.compose.ui.graphics.layer.GraphicsLayer layer, optional float zIndex);
method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, long position, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
- method public final void withDirectManipulationPlacement(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Placeable.PlacementScope,kotlin.Unit> block);
+ method public final void withCurrentFrameOfReferencePlacement(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Placeable.PlacementScope,kotlin.Unit> block);
property public androidx.compose.ui.layout.LayoutCoordinates? coordinates;
property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
property protected abstract int parentWidth;
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index a866f51..6af789d 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -226,7 +226,7 @@
androidx {
name = "Compose UI"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Compose UI primitives. This library contains the primitives that form the Compose UI Toolkit, such as drawing, measurement and layout."
legacyDisableKotlinStrictApiMode = true
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index 170a06e..d8ea6c7 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -17,6 +17,7 @@
package androidx.compose.ui
import android.content.Context.ACCESSIBILITY_SERVICE
+import android.content.res.Resources
import android.graphics.Rect
import android.graphics.RectF
import android.os.Build
@@ -61,6 +62,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
@@ -246,6 +248,7 @@
private lateinit var container: OpenComposeView
private lateinit var delegate: AndroidComposeViewAccessibilityDelegateCompat
private lateinit var provider: AccessibilityNodeProviderCompat
+ private lateinit var resources: Resources
private val accessibilityManager: AccessibilityManager
get() = androidComposeView.context
@@ -258,6 +261,7 @@
fun setup() {
// Use uiAutomation to enable accessibility manager.
InstrumentationRegistry.getInstrumentation().uiAutomation
+ resources = InstrumentationRegistry.getInstrumentation().context.resources
rule.activityRule.scenario.onActivity { activity ->
container = spy(OpenComposeView(activity)) {
@@ -678,6 +682,110 @@
}
@Test
+ fun emptyTextField_hasStateDescription() {
+ setContent {
+ BasicTextField(
+ rememberTextFieldState(),
+ modifier = Modifier.testTag(tag)
+ )
+ }
+
+ val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
+
+ rule.runOnIdle {
+ with(info) {
+ assertThat(stateDescription).isEqualTo(resources.getString(R.string.state_empty))
+ }
+ }
+ }
+
+ @Test
+ fun emptyTextField_noSpeakableChild_hasStateDescription() {
+ setContent {
+ BasicTextField(
+ "",
+ {},
+ modifier = Modifier.testTag(tag)
+ ) {
+ Column {
+ it()
+ Button(onClick = {}) {}
+ Box(
+ Modifier
+ .size(10.dp)
+ .semantics { testTag = "unspeakable child" })
+ }
+ }
+ }
+
+ val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
+
+ rule.runOnIdle {
+ with(info) {
+ assertThat(stateDescription).isEqualTo(resources.getString(R.string.state_empty))
+ }
+ }
+ }
+
+ @Test
+ fun emptyTextField_hasSpeakableChild_noStateDescription_() {
+ setContent {
+ BasicTextField(
+ rememberTextFieldState(),
+ modifier = Modifier.testTag(tag),
+ decorator = {
+ Row {
+ it()
+ BasicText(text = "Label")
+ }
+ }
+ )
+ }
+
+ val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
+
+ rule.runOnIdle {
+ with(info) {
+ assertThat(stateDescription).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun emptyTextField_hasSpeakableIndirectChild_noStateDescription_() {
+ setContent {
+ BasicTextField(
+ rememberTextFieldState(),
+ modifier = Modifier.testTag(tag),
+ decorator = {
+ Row {
+ it()
+ Box(modifier = Modifier
+ .wrapContentSize()
+ .semantics {
+ testTag = "box test tag"
+ }) {
+ BasicText(text = "Label")
+ }
+ }
+ }
+ )
+ }
+
+ val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
+
+ rule.runOnIdle {
+ with(info) {
+ assertThat(stateDescription).isNull()
+ }
+ }
+ }
+
+ @Test
fun testCreateAccessibilityNodeInfo_forText() {
// Arrange.
val text = "Test"
@@ -2465,7 +2573,10 @@
setContent {
Row {
// Initially focused item.
- Box(Modifier.size(10.dp).focusable())
+ Box(
+ Modifier
+ .size(10.dp)
+ .focusable())
Box(
Modifier
.testTag(tag)
@@ -2621,7 +2732,10 @@
setContent {
Row {
// Initially focused item.
- Box(Modifier.size(10.dp).focusable())
+ Box(
+ Modifier
+ .size(10.dp)
+ .focusable())
BasicTextField(
modifier = Modifier.testTag(tag),
value = "value",
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index 2ca13db..ad00359 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -396,7 +396,9 @@
val scope = ReusableGraphicsLayerScope()
scope.cameraDistance = cameraDistance
scope.compositingStrategy = compositingStrategy
- updateLayerProperties(scope, LayoutDirection.Ltr, Density(1f))
+ scope.layoutDirection = LayoutDirection.Ltr
+ scope.graphicsDensity = Density(1f)
+ updateLayerProperties(scope)
}
return expectedLayerType == view.layerType &&
expectedOverlappingRendering == view.hasOverlappingRendering()
@@ -436,7 +438,9 @@
).apply {
val scope = ReusableGraphicsLayerScope()
scope.cameraDistance = cameraDistance
- updateLayerProperties(scope, LayoutDirection.Ltr, Density(1f))
+ scope.layoutDirection = LayoutDirection.Ltr
+ scope.graphicsDensity = Density(1f)
+ updateLayerProperties(scope)
}
// Verify that the camera distance is applied properly even after accounting for
// the internal dp conversion within View
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/draw/ClipDrawTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/draw/ClipDrawTest.kt
index 6bc2204..490bb83 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/draw/ClipDrawTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/draw/ClipDrawTest.kt
@@ -47,6 +47,7 @@
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathOperation
import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.addOutline
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.toArgb
@@ -471,6 +472,126 @@
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@Test
+ fun switchBetweenDifferentOutlines_differentPath_observableShape() {
+ var invertedTriangle by mutableStateOf(false)
+ val observableShape = object : Shape {
+ override fun createOutline(
+ size: Size,
+ layoutDirection: LayoutDirection,
+ density: Density
+ ): Outline {
+ return if (invertedTriangle) {
+ invertedTriangleShape.createOutline(size, layoutDirection, density)
+ } else {
+ triangleShape.createOutline(size, layoutDirection, density)
+ }
+ }
+ }
+ // to be replaced with a DrawModifier wrapped into remember, so the recomposition
+ // is not causing invalidation as the DrawModifier didn't change
+ val drawCallback: DrawScope.() -> Unit = {
+ drawRect(
+ Color.Cyan,
+ topLeft = Offset(-100f, -100f),
+ size = Size(size.width + 200f, size.height + 200f)
+ )
+ }
+
+ val clip = Modifier.graphicsLayer {
+ shape = observableShape
+ clip = true
+ drawLatch.countDown()
+ }
+
+ rule.runOnUiThreadIR {
+ activity.setContent {
+ AtLeastSize(
+ size = 30,
+ modifier = Modifier
+ .background(Color.Green)
+ .then(clip)
+ .drawBehind(drawCallback)
+ ) {
+ }
+ }
+ }
+
+ takeScreenShot(30).apply {
+ assertTriangle(Color.Cyan, Color.Green)
+ }
+
+ drawLatch = CountDownLatch(1)
+ rule.runOnUiThreadIR { invertedTriangle = true }
+
+ takeScreenShot(30).apply {
+ assertInvertedTriangle(Color.Cyan, Color.Green)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun switchBetweenDifferentOutlines_samePath_observableShape() {
+ var invertedTriangle by mutableStateOf(false)
+ val path = Path()
+ val observableShape = object : Shape {
+ override fun createOutline(
+ size: Size,
+ layoutDirection: LayoutDirection,
+ density: Density
+ ): Outline {
+ val outline = if (invertedTriangle) {
+ invertedTriangleShape.createOutline(size, layoutDirection, density)
+ } else {
+ triangleShape.createOutline(size, layoutDirection, density)
+ }
+ path.reset()
+ path.addOutline(outline)
+ return Outline.Generic(path)
+ }
+ }
+ // to be replaced with a DrawModifier wrapped into remember, so the recomposition
+ // is not causing invalidation as the DrawModifier didn't change
+ val drawCallback: DrawScope.() -> Unit = {
+ drawRect(
+ Color.Cyan,
+ topLeft = Offset(-100f, -100f),
+ size = Size(size.width + 200f, size.height + 200f)
+ )
+ }
+
+ val clip = Modifier.graphicsLayer {
+ shape = observableShape
+ clip = true
+ drawLatch.countDown()
+ }
+
+ rule.runOnUiThreadIR {
+ activity.setContent {
+ AtLeastSize(
+ size = 30,
+ modifier = Modifier
+ .background(Color.Green)
+ .then(clip)
+ .drawBehind(drawCallback)
+ ) {
+ }
+ }
+ }
+
+ takeScreenShot(30).apply {
+ assertTriangle(Color.Cyan, Color.Green)
+ }
+
+ drawLatch = CountDownLatch(1)
+ rule.runOnUiThreadIR { invertedTriangle = true }
+
+ takeScreenShot(30).apply {
+ assertInvertedTriangle(Color.Cyan, Color.Green)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
fun emitClipLater() {
val model = mutableStateOf(false)
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
index d1b40df..7af8753 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
@@ -252,236 +252,6 @@
assertThat(areEqual(hitPathTracker.root, expectedRoot)).isTrue()
}
- // Inserts a new Node at the top of an existing branch (tests removal of duplicate Nodes too).
- @Test
- fun addHitPath_dynamicNodeAddedTopPartiallyMatchingTreeWithOnePointerId_correctResult() {
- val pif1 = PointerInputNodeMock()
- val pif2 = PointerInputNodeMock()
- val pif3 = PointerInputNodeMock()
- val pif4 = PointerInputNodeMock()
- val pifNew1 = PointerInputNodeMock()
-
- val pointerId1 = PointerId(1)
- hitPathTracker.addHitPath(pointerId1, listOf(pif1, pif2, pif3, pif4))
- hitPathTracker.addHitPath(pointerId1, listOf(pifNew1, pif1, pif2, pif3, pif4))
-
- val expectedRoot = NodeParent().apply {
- children.add(
- Node(pifNew1).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif1).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif2).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif3).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif4).apply {
- pointerIds.add(pointerId1)
- }
- )
- }
- )
- }
- )
- }
- )
- }
- )
- }
- assertThat(areEqual(hitPathTracker.root, expectedRoot)).isTrue()
- }
-
- @Test
- fun addHitPath_dynamicNodeAddedTopPartiallyMatchingTreeWithTwoPointerIds_correctResult() {
- val pif1 = PointerInputNodeMock()
- val pif2 = PointerInputNodeMock()
- val pif3 = PointerInputNodeMock()
- val pif4 = PointerInputNodeMock()
- val pif5 = PointerInputNodeMock()
- val pif6 = PointerInputNodeMock()
- val pif7 = PointerInputNodeMock()
- val pif8 = PointerInputNodeMock()
-
- val pifNew1 = PointerInputNodeMock()
-
- val pointerId1 = PointerId(1)
- val pointerId2 = PointerId(2)
-
- hitPathTracker.addHitPath(pointerId1, listOf(pif1, pif2, pif3, pif4))
- hitPathTracker.addHitPath(pointerId2, listOf(pif5, pif6, pif7, pif8))
-
- hitPathTracker.addHitPath(pointerId2, listOf(pifNew1, pif5, pif6, pif7, pif8))
-
- val expectedRoot = NodeParent().apply {
- children.add(
- Node(pif1).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif2).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif3).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif4).apply {
- pointerIds.add(pointerId1)
- }
- )
- }
- )
- }
- )
- }
- )
-
- children.add(
- Node(pifNew1).apply {
- pointerIds.add(pointerId2)
- children.add(
- Node(pif5).apply {
- pointerIds.add(pointerId2)
- children.add(
- Node(pif6).apply {
- pointerIds.add(pointerId2)
- children.add(
- Node(pif7).apply {
- pointerIds.add(pointerId2)
- children.add(
- Node(pif8).apply {
- pointerIds.add(pointerId2)
- }
- )
- }
- )
- }
- )
- }
- )
- }
- )
- }
- assertThat(areEqual(hitPathTracker.root, expectedRoot)).isTrue()
- }
-
- // Inserts a new Node inside an existing branch (tests removal of duplicate Nodes too).
- @Test
- fun addHitPath_dynamicNodeAddedInsidePartiallyMatchingTreeWithOnePointerId_correctResult() {
- val pif1 = PointerInputNodeMock()
- val pif2 = PointerInputNodeMock()
- val pif3 = PointerInputNodeMock()
- val pif4 = PointerInputNodeMock()
- val pifNew1 = PointerInputNodeMock()
-
- val pointerId1 = PointerId(1)
- hitPathTracker.addHitPath(pointerId1, listOf(pif1, pif2, pif3, pif4))
- hitPathTracker.addHitPath(pointerId1, listOf(pif1, pifNew1, pif2, pif3, pif4))
-
- val expectedRoot = NodeParent().apply {
- children.add(
- Node(pif1).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pifNew1).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif2).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif3).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif4).apply {
- pointerIds.add(pointerId1)
- }
- )
- }
- )
- }
- )
- }
- )
- }
- )
- }
- assertThat(areEqual(hitPathTracker.root, expectedRoot)).isTrue()
- }
-
- @Test
- fun addHitPath_dynamicNodeAddedInsidePartiallyMatchingTreeWithTwoPointerIds_correctResult() {
- val pif1 = PointerInputNodeMock()
- val pif2 = PointerInputNodeMock()
- val pif3 = PointerInputNodeMock()
- val pif4 = PointerInputNodeMock()
- val pif5 = PointerInputNodeMock()
- val pif6 = PointerInputNodeMock()
- val pif7 = PointerInputNodeMock()
- val pif8 = PointerInputNodeMock()
-
- val pifNew1 = PointerInputNodeMock()
-
- val pointerId1 = PointerId(1)
- val pointerId2 = PointerId(2)
-
- hitPathTracker.addHitPath(pointerId1, listOf(pif1, pif2, pif3, pif4))
- hitPathTracker.addHitPath(pointerId2, listOf(pif5, pif6, pif7, pif8))
-
- hitPathTracker.addHitPath(pointerId2, listOf(pif5, pif6, pifNew1, pif7, pif8))
-
- val expectedRoot = NodeParent().apply {
- children.add(
- Node(pif1).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif2).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif3).apply {
- pointerIds.add(pointerId1)
- children.add(
- Node(pif4).apply {
- pointerIds.add(pointerId1)
- }
- )
- }
- )
- }
- )
- }
- )
-
- children.add(
- Node(pif5).apply {
- pointerIds.add(pointerId2)
- children.add(
- Node(pif6).apply {
- pointerIds.add(pointerId2)
- children.add(
- Node(pifNew1).apply {
- pointerIds.add(pointerId2)
- children.add(
- Node(pif7).apply {
- pointerIds.add(pointerId2)
- children.add(
- Node(pif8).apply {
- pointerIds.add(pointerId2)
- }
- )
- }
- )
- }
- )
- }
- )
- }
- )
- }
- assertThat(areEqual(hitPathTracker.root, expectedRoot)).isTrue()
- }
-
// Inserts a Node in the bottom of an existing branch (tests removal of duplicate Nodes too).
@Test
fun addHitPath_dynamicNodeAddedBelowPartiallyMatchingTreeWithOnePointerId_correctResult() {
@@ -492,8 +262,16 @@
val pifNew1 = PointerInputNodeMock()
val pointerId1 = PointerId(1)
+ // Modifier.Node(s) hit by the first pointer input event
hitPathTracker.addHitPath(pointerId1, listOf(pif1, pif2, pif3, pif4))
+ // Clear any old hits from previous calls (does not really apply here since it's the first
+ // call)
+ hitPathTracker.removeDetachedPointerInputNodes()
+
+ // Modifier.Node(s) hit by the second pointer input event
hitPathTracker.addHitPath(pointerId1, listOf(pif1, pif2, pif3, pif4, pifNew1))
+ // Clear any old hits from previous calls
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -541,10 +319,18 @@
val pointerId1 = PointerId(1)
val pointerId2 = PointerId(2)
+ // Modifier.Node(s) hit by the first pointer input event
hitPathTracker.addHitPath(pointerId1, listOf(pif1, pif2, pif3, pif4))
hitPathTracker.addHitPath(pointerId2, listOf(pif5, pif6, pif7, pif8))
+ // Clear any old hits from previous calls (does not really apply here since it's the first
+ // call)
+ hitPathTracker.removeDetachedPointerInputNodes()
+ // Modifier.Node(s) hit by the second pointer input event
+ hitPathTracker.addHitPath(pointerId1, listOf(pif1, pif2, pif3, pif4))
hitPathTracker.addHitPath(pointerId2, listOf(pif5, pif6, pif7, pif8, pifNew1))
+ // Clear any old hits from previous calls
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -1335,7 +1121,7 @@
@Test
fun removeDetachedPointerInputFilters_noNodes_hitResultJustHasRootAndDoesNotCrash() {
val throwable = catchThrowable {
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
}
assertThat(throwable).isNull()
@@ -1373,7 +1159,7 @@
// Act.
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
// Assert.
@@ -1451,7 +1237,7 @@
hitPathTracker.addHitPath(PointerId(0), listOf(root, middle, leaf))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
assertThat(areEqual(hitPathTracker.root, NodeParent())).isTrue()
@@ -1478,7 +1264,7 @@
val pointerId = PointerId(0)
hitPathTracker.addHitPath(pointerId, listOf(root, middle, child))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -1511,7 +1297,7 @@
val pointerId = PointerId(0)
hitPathTracker.addHitPath(pointerId, listOf(root, middle, leaf))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -1570,7 +1356,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -1648,7 +1434,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -1727,7 +1513,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -1825,7 +1611,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -1897,7 +1683,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -1971,7 +1757,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -2070,7 +1856,7 @@
hitPathTracker.addHitPath(PointerId(5), listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(PointerId(7), listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent()
@@ -2135,7 +1921,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -2204,7 +1990,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -2294,7 +2080,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root2, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root3, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -2371,7 +2157,7 @@
hitPathTracker.addHitPath(PointerId(5), listOf(root, middle2, leaf2))
hitPathTracker.addHitPath(PointerId(7), listOf(root, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent()
@@ -2444,7 +2230,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -2524,7 +2310,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -2602,7 +2388,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root, middle2, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root, middle3, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -2654,7 +2440,7 @@
hitPathTracker.addHitPath(pointerId2, listOf(root, middle, leaf2))
hitPathTracker.addHitPath(pointerId3, listOf(root, middle, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -2721,7 +2507,7 @@
hitPathTracker.addHitPath(PointerId(5), listOf(root, middle, leaf2))
hitPathTracker.addHitPath(PointerId(7), listOf(root, middle, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -2787,7 +2573,7 @@
hitPathTracker.addHitPath(PointerId(5), listOf(root, middle, leaf2))
hitPathTracker.addHitPath(PointerId(7), listOf(root, middle, leaf3))
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
val expectedRoot = NodeParent().apply {
children.add(
@@ -4172,12 +3958,7 @@
explicitLayer: GraphicsLayer?
): OwnedLayer {
return object : OwnedLayer {
- override fun updateLayerProperties(
- scope: ReusableGraphicsLayerScope,
- layoutDirection: LayoutDirection,
- density: Density
- ) {
- }
+ override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {}
override fun isInLayer(position: Offset) = true
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/ApproachLayoutTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/ApproachLayoutTest.kt
index ba4c17c..0e60e34 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/ApproachLayoutTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/ApproachLayoutTest.kt
@@ -874,7 +874,7 @@
.run {
layout(width, height) {
if (toggleDmp) {
- withDirectManipulationPlacement {
+ withCurrentFrameOfReferencePlacement {
place(0, 200)
}
} else {
@@ -895,10 +895,9 @@
positionExcludingDmp = it
.parentLayoutCoordinates!!
.toLookaheadCoordinates()
- .localLookaheadPositionOf(
- coordinates = it
+ .positionInLocalLookaheadFrameOfReference(
+ sourceCoordinates = it
.toLookaheadCoordinates(),
- excludeDirectManipulationOffset = true
)
}
placeable.place(0, 0)
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt
index e8557cf..e0f3bf0 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt
@@ -570,12 +570,7 @@
internal open class MockLayer() : OwnedLayer {
- override fun updateLayerProperties(
- scope: ReusableGraphicsLayerScope,
- layoutDirection: LayoutDirection,
- density: Density
- ) {
- }
+ override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {}
override fun isInLayer(position: Offset) = true
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
index 45c414a..cc84701 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
@@ -2747,13 +2747,11 @@
val position = parent
.localLookaheadPositionOf(
- coordinates = coordinates!!,
- excludeDirectManipulationOffset = false
+ sourceCoordinates = coordinates!!,
)
val excludedPosition = parent
- .localLookaheadPositionOf(
- coordinates = coordinates!!,
- excludeDirectManipulationOffset = true
+ .positionInLocalLookaheadFrameOfReference(
+ sourceCoordinates = coordinates!!,
)
positionToExcludedArray[i] =
position to excludedPosition
@@ -2826,14 +2824,12 @@
val position = parent
.localLookaheadPositionOf(
- coordinates = coordinates!!,
- excludeDirectManipulationOffset = false
+ sourceCoordinates = coordinates!!,
)
val excludedPosition = parent
- .localLookaheadPositionOf(
- coordinates = coordinates!!,
- excludeDirectManipulationOffset = true
+ .positionInLocalLookaheadFrameOfReference(
+ sourceCoordinates = coordinates!!,
)
positionToExcludedArray[i] =
position to excludedPosition
@@ -2886,7 +2882,7 @@
val placeable = measurable.measure(constraints)
return layout(placeable.width, placeable.height) {
if (withDirectManipulation) {
- withDirectManipulationPlacement {
+ withCurrentFrameOfReferencePlacement {
placeable.place(0, offset.fastRoundToInt())
}
} else {
@@ -2921,15 +2917,13 @@
regularPosition =
parentLookaheadCoords
.localLookaheadPositionOf(
- coordinates = it,
- excludeDirectManipulationOffset = false
+ sourceCoordinates = it
)
excludedManipulationPosition =
parentLookaheadCoords
- .localLookaheadPositionOf(
- coordinates = it,
- excludeDirectManipulationOffset = true
+ .positionInLocalLookaheadFrameOfReference(
+ sourceCoordinates = it,
)
}
)
@@ -2979,7 +2973,7 @@
layout(300, 300) {
placeableData.fastForEach { (placeable, offsetData) ->
if (offsetData.withDirectManipulation) {
- withDirectManipulationPlacement {
+ withCurrentFrameOfReferencePlacement {
placeable.place(0, offsetData.offset.fastRoundToInt())
}
} else {
@@ -3009,15 +3003,13 @@
regularPositions[0] =
parentLookaheadCoords
.localLookaheadPositionOf(
- coordinates = it,
- excludeDirectManipulationOffset = false
+ sourceCoordinates = it
)
excludedManipulationPositions[0] =
parentLookaheadCoords
- .localLookaheadPositionOf(
- coordinates = it,
- excludeDirectManipulationOffset = true
+ .positionInLocalLookaheadFrameOfReference(
+ sourceCoordinates = it,
)
}
)
@@ -3033,15 +3025,13 @@
regularPositions[1] =
parentLookaheadCoords
.localLookaheadPositionOf(
- coordinates = it,
- excludeDirectManipulationOffset = false
+ sourceCoordinates = it
)
excludedManipulationPositions[1] =
parentLookaheadCoords
- .localLookaheadPositionOf(
- coordinates = it,
- excludeDirectManipulationOffset = true
+ .positionInLocalLookaheadFrameOfReference(
+ sourceCoordinates = it
)
}
)
@@ -3092,7 +3082,7 @@
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
if (placeWithDirectManipulation) {
- withDirectManipulationPlacement {
+ withCurrentFrameOfReferencePlacement {
placeable.place(0, 200)
}
} else {
@@ -3111,13 +3101,11 @@
lookingAheadPosition = lookaheadScopeCoordinates
.localLookaheadPositionOf(
- coordinates = lookaheadCoordinates,
- excludeDirectManipulationOffset = false
+ sourceCoordinates = lookaheadCoordinates,
)
lookingAheadPositionExcludingDmp = lookaheadScopeCoordinates
- .localLookaheadPositionOf(
- coordinates = lookaheadCoordinates,
- excludeDirectManipulationOffset = true
+ .positionInLocalLookaheadFrameOfReference(
+ sourceCoordinates = lookaheadCoordinates
)
}
placeable.place(0, 0)
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
index f80881e..0b55a41 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
@@ -32,6 +32,7 @@
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.AtLeastSize
@@ -1284,6 +1285,43 @@
assertThat(position).isEqualTo(Offset(1f, 1f))
}
}
+
+ @Test
+ fun removingOnPositionedCallbackDoesNotTriggerOtherCallbacks() {
+ val callbackPresent = mutableStateOf(true)
+
+ var positionCalled1Count = 0
+ var positionCalled2Count = 0
+ rule.setContent {
+ val modifier = if (callbackPresent.value) {
+ // Remember lambdas to avoid triggering a node update when the lambda changes
+ Modifier.onGloballyPositioned(remember { { positionCalled1Count++ } })
+ } else {
+ Modifier
+ }
+ Box(Modifier
+ // Remember lambdas to avoid triggering a node update when the lambda changes
+ .onGloballyPositioned(remember { { positionCalled2Count++ } })
+ .then(modifier)
+ .fillMaxSize()
+ )
+ }
+
+ rule.runOnIdle {
+ // Both callbacks should be called
+ assertThat(positionCalled1Count).isEqualTo(1)
+ assertThat(positionCalled2Count).isEqualTo(1)
+ }
+
+ // Remove the first node
+ rule.runOnIdle { callbackPresent.value = false }
+
+ rule.runOnIdle {
+ // Removing the node should not trigger any new callbacks
+ assertThat(positionCalled1Count).isEqualTo(1)
+ assertThat(positionCalled2Count).isEqualTo(1)
+ }
+ }
}
@Composable
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
index af67ee0..2f2a5c5 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnSizeChangedTest.kt
@@ -22,10 +22,12 @@
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.LayoutAwareModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.elementFor
import androidx.compose.ui.test.TestActivity
@@ -214,7 +216,7 @@
}
@Test
- @SmallTest
+ @MediumTest
fun addedModifier() {
val latch1 = CountDownLatch(1)
val latch2 = CountDownLatch(1)
@@ -225,15 +227,18 @@
rule.runOnUiThread {
activity.setContent {
with(LocalDensity.current) {
- val mod = if (addModifier) Modifier.onSizeChanged {
+ // Remember lambdas to avoid triggering a node update when the lambda changes
+ val mod = if (addModifier) Modifier.onSizeChanged(remember { {
changedSize2 = it
latch2.countDown()
- } else Modifier
+ } }) else Modifier
Box(
- Modifier.padding(10.toDp()).onSizeChanged {
+ // Remember lambdas to avoid triggering a node update when the lambda
+ // changes
+ Modifier.padding(10.toDp()).onSizeChanged(remember { {
changedSize1 = it
latch1.countDown()
- }.then(mod)
+ } }).then(mod)
) {
Box(Modifier.requiredSize(10.toDp()))
}
@@ -248,18 +253,20 @@
addModifier = true
- // We've added an onSizeChanged modifier, so it must trigger another size change
+ // We've added an onSizeChanged modifier, so it must trigger another size change.
+ // The existing modifier will also be called, but onSizeChanged only invokes the lambda if
+ // the size changes, so we won't see it.
assertTrue(latch2.await(1, TimeUnit.SECONDS))
assertEquals(10, changedSize2.height)
assertEquals(10, changedSize2.width)
}
@Test
- @SmallTest
+ @MediumTest
fun addedModifierNode() {
- val sizeLatch1 = CountDownLatch(1)
+ var sizeLatch1 = CountDownLatch(1)
val sizeLatch2 = CountDownLatch(1)
- val placedLatch1 = CountDownLatch(1)
+ var placedLatch1 = CountDownLatch(1)
val placedLatch2 = CountDownLatch(1)
var changedSize1 = IntSize.Zero
var changedSize2 = IntSize.Zero
@@ -304,11 +311,300 @@
assertEquals(10, changedSize1.height)
assertEquals(10, changedSize1.width)
+ sizeLatch1 = CountDownLatch(1)
+ placedLatch1 = CountDownLatch(1)
addModifier = true
- // We've added a node, so it must trigger onRemeasured and onPlaced on the new node
+ // We've added a node, so it must trigger onRemeasured and onPlaced on the new node, and
+ // the old node should see a relayout too
+ assertTrue(sizeLatch1.await(1, TimeUnit.SECONDS))
+ assertTrue(placedLatch1.await(1, TimeUnit.SECONDS))
assertTrue(sizeLatch2.await(1, TimeUnit.SECONDS))
assertTrue(placedLatch2.await(1, TimeUnit.SECONDS))
+ assertEquals(10, changedSize1.height)
+ assertEquals(10, changedSize1.width)
+ assertEquals(10, changedSize2.height)
+ assertEquals(10, changedSize2.width)
+ }
+
+ @Test
+ @MediumTest
+ fun removedModifier() {
+ var latch1 = CountDownLatch(1)
+ val latch2 = CountDownLatch(1)
+ var changedSize1 = IntSize.Zero
+ var changedSize2 = IntSize.Zero
+ var addModifier by mutableStateOf(true)
+
+ rule.runOnUiThread {
+ activity.setContent {
+ with(LocalDensity.current) {
+ // Remember lambdas to avoid triggering a node update when the lambda changes
+ val mod = if (addModifier) Modifier.onSizeChanged(remember { {
+ changedSize2 = it
+ latch2.countDown()
+ } }) else Modifier
+ Box(
+ // Remember lambdas to avoid triggering a node update when the lambda
+ // changes
+ Modifier.padding(10.toDp()).onSizeChanged(remember { {
+ changedSize1 = it
+ latch1.countDown()
+ } }).then(mod)
+ ) {
+ Box(Modifier.requiredSize(10.toDp()))
+ }
+ }
+ }
+ }
+
+ // Initial setting will call onSizeChanged
+ assertTrue(latch1.await(1, TimeUnit.SECONDS))
+ assertTrue(latch2.await(1, TimeUnit.SECONDS))
+ assertEquals(10, changedSize1.height)
+ assertEquals(10, changedSize1.width)
+ assertEquals(10, changedSize2.height)
+ assertEquals(10, changedSize2.width)
+
+ latch1 = CountDownLatch(1)
+ // Remove the modifier
+ addModifier = false
+
+ // We've removed a modifier, so the other modifier should not be informed since there was no
+ // layout change. (In any case onSizeChanged only invokes the lambda if the size changes,
+ // so this hopefully wouldn't fail anyway unless that caching behavior changes).
+ assertFalse(latch1.await(1, TimeUnit.SECONDS))
+ }
+
+ @Test
+ @MediumTest
+ fun removedModifierNode() {
+ var latch1 = CountDownLatch(2)
+ val latch2 = CountDownLatch(2)
+ var changedSize1 = IntSize.Zero
+ var changedSize2 = IntSize.Zero
+ var addModifier by mutableStateOf(true)
+
+ val node = object : LayoutAwareModifierNode, Modifier.Node() {
+ override fun onRemeasured(size: IntSize) {
+ changedSize1 = size
+ latch1.countDown()
+ }
+ override fun onPlaced(coordinates: LayoutCoordinates) {
+ latch1.countDown()
+ }
+ }
+
+ val node2 = object : LayoutAwareModifierNode, Modifier.Node() {
+ override fun onRemeasured(size: IntSize) {
+ changedSize2 = size
+ latch2.countDown()
+ }
+ override fun onPlaced(coordinates: LayoutCoordinates) {
+ latch2.countDown()
+ }
+ }
+
+ rule.runOnUiThread {
+ activity.setContent {
+ with(LocalDensity.current) {
+ val mod = if (addModifier) Modifier.elementFor(node2) else Modifier
+ Box(
+ Modifier.padding(10.toDp()).elementFor(node).then(mod)
+ ) {
+ Box(Modifier.requiredSize(10.toDp()))
+ }
+ }
+ }
+ }
+
+ // Initial setting will call onRemeasured and onPlaced for both
+ assertTrue(latch1.await(1, TimeUnit.SECONDS))
+ assertTrue(latch2.await(1, TimeUnit.SECONDS))
+ assertEquals(10, changedSize1.height)
+ assertEquals(10, changedSize1.width)
+ assertEquals(10, changedSize2.height)
+ assertEquals(10, changedSize2.width)
+
+ latch1 = CountDownLatch(2)
+ // Remove the modifier node
+ addModifier = false
+
+ // We've removed a node, so the other node should not be informed since there was no layout
+ // change
+ assertFalse(latch1.await(1, TimeUnit.SECONDS))
+ assertEquals(2, latch1.count)
+ }
+
+ @Test
+ @MediumTest
+ fun updatedModifierLambda() {
+ val latch1 = CountDownLatch(1)
+ val latch2 = CountDownLatch(1)
+ var changedSize1 = IntSize.Zero
+ var changedSize2 = IntSize.Zero
+
+ var lambda1: (IntSize) -> Unit by mutableStateOf({
+ changedSize1 = it
+ latch1.countDown()
+ })
+
+ // Stable lambda so that this one won't change while we change lambda1
+ val lambda2: (IntSize) -> Unit = {
+ changedSize2 = it
+ latch2.countDown()
+ }
+
+ rule.runOnUiThread {
+ activity.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier.padding(10.toDp())
+ .onSizeChanged(lambda1)
+ .onSizeChanged(lambda2)
+ ) {
+ Box(Modifier.requiredSize(10.toDp()))
+ }
+ }
+ }
+ }
+
+ // Initial setting will call onSizeChanged
+ assertTrue(latch1.await(1, TimeUnit.SECONDS))
+ assertTrue(latch2.await(1, TimeUnit.SECONDS))
+ assertEquals(10, changedSize1.height)
+ assertEquals(10, changedSize1.width)
+ assertEquals(10, changedSize2.height)
+ assertEquals(10, changedSize2.width)
+
+ val newLatch = CountDownLatch(1)
+ // Change lambda instance, this should cause us to invalidate and invoke callbacks again
+ lambda1 = {
+ changedSize1 = it
+ newLatch.countDown()
+ }
+
+ // We updated the lambda on the first item, so the new lambda should be called
+ assertTrue(newLatch.await(1, TimeUnit.SECONDS))
+ assertEquals(10, changedSize1.height)
+ assertEquals(10, changedSize1.width)
+ // The existing modifier will also be called, but onSizeChanged only invokes the lambda if
+ // the size changes, so we won't see it.
+ }
+
+ @Test
+ @MediumTest
+ fun updatedModifierNode() {
+ val latch1 = CountDownLatch(2)
+ var latch2 = CountDownLatch(2)
+ var changedSize1 = IntSize.Zero
+ var changedSize2 = IntSize.Zero
+
+ var onRemeasuredLambda: (IntSize) -> Unit by mutableStateOf({
+ changedSize1 = it
+ latch1.countDown()
+ })
+
+ var onPlacedLambda: (LayoutCoordinates) -> Unit by mutableStateOf({
+ latch1.countDown()
+ })
+
+ class Node1(
+ var onRemeasuredLambda: (IntSize) -> Unit,
+ var onPlacedLambda: (LayoutCoordinates) -> Unit
+ ) : LayoutAwareModifierNode, Modifier.Node() {
+ // We are testing auto invalidation behavior here
+ override val shouldAutoInvalidate = true
+
+ override fun onRemeasured(size: IntSize) {
+ onRemeasuredLambda(size)
+ }
+ override fun onPlaced(coordinates: LayoutCoordinates) {
+ onPlacedLambda(coordinates)
+ }
+ }
+
+ class Node1Element(
+ private var onRemeasured: (IntSize) -> Unit,
+ private var onPlaced: (LayoutCoordinates) -> Unit
+ ) : ModifierNodeElement<Node1>() {
+ override fun create(): Node1 {
+ return Node1(onRemeasured, onPlaced)
+ }
+
+ override fun update(node: Node1) {
+ node.onRemeasuredLambda = onRemeasured
+ node.onPlacedLambda = onPlaced
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is Node1Element) return false
+
+ if (onRemeasured != other.onRemeasured) return false
+ if (onPlaced != other.onPlaced) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = onRemeasured.hashCode()
+ result = 31 * result + onPlaced.hashCode()
+ return result
+ }
+ }
+
+ val node2 = object : LayoutAwareModifierNode, Modifier.Node() {
+ override fun onRemeasured(size: IntSize) {
+ changedSize2 = size
+ latch2.countDown()
+ }
+ override fun onPlaced(coordinates: LayoutCoordinates) {
+ latch2.countDown()
+ }
+ }
+
+ rule.runOnUiThread {
+ activity.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier.padding(10.toDp())
+ .then(Node1Element(onRemeasuredLambda, onPlacedLambda))
+ .elementFor(node2)
+ ) {
+ Box(Modifier.requiredSize(10.toDp()))
+ }
+ }
+ }
+ }
+
+ // Initial setting will call onSizeChanged
+ assertTrue(latch1.await(1, TimeUnit.SECONDS))
+ assertTrue(latch2.await(1, TimeUnit.SECONDS))
+ assertEquals(10, changedSize1.height)
+ assertEquals(10, changedSize1.width)
+ assertEquals(10, changedSize2.height)
+ assertEquals(10, changedSize2.width)
+
+ latch2 = CountDownLatch(2)
+ val newLatch = CountDownLatch(2)
+ // Change lambda instance, this should cause us to autoinvalidate and invoke callbacks again
+ onRemeasuredLambda = {
+ changedSize1 = it
+ newLatch.countDown()
+ }
+ onPlacedLambda = {
+ newLatch.countDown()
+ }
+
+ // We updated the lambda on the first item, so the new lambda should be called
+ assertTrue(newLatch.await(1, TimeUnit.SECONDS))
+ assertEquals(10, changedSize1.height)
+ assertEquals(10, changedSize1.width)
+ // Currently updating causes a relayout, so the existing node should also be invoked. In
+ // the future this might be optimized so we only re-invoke the callbacks on the updated
+ // node, without causing a full relayout / affecting other nodes.
+ assertTrue(latch2.await(1, TimeUnit.SECONDS))
assertEquals(10, changedSize2.height)
assertEquals(10, changedSize2.width)
}
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
index c2ec417..339d19f 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
@@ -508,11 +508,7 @@
drawBlock: (Canvas, GraphicsLayer?) -> Unit,
invalidateParentLayer: () -> Unit
) {}
- override fun updateLayerProperties(
- scope: ReusableGraphicsLayerScope,
- layoutDirection: LayoutDirection,
- density: Density
- ) {
+ override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {
transform.reset()
// This is not expected to be 100% accurate
transform.scale(scope.scaleX, scope.scaleY)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index 3631b76..d4a106f 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -26,6 +26,7 @@
import android.os.Build.VERSION_CODES.M
import android.os.Build.VERSION_CODES.N
import android.os.Build.VERSION_CODES.O
+import android.os.Build.VERSION_CODES.P
import android.os.Build.VERSION_CODES.Q
import android.os.Build.VERSION_CODES.S
import android.os.Looper
@@ -1426,20 +1427,22 @@
return layer
}
+ // enable new layers on versions supporting render nodes
+ if (isHardwareAccelerated && SDK_INT >= M && SDK_INT != P) {
+ return GraphicsLayerOwnerLayer(
+ graphicsLayer = graphicsContext.createGraphicsLayer(),
+ context = graphicsContext,
+ ownerView = this,
+ drawBlock = drawBlock,
+ invalidateParentLayer = invalidateParentLayer
+ )
+ }
+
// RenderNode is supported on Q+ for certain, but may also be supported on M-O.
// We can't be confident that RenderNode is supported, so we try and fail over to
// the ViewLayer implementation. We'll try even on on P devices, but it will fail
// until ART allows things on the unsupported list on P.
if (isHardwareAccelerated && SDK_INT >= M && isRenderNodeCompatible) {
- if (SDK_INT >= Q) {
- return GraphicsLayerOwnerLayer(
- graphicsLayer = graphicsContext.createGraphicsLayer(),
- context = graphicsContext,
- ownerView = this,
- drawBlock = drawBlock,
- invalidateParentLayer = invalidateParentLayer
- )
- }
try {
return RenderNodeLayer(
this,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index d21a838..c6254fb 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -1349,9 +1349,29 @@
}
}
+ if (node.unmergedConfig.contains(SemanticsActions.SetText)) {
+ stateDescription = createStateDescriptionForTextField(node)
+ }
+
return stateDescription
}
+ /**
+ * Empty text field should not be ignored by the TB so we set a state description.
+ * When there is a speakable child, like a label or a placeholder text, setting this state
+ * description is redundant
+ */
+ private fun createStateDescriptionForTextField(node: SemanticsNode): String? {
+ val mergedConfig = node.copyWithMergingEnabled().config
+ val mergedNodeIsUnspeakable =
+ mergedConfig.getOrNull(SemanticsProperties.ContentDescription).isNullOrEmpty() &&
+ mergedConfig.getOrNull(SemanticsProperties.Text).isNullOrEmpty() &&
+ mergedConfig.getOrNull(SemanticsProperties.EditableText).isNullOrEmpty()
+ return if (mergedNodeIsUnspeakable) {
+ view.context.resources.getString(R.string.state_empty)
+ } else null
+ }
+
private fun setStateDescription(
node: SemanticsNode,
info: AccessibilityNodeInfoCompat,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt
index 12737fd..8399995 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt
@@ -28,9 +28,7 @@
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.ReusableGraphicsLayerScope
-import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
import androidx.compose.ui.graphics.drawscope.draw
@@ -80,7 +78,7 @@
private val scope = CanvasDrawScope()
private var mutatedFields: Int = 0
private var transformOrigin: TransformOrigin = TransformOrigin.Center
- private var shape: Shape = RectangleShape
+ private var outline: Outline? = null
private var tmpPath: Path? = null
/**
* Optional paint used when the RenderNode is rendered on a software backed
@@ -88,17 +86,10 @@
*/
private var softwareLayerPaint: Paint? = null
- override fun updateLayerProperties(
- scope: ReusableGraphicsLayerScope,
- layoutDirection: LayoutDirection,
- density: Density,
- ) {
- var maybeChangedFields = scope.mutatedFields or mutatedFields
- if (this.layoutDirection != layoutDirection || this.density != density) {
- this.layoutDirection = layoutDirection
- this.density = density
- maybeChangedFields = maybeChangedFields or Fields.Shape
- }
+ override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {
+ val maybeChangedFields = scope.mutatedFields or mutatedFields
+ this.layoutDirection = scope.layoutDirection
+ this.density = scope.graphicsDensity
if (maybeChangedFields and Fields.TransformOrigin != 0) {
this.transformOrigin = scope.transformOrigin
}
@@ -152,10 +143,6 @@
transformOrigin.pivotFractionY * size.height
)
}
- if (maybeChangedFields and Fields.Shape != 0) {
- this.shape = scope.shape
- updateOutline()
- }
if (maybeChangedFields and Fields.Clip != 0) {
graphicsLayer.clip = scope.clip
}
@@ -171,8 +158,16 @@
}
}
+ var outlineChanged = false
+
+ if (outline != scope.outline) {
+ outlineChanged = true
+ outline = scope.outline
+ updateOutline()
+ }
+
mutatedFields = scope.mutatedFields
- if (maybeChangedFields != 0) {
+ if (maybeChangedFields != 0 || outlineChanged) {
triggerRepaint()
}
}
@@ -189,7 +184,7 @@
}
private fun updateOutline() {
- val outline = shape.createOutline(size.toSize(), layoutDirection, density)
+ val outline = outline ?: return
graphicsLayer.setOutline(outline)
if (outline is Outline.Generic && Build.VERSION.SDK_INT < 33) {
// before 33 many of the paths are not clipping by rendernode. instead we have to
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/OutlineResolver.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/OutlineResolver.android.kt
index 7f15fef..30d26a5 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/OutlineResolver.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/OutlineResolver.android.kt
@@ -27,17 +27,13 @@
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.asAndroidPath
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.util.fastRoundToInt
/**
- * Resolves the [AndroidOutline] from the [Shape] of an [OwnedLayer].
+ * Resolves the [AndroidOutline] from the [Outline] of an [androidx.compose.ui.node.OwnedLayer].
*/
-internal class OutlineResolver(private var density: Density) {
+internal class OutlineResolver {
/**
* Flag to determine if the shape specified on the outline is supported.
@@ -51,14 +47,9 @@
private val cachedOutline = AndroidOutline().apply { alpha = 1f }
/**
- * The size of the layer. This is used in generating the [Outline] from the [Shape].
+ * The [Outline] of the Layer.
*/
- private var size: Size = Size.Zero
-
- /**
- * The [Shape] of the Outline of the Layer.
- */
- private var shape: Shape = RectangleShape
+ private var outline: Outline? = null
/**
* Asymmetric rounded rectangles need to use a Path. This caches that Path so that
@@ -107,7 +98,7 @@
/**
* Returns the Android Outline to be used in the layer.
*/
- val outline: AndroidOutline?
+ val androidOutline: AndroidOutline?
get() {
updateCache()
return if (!outlineNeeded || !isSupportedOutline) null else cachedOutline
@@ -145,7 +136,6 @@
/**
* Returns the size for a rectangular, or rounded rect outline (regardless if it
* is symmetric or asymmetric)
- * For path based outlines this returns [Size.Zero]
*/
private var rectSize: Size = Size.Zero
@@ -154,43 +144,32 @@
*/
private var outlineNeeded = false
- private var layoutDirection = LayoutDirection.Ltr
-
private var tmpTouchPointPath: Path? = null
private var tmpOpPath: Path? = null
- private var calculatedOutline: Outline? = null
/**
* Updates the values of the outline. Returns `true` when the shape has changed.
*/
fun update(
- shape: Shape,
+ outline: Outline?,
alpha: Float,
clipToOutline: Boolean,
elevation: Float,
- layoutDirection: LayoutDirection,
- density: Density
+ size: Size,
): Boolean {
cachedOutline.alpha = alpha
- val shapeChanged = this.shape != shape
- if (shapeChanged) {
- this.shape = shape
+ val outlineChanged = this.outline != outline
+ if (outlineChanged) {
+ this.outline = outline
cacheIsDirty = true
}
- val outlineNeeded = clipToOutline || elevation > 0f
+ this.rectSize = size
+ val outlineNeeded = outline != null && (clipToOutline || elevation > 0f)
if (this.outlineNeeded != outlineNeeded) {
this.outlineNeeded = outlineNeeded
cacheIsDirty = true
}
- if (this.layoutDirection != layoutDirection) {
- this.layoutDirection = layoutDirection
- cacheIsDirty = true
- }
- if (this.density != density) {
- this.density = density
- cacheIsDirty = true
- }
- return shapeChanged
+ return outlineChanged
}
/**
@@ -200,7 +179,7 @@
if (!outlineNeeded) {
return true
}
- val outline = calculatedOutline ?: return true
+ val outline = outline ?: return true
return isInOutline(outline, position.x, position.y, tmpTouchPointPath, tmpOpPath)
}
@@ -256,31 +235,20 @@
}
}
- /**
- * Updates the size.
- */
- fun update(size: Size) {
- if (this.size != size) {
- this.size = size
- cacheIsDirty = true
- }
- }
-
private fun updateCache() {
if (cacheIsDirty) {
rectTopLeft = Offset.Zero
- rectSize = size
roundedCornerRadius = 0f
outlinePath = null
cacheIsDirty = false
usePathForClip = false
- if (outlineNeeded && size.width > 0.0f && size.height > 0.0f) {
+ val outline = outline
+ if (outline != null && outlineNeeded &&
+ rectSize.width > 0.0f && rectSize.height > 0.0f) {
// Always assume the outline type is supported
// The methods to configure the outline will determine/update the flag
// if it not supported on the API level
isSupportedOutline = true
- val outline = shape.createOutline(size, layoutDirection, density)
- calculatedOutline = outline
when (outline) {
is Outline.Rectangle -> updateCacheWithRect(outline.rect)
is Outline.Rounded -> updateCacheWithRoundRect(outline.roundRect)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
index b1c7718..2e6c0be 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
@@ -19,10 +19,8 @@
import android.os.Build
import android.view.View
import androidx.annotation.RequiresApi
-import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.geometry.MutableRect
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.CanvasHolder
import androidx.compose.ui.graphics.Fields
@@ -36,10 +34,8 @@
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.GraphicLayerInfo
import androidx.compose.ui.node.OwnedLayer
-import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
/**
* RenderNode implementation of OwnedLayer.
@@ -63,11 +59,7 @@
ownerView.notifyLayerIsDirty(this, value)
}
}
- private val outlineResolver = Snapshot.withoutReadObservation {
- // we don't really care about observation here as density is applied manually
- // not observing the density changes saves performance on recording reads
- OutlineResolver(ownerView.density)
- }
+ private val outlineResolver = OutlineResolver()
private var isDestroyed = false
private var drawnWithZ = false
@@ -117,11 +109,7 @@
private var mutatedFields: Int = 0
- override fun updateLayerProperties(
- scope: ReusableGraphicsLayerScope,
- layoutDirection: LayoutDirection,
- density: Density,
- ) {
+ override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {
val maybeChangedFields = scope.mutatedFields or mutatedFields
if (maybeChangedFields and Fields.TransformOrigin != 0) {
this.transformOrigin = scope.transformOrigin
@@ -179,15 +167,14 @@
renderNode.compositingStrategy = scope.compositingStrategy
}
val shapeChanged = outlineResolver.update(
- scope.shape,
+ scope.outline,
scope.alpha,
clipToOutline,
scope.shadowElevation,
- layoutDirection,
- density
+ scope.size,
)
if (outlineResolver.cacheIsDirty) {
- renderNode.setOutline(outlineResolver.outline)
+ renderNode.setOutline(outlineResolver.androidOutline)
}
val isClippingManually = clipToOutline && !outlineResolver.outlineClipSupported
if (wasClippingManually != isClippingManually || (isClippingManually && shapeChanged)) {
@@ -232,8 +219,7 @@
renderNode.top + height
)
) {
- outlineResolver.update(Size(width.toFloat(), height.toFloat()))
- renderNode.setOutline(outlineResolver.outline)
+ renderNode.setOutline(outlineResolver.androidOutline)
invalidate()
matrixCache.invalidate()
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
index 0ccc913..6e820de 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
@@ -21,10 +21,8 @@
import android.view.View
import android.view.ViewOutlineProvider
import androidx.annotation.RequiresApi
-import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.geometry.MutableRect
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.CanvasHolder
import androidx.compose.ui.graphics.CompositingStrategy
@@ -39,10 +37,8 @@
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.GraphicLayerInfo
import androidx.compose.ui.node.OwnedLayer
-import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
import java.lang.reflect.Field
import java.lang.reflect.Method
@@ -58,11 +54,7 @@
private var drawBlock: ((canvas: Canvas, parentLayer: GraphicsLayer?) -> Unit)? = drawBlock
private var invalidateParentLayer: (() -> Unit)? = invalidateParentLayer
- private val outlineResolver = Snapshot.withoutReadObservation {
- // we don't really care about observation here as density is applied manually
- // not observing the density changes saves performance on recording reads
- OutlineResolver(ownerView.density)
- }
+ private val outlineResolver = OutlineResolver()
// Value of the layerModifier's clipToBounds property
private var clipToBounds = false
private var clipBoundsCache: android.graphics.Rect? = null
@@ -132,11 +124,7 @@
private var mutatedFields: Int = 0
- override fun updateLayerProperties(
- scope: ReusableGraphicsLayerScope,
- layoutDirection: LayoutDirection,
- density: Density,
- ) {
+ override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {
val maybeChangedFields = scope.mutatedFields or mutatedFields
if (maybeChangedFields and Fields.TransformOrigin != 0) {
this.mTransformOrigin = scope.transformOrigin
@@ -181,12 +169,11 @@
this.clipToOutline = clipToOutline
}
val shapeChanged = outlineResolver.update(
- scope.shape,
+ scope.outline,
scope.alpha,
clipToOutline,
scope.shadowElevation,
- layoutDirection,
- density
+ scope.size
)
if (outlineResolver.cacheIsDirty) {
updateOutlineResolver()
@@ -261,7 +248,7 @@
}
private fun updateOutlineResolver() {
- this.outlineProvider = if (outlineResolver.outline != null) {
+ this.outlineProvider = if (outlineResolver.androidOutline != null) {
OutlineProvider
} else {
null
@@ -287,7 +274,6 @@
if (width != this.width || height != this.height) {
pivotX = mTransformOrigin.pivotFractionX * width
pivotY = mTransformOrigin.pivotFractionY * height
- outlineResolver.update(Size(width.toFloat(), height.toFloat()))
updateOutlineResolver()
layout(left, top, left + width, top + height)
resetClipBounds()
@@ -437,7 +423,7 @@
val OutlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: android.graphics.Outline) {
view as ViewLayer
- outline.set(view.outlineResolver.outline!!)
+ outline.set(view.outlineResolver.androidOutline!!)
}
}
private var updateDisplayListIfDirtyMethod: Method? = null
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 d4ff363..374463d 100644
--- a/compose/ui/ui/src/androidMain/res/values-af/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-af/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Maak navigasiekieslys toe"</string>
<string name="close_sheet" msgid="7573152094250666567">"Maak sigblad toe"</string>
<string name="default_error_message" msgid="8038256446254964252">"Ongeldige invoer"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Opspringvenster"</string>
<string name="range_start" msgid="7097486360902471446">"Begingrens"</string>
<string name="range_end" msgid="5941395253238309765">"Eindgrens"</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 63202e8..1a4e3dd 100644
--- a/compose/ui/ui/src/androidMain/res/values-am/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-am/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"የዳሰሳ ምናሌን ዝጋ"</string>
<string name="close_sheet" msgid="7573152094250666567">"ሉህን ዝጋ"</string>
<string name="default_error_message" msgid="8038256446254964252">"ልክ ያልሆነ ግቤት"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"ብቅ-ባይ መስኮት"</string>
<string name="range_start" msgid="7097486360902471446">"የክልል መጀመሪያ"</string>
<string name="range_end" msgid="5941395253238309765">"የክልል መጨረሻ"</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 3cc8b9b..58c7262 100644
--- a/compose/ui/ui/src/androidMain/res/values-ar/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ar/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"إغلاق قائمة التنقل"</string>
<string name="close_sheet" msgid="7573152094250666567">"إغلاق الورقة"</string>
<string name="default_error_message" msgid="8038256446254964252">"إدخال غير صالح"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"نافذة منبثقة"</string>
<string name="range_start" msgid="7097486360902471446">"بداية النطاق"</string>
<string name="range_end" msgid="5941395253238309765">"نهاية النطاق"</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 c68278cc..b6a3786 100644
--- a/compose/ui/ui/src/androidMain/res/values-as/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-as/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"নেভিগেশ্বন মেনু বন্ধ কৰক"</string>
<string name="close_sheet" msgid="7573152094250666567">"শ্বীট বন্ধ কৰক"</string>
<string name="default_error_message" msgid="8038256446254964252">"অমান্য ইনপুট"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"পপ-আপ ৱিণ্ড’"</string>
<string name="range_start" msgid="7097486360902471446">"পৰিসৰৰ আৰম্ভণি"</string>
<string name="range_end" msgid="5941395253238309765">"পৰিসৰৰ সমাপ্তি"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-az/strings.xml b/compose/ui/ui/src/androidMain/res/values-az/strings.xml
index 929a2a6..ccac763 100644
--- a/compose/ui/ui/src/androidMain/res/values-az/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-az/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Naviqasiya menyusunu bağlayın"</string>
<string name="close_sheet" msgid="7573152094250666567">"Səhifəni bağlayın"</string>
<string name="default_error_message" msgid="8038256446254964252">"Yanlış daxiletmə"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Popap Pəncərəsi"</string>
<string name="range_start" msgid="7097486360902471446">"Sıranın başlanğıcı"</string>
<string name="range_end" msgid="5941395253238309765">"Sıranın sonu"</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 5f78ccf..794d4ee 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
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Zatvori meni za navigaciju"</string>
<string name="close_sheet" msgid="7573152094250666567">"Zatvorite tabelu"</string>
<string name="default_error_message" msgid="8038256446254964252">"Unos je nevažeći"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Iskačući prozor"</string>
<string name="range_start" msgid="7097486360902471446">"Početak opsega"</string>
<string name="range_end" msgid="5941395253238309765">"Kraj opsega"</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 f36b4bb..3be7010 100644
--- a/compose/ui/ui/src/androidMain/res/values-be/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-be/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Закрыць меню навігацыі"</string>
<string name="close_sheet" msgid="7573152094250666567">"Закрыць аркуш"</string>
<string name="default_error_message" msgid="8038256446254964252">"Памылка ўводу"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Усплывальнае акно"</string>
<string name="range_start" msgid="7097486360902471446">"Пачатак пераліку"</string>
<string name="range_end" msgid="5941395253238309765">"Канец пераліку"</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 68b6856..6814429 100644
--- a/compose/ui/ui/src/androidMain/res/values-bg/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-bg/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Затваряне на менюто за навигация"</string>
<string name="close_sheet" msgid="7573152094250666567">"Затваряне на таблицата"</string>
<string name="default_error_message" msgid="8038256446254964252">"Въведеното е невалидно"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Изскачащ прозорец"</string>
<string name="range_start" msgid="7097486360902471446">"Начало на обхвата"</string>
<string name="range_end" msgid="5941395253238309765">"Край на обхвата"</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 ea9fe9b..2f84058 100644
--- a/compose/ui/ui/src/androidMain/res/values-bn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-bn/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"নেভিগেশন মেনু বন্ধ করুন"</string>
<string name="close_sheet" msgid="7573152094250666567">"শিট বন্ধ করুন"</string>
<string name="default_error_message" msgid="8038256446254964252">"ভুল ইনপুট"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"পপ-আপ উইন্ডো"</string>
<string name="range_start" msgid="7097486360902471446">"রেঞ্জ শুরু"</string>
<string name="range_end" msgid="5941395253238309765">"রেঞ্জ শেষ"</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 acf3667..d8d8334 100644
--- a/compose/ui/ui/src/androidMain/res/values-bs/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-bs/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Zatvaranje navigacionog menija"</string>
<string name="close_sheet" msgid="7573152094250666567">"Zatvaranje tabele"</string>
<string name="default_error_message" msgid="8038256446254964252">"Pogrešan unos"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Skočni prozor"</string>
<string name="range_start" msgid="7097486360902471446">"Početak raspona"</string>
<string name="range_end" msgid="5941395253238309765">"Kraj raspona"</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 f85f039..dd44ae7 100644
--- a/compose/ui/ui/src/androidMain/res/values-ca/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ca/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Tanca el menú de navegació"</string>
<string name="close_sheet" msgid="7573152094250666567">"Tanca el full"</string>
<string name="default_error_message" msgid="8038256446254964252">"L\'entrada no és vàlida"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Finestra emergent"</string>
<string name="range_start" msgid="7097486360902471446">"Inici de l\'interval"</string>
<string name="range_end" msgid="5941395253238309765">"Fi de l\'interval"</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 a0c4662..e182a1a 100644
--- a/compose/ui/ui/src/androidMain/res/values-cs/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-cs/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Zavřít navigační panel"</string>
<string name="close_sheet" msgid="7573152094250666567">"Zavřít sešit"</string>
<string name="default_error_message" msgid="8038256446254964252">"Neplatný údaj"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Vyskakovací okno"</string>
<string name="range_start" msgid="7097486360902471446">"Začátek rozsahu"</string>
<string name="range_end" msgid="5941395253238309765">"Konec rozsahu"</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 e440106..792e409 100644
--- a/compose/ui/ui/src/androidMain/res/values-da/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-da/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Luk navigationsmenuen"</string>
<string name="close_sheet" msgid="7573152094250666567">"Luk arket"</string>
<string name="default_error_message" msgid="8038256446254964252">"Ugyldigt input"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Pop op-vindue"</string>
<string name="range_start" msgid="7097486360902471446">"Startinterval"</string>
<string name="range_end" msgid="5941395253238309765">"Slutinterval"</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 fce4e02..0022a48 100644
--- a/compose/ui/ui/src/androidMain/res/values-de/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-de/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Navigationsmenü schließen"</string>
<string name="close_sheet" msgid="7573152094250666567">"Tabelle schließen"</string>
<string name="default_error_message" msgid="8038256446254964252">"Ungültige Eingabe"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Pop-up-Fenster"</string>
<string name="range_start" msgid="7097486360902471446">"Bereichsstart"</string>
<string name="range_end" msgid="5941395253238309765">"Bereichsende"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-el/strings.xml b/compose/ui/ui/src/androidMain/res/values-el/strings.xml
index 6a0e6ee..31496f4 100644
--- a/compose/ui/ui/src/androidMain/res/values-el/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-el/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Κλείσιμο του μενού πλοήγησης"</string>
<string name="close_sheet" msgid="7573152094250666567">"Κλείσιμο φύλλου"</string>
<string name="default_error_message" msgid="8038256446254964252">"Μη έγκυρη καταχώριση"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Αναδυόμενο παράθυρο"</string>
<string name="range_start" msgid="7097486360902471446">"Αρχή εύρους"</string>
<string name="range_end" msgid="5941395253238309765">"Τέλος εύρους"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rAU/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rAU/strings.xml
index 4cd1620..6cec5e5 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rAU/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rAU/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Close navigation menu"</string>
<string name="close_sheet" msgid="7573152094250666567">"Close sheet"</string>
<string name="default_error_message" msgid="8038256446254964252">"Invalid input"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Pop-up window"</string>
<string name="range_start" msgid="7097486360902471446">"Range start"</string>
<string name="range_end" msgid="5941395253238309765">"Range end"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rCA/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rCA/strings.xml
index 263002e13..af69c1f 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rCA/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rCA/strings.xml
@@ -31,6 +31,7 @@
<string name="close_drawer" msgid="406453423630273620">"Close navigation menu"</string>
<string name="close_sheet" msgid="7573152094250666567">"Close sheet"</string>
<string name="default_error_message" msgid="8038256446254964252">"Invalid input"</string>
+ <string name="state_empty" msgid="4139871816613051306">"Empty"</string>
<string name="default_popup_window_title" msgid="6312721426453364202">"Pop-Up Window"</string>
<string name="range_start" msgid="7097486360902471446">"Range start"</string>
<string name="range_end" msgid="5941395253238309765">"Range end"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rGB/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rGB/strings.xml
index 4cd1620..6cec5e5 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rGB/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rGB/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Close navigation menu"</string>
<string name="close_sheet" msgid="7573152094250666567">"Close sheet"</string>
<string name="default_error_message" msgid="8038256446254964252">"Invalid input"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Pop-up window"</string>
<string name="range_start" msgid="7097486360902471446">"Range start"</string>
<string name="range_end" msgid="5941395253238309765">"Range end"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rIN/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rIN/strings.xml
index 4cd1620..6cec5e5 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rIN/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rIN/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Close navigation menu"</string>
<string name="close_sheet" msgid="7573152094250666567">"Close sheet"</string>
<string name="default_error_message" msgid="8038256446254964252">"Invalid input"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Pop-up window"</string>
<string name="range_start" msgid="7097486360902471446">"Range start"</string>
<string name="range_end" msgid="5941395253238309765">"Range end"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values-en-rXC/strings.xml b/compose/ui/ui/src/androidMain/res/values-en-rXC/strings.xml
index 4779808..e19716e 100644
--- a/compose/ui/ui/src/androidMain/res/values-en-rXC/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-en-rXC/strings.xml
@@ -31,6 +31,7 @@
<string name="close_drawer" msgid="406453423630273620">"Close navigation menu"</string>
<string name="close_sheet" msgid="7573152094250666567">"Close sheet"</string>
<string name="default_error_message" msgid="8038256446254964252">"Invalid input"</string>
+ <string name="state_empty" msgid="4139871816613051306">"Empty"</string>
<string name="default_popup_window_title" msgid="6312721426453364202">"Pop-Up Window"</string>
<string name="range_start" msgid="7097486360902471446">"Range start"</string>
<string name="range_end" msgid="5941395253238309765">"Range end"</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 bf5e07e..db50050 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
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Cerrar el menú de navegación"</string>
<string name="close_sheet" msgid="7573152094250666567">"Cerrar hoja"</string>
<string name="default_error_message" msgid="8038256446254964252">"Entrada no válida"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Ventana emergente"</string>
<string name="range_start" msgid="7097486360902471446">"Inicio de intervalo"</string>
<string name="range_end" msgid="5941395253238309765">"Final de intervalo"</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 68c06e1..3b25bdd 100644
--- a/compose/ui/ui/src/androidMain/res/values-es/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-es/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Cerrar menú de navegación"</string>
<string name="close_sheet" msgid="7573152094250666567">"Cerrar hoja"</string>
<string name="default_error_message" msgid="8038256446254964252">"Entrada no válida"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Ventana emergente"</string>
<string name="range_start" msgid="7097486360902471446">"Inicio del intervalo"</string>
<string name="range_end" msgid="5941395253238309765">"Fin del intervalo"</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 803206d..dd9cae9 100644
--- a/compose/ui/ui/src/androidMain/res/values-et/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-et/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Sule navigeerimismenüü"</string>
<string name="close_sheet" msgid="7573152094250666567">"Sule leht"</string>
<string name="default_error_message" msgid="8038256446254964252">"Sobimatu sisend"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Hüpikaken"</string>
<string name="range_start" msgid="7097486360902471446">"Vahemiku algus"</string>
<string name="range_end" msgid="5941395253238309765">"Vahemiku lõpp"</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 edcc8f2..757bf68 100644
--- a/compose/ui/ui/src/androidMain/res/values-eu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-eu/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Itxi nabigazio-menua"</string>
<string name="close_sheet" msgid="7573152094250666567">"Itxi orria"</string>
<string name="default_error_message" msgid="8038256446254964252">"Sarrerak ez du balio"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Leiho gainerakorra"</string>
<string name="range_start" msgid="7097486360902471446">"Barrutiaren hasiera"</string>
<string name="range_end" msgid="5941395253238309765">"Barrutiaren amaiera"</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 39b1a92..c9c25ee 100644
--- a/compose/ui/ui/src/androidMain/res/values-fa/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fa/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"بستن منوی پیمایش"</string>
<string name="close_sheet" msgid="7573152094250666567">"بستن برگ"</string>
<string name="default_error_message" msgid="8038256446254964252">"ورودی نامعتبر"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"پنجره بالاپر"</string>
<string name="range_start" msgid="7097486360902471446">"شروع محدوده"</string>
<string name="range_end" msgid="5941395253238309765">"پایان محدوده"</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 5479432..db69277 100644
--- a/compose/ui/ui/src/androidMain/res/values-fi/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fi/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Sulje navigointivalikko"</string>
<string name="close_sheet" msgid="7573152094250666567">"Sulje taulukko"</string>
<string name="default_error_message" msgid="8038256446254964252">"Virheellinen syöte"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Ponnahdusikkuna"</string>
<string name="range_start" msgid="7097486360902471446">"Alueen alku"</string>
<string name="range_end" msgid="5941395253238309765">"Alueen loppu"</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 abcbedd..6c9a7ed 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
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Fermer le menu de navigation"</string>
<string name="close_sheet" msgid="7573152094250666567">"Fermer la feuille"</string>
<string name="default_error_message" msgid="8038256446254964252">"Entrée incorrecte"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Fenêtre contextuelle"</string>
<string name="range_start" msgid="7097486360902471446">"Début de plage"</string>
<string name="range_end" msgid="5941395253238309765">"Fin de plage"</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 7f6e679..29894ec 100644
--- a/compose/ui/ui/src/androidMain/res/values-fr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-fr/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Fermer le menu de navigation"</string>
<string name="close_sheet" msgid="7573152094250666567">"Fermer la feuille"</string>
<string name="default_error_message" msgid="8038256446254964252">"Données incorrectes"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Fenêtre pop-up"</string>
<string name="range_start" msgid="7097486360902471446">"Début de plage"</string>
<string name="range_end" msgid="5941395253238309765">"Fin de plage"</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 045f898..e7a7c7b 100644
--- a/compose/ui/ui/src/androidMain/res/values-gl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-gl/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Pechar menú de navegación"</string>
<string name="close_sheet" msgid="7573152094250666567">"Pechar folla"</string>
<string name="default_error_message" msgid="8038256446254964252">"O texto escrito non é válido"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Ventá emerxente"</string>
<string name="range_start" msgid="7097486360902471446">"Inicio do intervalo"</string>
<string name="range_end" msgid="5941395253238309765">"Fin do intervalo"</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 a4cd743..23b9ccb 100644
--- a/compose/ui/ui/src/androidMain/res/values-gu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-gu/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"નૅવિગેશન મેનૂ બંધ કરો"</string>
<string name="close_sheet" msgid="7573152094250666567">"શીટ બંધ કરો"</string>
<string name="default_error_message" msgid="8038256446254964252">"અમાન્ય ઇનપુટ"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"પૉપ-અપ વિન્ડો"</string>
<string name="range_start" msgid="7097486360902471446">"રેંજની શરૂઆત"</string>
<string name="range_end" msgid="5941395253238309765">"રેંજની સમાપ્તિ"</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 fe6906a..be2db07 100644
--- a/compose/ui/ui/src/androidMain/res/values-hi/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hi/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"नेविगेशन मेन्यू बंद करें"</string>
<string name="close_sheet" msgid="7573152094250666567">"शीट बंद करें"</string>
<string name="default_error_message" msgid="8038256446254964252">"अमान्य इनपुट"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"पॉप-अप विंडो"</string>
<string name="range_start" msgid="7097486360902471446">"रेंज की शुरुआत"</string>
<string name="range_end" msgid="5941395253238309765">"रेंज की सीमा"</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 1723b39..c168ea0 100644
--- a/compose/ui/ui/src/androidMain/res/values-hr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hr/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Zatvaranje izbornika za navigaciju"</string>
<string name="close_sheet" msgid="7573152094250666567">"Zatvaranje lista"</string>
<string name="default_error_message" msgid="8038256446254964252">"Nevažeći unos"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Skočni prozor"</string>
<string name="range_start" msgid="7097486360902471446">"Početak raspona"</string>
<string name="range_end" msgid="5941395253238309765">"Kraj raspona"</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 fa50b3e..631ea36 100644
--- a/compose/ui/ui/src/androidMain/res/values-hu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hu/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Navigációs menü bezárása"</string>
<string name="close_sheet" msgid="7573152094250666567">"Munkalap bezárása"</string>
<string name="default_error_message" msgid="8038256446254964252">"Érvénytelen adat"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Előugró ablak"</string>
<string name="range_start" msgid="7097486360902471446">"Tartomány kezdete"</string>
<string name="range_end" msgid="5941395253238309765">"Tartomány vége"</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 3acfd76..598a469 100644
--- a/compose/ui/ui/src/androidMain/res/values-hy/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-hy/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Փակել նավիգացիայի ընտրացանկը"</string>
<string name="close_sheet" msgid="7573152094250666567">"Փակել թերթը"</string>
<string name="default_error_message" msgid="8038256446254964252">"Սխալ ներածում"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Ելնող պատուհան"</string>
<string name="range_start" msgid="7097486360902471446">"Ընդգրկույթի սկիզբ"</string>
<string name="range_end" msgid="5941395253238309765">"Ընդգրկույթի վերջ"</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 577e85c..650487a 100644
--- a/compose/ui/ui/src/androidMain/res/values-in/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-in/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Tutup menu navigasi"</string>
<string name="close_sheet" msgid="7573152094250666567">"Tutup sheet"</string>
<string name="default_error_message" msgid="8038256446254964252">"Input tidak valid"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Jendela Pop-Up"</string>
<string name="range_start" msgid="7097486360902471446">"Rentang awal"</string>
<string name="range_end" msgid="5941395253238309765">"Rentang akhir"</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 af87f3b..71b16b1 100644
--- a/compose/ui/ui/src/androidMain/res/values-is/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-is/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Loka yfirlitsvalmynd"</string>
<string name="close_sheet" msgid="7573152094250666567">"Loka blaði"</string>
<string name="default_error_message" msgid="8038256446254964252">"Ógildur innsláttur"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Sprettigluggi"</string>
<string name="range_start" msgid="7097486360902471446">"Upphaf sviðs"</string>
<string name="range_end" msgid="5941395253238309765">"Lok sviðs"</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 e5727e4..509a24d 100644
--- a/compose/ui/ui/src/androidMain/res/values-it/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-it/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Chiudi il menu di navigazione"</string>
<string name="close_sheet" msgid="7573152094250666567">"Chiudi il foglio"</string>
<string name="default_error_message" msgid="8038256446254964252">"Valore non valido"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Finestra popup"</string>
<string name="range_start" msgid="7097486360902471446">"Inizio intervallo"</string>
<string name="range_end" msgid="5941395253238309765">"Fine intervallo"</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 f32fd3a..f567055 100644
--- a/compose/ui/ui/src/androidMain/res/values-iw/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-iw/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"סגירת תפריט הניווט"</string>
<string name="close_sheet" msgid="7573152094250666567">"סגירת הגיליון"</string>
<string name="default_error_message" msgid="8038256446254964252">"הקלט לא תקין"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"חלון קופץ"</string>
<string name="range_start" msgid="7097486360902471446">"תחילת הטווח"</string>
<string name="range_end" msgid="5941395253238309765">"סוף הטווח"</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 0563fb4..cf83e57 100644
--- a/compose/ui/ui/src/androidMain/res/values-ja/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ja/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"ナビゲーションメニューを閉じる"</string>
<string name="close_sheet" msgid="7573152094250666567">"シートを閉じる"</string>
<string name="default_error_message" msgid="8038256446254964252">"入力値が無効です"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"ポップアップウィンドウ"</string>
<string name="range_start" msgid="7097486360902471446">"範囲の先頭"</string>
<string name="range_end" msgid="5941395253238309765">"範囲の末尾"</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 d108b025..eb093ba 100644
--- a/compose/ui/ui/src/androidMain/res/values-ka/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ka/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"ნავიგაციის მენიუს დახურვა"</string>
<string name="close_sheet" msgid="7573152094250666567">"ფურცლის დახურვა"</string>
<string name="default_error_message" msgid="8038256446254964252">"შენატანი არასწორია"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"ამომხტარი ფანჯარა"</string>
<string name="range_start" msgid="7097486360902471446">"დიაპაზონის დასაწყისი"</string>
<string name="range_end" msgid="5941395253238309765">"დიაპაზონის დასასრული"</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 69c768d..d21df0b 100644
--- a/compose/ui/ui/src/androidMain/res/values-kk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-kk/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Навигация мәзірін жабу"</string>
<string name="close_sheet" msgid="7573152094250666567">"Парақты жабу"</string>
<string name="default_error_message" msgid="8038256446254964252">"Енгізілген мән жарамсыз."</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Қалқымалы терезе"</string>
<string name="range_start" msgid="7097486360902471446">"Аралықтың басы"</string>
<string name="range_end" msgid="5941395253238309765">"Аралықтың соңы"</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 5257840..207a067 100644
--- a/compose/ui/ui/src/androidMain/res/values-km/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-km/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"បិទម៉ឺនុយរុករក"</string>
<string name="close_sheet" msgid="7573152094250666567">"បិទសន្លឹក"</string>
<string name="default_error_message" msgid="8038256446254964252">"ការបញ្ចូលមិនត្រឹមត្រូវ"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"វិនដូលោតឡើង"</string>
<string name="range_start" msgid="7097486360902471446">"ចំណុចចាប់ផ្ដើម"</string>
<string name="range_end" msgid="5941395253238309765">"ចំណុចបញ្ចប់"</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 271b86a..5eb9b0b 100644
--- a/compose/ui/ui/src/androidMain/res/values-kn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-kn/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"ನ್ಯಾವಿಗೇಷನ್ ಮೆನು ಮುಚ್ಚಿರಿ"</string>
<string name="close_sheet" msgid="7573152094250666567">"ಶೀಟ್ ಮುಚ್ಚಿರಿ"</string>
<string name="default_error_message" msgid="8038256446254964252">"ಅಮಾನ್ಯ ಇನ್ಪುಟ್"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"ಪಾಪ್-ಅಪ್ ವಿಂಡೋ"</string>
<string name="range_start" msgid="7097486360902471446">"ಶ್ರೇಣಿಯ ಪ್ರಾರಂಭ"</string>
<string name="range_end" msgid="5941395253238309765">"ಶ್ರೇಣಿಯ ಅಂತ್ಯ"</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 b486704..d452c4d82 100644
--- a/compose/ui/ui/src/androidMain/res/values-ko/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ko/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"탐색 메뉴 닫기"</string>
<string name="close_sheet" msgid="7573152094250666567">"시트 닫기"</string>
<string name="default_error_message" msgid="8038256446254964252">"입력이 잘못됨"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"팝업 창"</string>
<string name="range_start" msgid="7097486360902471446">"범위 시작"</string>
<string name="range_end" msgid="5941395253238309765">"범위 끝"</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 686a577..4c6591e 100644
--- a/compose/ui/ui/src/androidMain/res/values-ky/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ky/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Чабыттоо менюсун жабуу"</string>
<string name="close_sheet" msgid="7573152094250666567">"Баракты жабуу"</string>
<string name="default_error_message" msgid="8038256446254964252">"Киргизилген маалымат жараксыз"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Калкыма терезе"</string>
<string name="range_start" msgid="7097486360902471446">"Диапазондун башы"</string>
<string name="range_end" msgid="5941395253238309765">"Диапазондун аягы"</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 7c36d6e..95bccae 100644
--- a/compose/ui/ui/src/androidMain/res/values-lo/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-lo/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"ປິດເມນູການນຳທາງ"</string>
<string name="close_sheet" msgid="7573152094250666567">"ປິດຊີດ"</string>
<string name="default_error_message" msgid="8038256446254964252">"ຂໍ້ມູນທີ່ປ້ອນເຂົ້າບໍ່ຖືກຕ້ອງ"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"ໜ້າຈໍປັອບອັບ"</string>
<string name="range_start" msgid="7097486360902471446">"ເລີ່ມຕົ້ນໄລຍະ"</string>
<string name="range_end" msgid="5941395253238309765">"ສິ້ນສຸດໄລຍະ"</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 7ad81ab..9da20ee 100644
--- a/compose/ui/ui/src/androidMain/res/values-lt/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-lt/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Uždaryti naršymo meniu"</string>
<string name="close_sheet" msgid="7573152094250666567">"Uždaryti lapą"</string>
<string name="default_error_message" msgid="8038256446254964252">"Netinkama įvestis"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Iššokantysis langas"</string>
<string name="range_start" msgid="7097486360902471446">"Diapazono pradžia"</string>
<string name="range_end" msgid="5941395253238309765">"Diapazono pabaiga"</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 4baf5f9..4a5726e 100644
--- a/compose/ui/ui/src/androidMain/res/values-lv/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-lv/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Aizvērt navigācijas izvēlni"</string>
<string name="close_sheet" msgid="7573152094250666567">"Aizvērt izklājlapu"</string>
<string name="default_error_message" msgid="8038256446254964252">"Nederīga ievade"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Uznirstošais logs"</string>
<string name="range_start" msgid="7097486360902471446">"Diapazona sākums"</string>
<string name="range_end" msgid="5941395253238309765">"Diapazona beigas"</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 b3f14062..6580504 100644
--- a/compose/ui/ui/src/androidMain/res/values-mk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-mk/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Затворете го менито за навигација"</string>
<string name="close_sheet" msgid="7573152094250666567">"Затворете го листот"</string>
<string name="default_error_message" msgid="8038256446254964252">"Неважечки запис"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Скокачки прозорец"</string>
<string name="range_start" msgid="7097486360902471446">"Почеток на опсегот"</string>
<string name="range_end" msgid="5941395253238309765">"Крај на опсегот"</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 7e1a80d..0514441 100644
--- a/compose/ui/ui/src/androidMain/res/values-ml/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ml/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"നാവിഗേഷൻ മെനു അടയ്ക്കുക"</string>
<string name="close_sheet" msgid="7573152094250666567">"ഷീറ്റ് അടയ്ക്കുക"</string>
<string name="default_error_message" msgid="8038256446254964252">"ഇൻപുട്ട് അസാധുവാണ്"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"പോപ്പ്-അപ്പ് വിൻഡോ"</string>
<string name="range_start" msgid="7097486360902471446">"ശ്രേണിയുടെ ആരംഭം"</string>
<string name="range_end" msgid="5941395253238309765">"ശ്രേണിയുടെ അവസാനം"</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 9fd17b1..df96b02 100644
--- a/compose/ui/ui/src/androidMain/res/values-mn/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-mn/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Навигацын цэсийг хаах"</string>
<string name="close_sheet" msgid="7573152094250666567">"Хүснэгтийг хаах"</string>
<string name="default_error_message" msgid="8038256446254964252">"Буруу оролт"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Попап цонх"</string>
<string name="range_start" msgid="7097486360902471446">"Мужийн эхлэл"</string>
<string name="range_end" msgid="5941395253238309765">"Мужийн төгсгөл"</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 ad44056..54aaab6 100644
--- a/compose/ui/ui/src/androidMain/res/values-mr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-mr/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"नेव्हिगेशन मेनू बंद करा"</string>
<string name="close_sheet" msgid="7573152094250666567">"शीट बंद करा"</string>
<string name="default_error_message" msgid="8038256446254964252">"इनपुट चुकीचे आहे"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"पॉप-अप विंडो"</string>
<string name="range_start" msgid="7097486360902471446">"रेंजची सुरुवात"</string>
<string name="range_end" msgid="5941395253238309765">"रेंजचा शेवट"</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 d33a81a..72af8eb 100644
--- a/compose/ui/ui/src/androidMain/res/values-ms/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ms/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Tutup menu navigasi"</string>
<string name="close_sheet" msgid="7573152094250666567">"Tutup helaian"</string>
<string name="default_error_message" msgid="8038256446254964252">"Input tidak sah"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Tetingkap Timbul"</string>
<string name="range_start" msgid="7097486360902471446">"Permulaan julat"</string>
<string name="range_end" msgid="5941395253238309765">"Penghujung julat"</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 1ef3aab..074defe 100644
--- a/compose/ui/ui/src/androidMain/res/values-my/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-my/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"လမ်းညွှန် မီနူး ပိတ်ရန်"</string>
<string name="close_sheet" msgid="7573152094250666567">"စာမျက်နှာ ပိတ်ရန်"</string>
<string name="default_error_message" msgid="8038256446254964252">"ထည့်သွင်းမှု မမှန်ကန်ပါ"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"ပေါ့ပ်အပ် ဝင်းဒိုး"</string>
<string name="range_start" msgid="7097486360902471446">"အပိုင်းအခြား အစ"</string>
<string name="range_end" msgid="5941395253238309765">"အပိုင်းအခြား အဆုံး"</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 1395a49..2a4c5d6 100644
--- a/compose/ui/ui/src/androidMain/res/values-nb/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-nb/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Lukk navigasjonsmenyen"</string>
<string name="close_sheet" msgid="7573152094250666567">"Lukk arket"</string>
<string name="default_error_message" msgid="8038256446254964252">"Ugyldige inndata"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Forgrunnsvindu"</string>
<string name="range_start" msgid="7097486360902471446">"Områdestart"</string>
<string name="range_end" msgid="5941395253238309765">"Områdeslutt"</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 53b2ce10..7078d5c 100644
--- a/compose/ui/ui/src/androidMain/res/values-ne/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ne/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"नेभिगेसन मेनु बन्द गर्नुहोस्"</string>
<string name="close_sheet" msgid="7573152094250666567">"पाना बन्द गर्नुहोस्"</string>
<string name="default_error_message" msgid="8038256446254964252">"अवैद्य इन्पुट"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"पपअप विन्डो"</string>
<string name="range_start" msgid="7097486360902471446">"दायराको सुरुवात बिन्दु"</string>
<string name="range_end" msgid="5941395253238309765">"दायराको अन्तिम बिन्दु"</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 c2a6f5b..129ccb0 100644
--- a/compose/ui/ui/src/androidMain/res/values-nl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-nl/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Navigatiemenu sluiten"</string>
<string name="close_sheet" msgid="7573152094250666567">"Blad sluiten"</string>
<string name="default_error_message" msgid="8038256446254964252">"Ongeldige invoer"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Pop-upvenster"</string>
<string name="range_start" msgid="7097486360902471446">"Start bereik"</string>
<string name="range_end" msgid="5941395253238309765">"Einde bereik"</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 32a9c02..6773c55 100644
--- a/compose/ui/ui/src/androidMain/res/values-or/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-or/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"ନାଭିଗେସନ୍ ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="close_sheet" msgid="7573152094250666567">"ସିଟ୍ ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="default_error_message" msgid="8038256446254964252">"ଅବୈଧ ଇନପୁଟ୍"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"ପପ୍-ଅପ୍ ୱିଣ୍ଡୋ"</string>
<string name="range_start" msgid="7097486360902471446">"ରେଞ୍ଜ ଆରମ୍ଭ"</string>
<string name="range_end" msgid="5941395253238309765">"ରେଞ୍ଜ ଶେଷ"</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 1a25393..a88164a 100644
--- a/compose/ui/ui/src/androidMain/res/values-pa/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pa/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"ਨੈਵੀਗੇਸ਼ਨ ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
<string name="close_sheet" msgid="7573152094250666567">"ਸ਼ੀਟ ਬੰਦ ਕਰੋ"</string>
<string name="default_error_message" msgid="8038256446254964252">"ਅਵੈਧ ਇਨਪੁੱਟ"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"ਪੌਪ-ਅੱਪ ਵਿੰਡੋ"</string>
<string name="range_start" msgid="7097486360902471446">"ਰੇਂਜ ਸ਼ੁਰੂ"</string>
<string name="range_end" msgid="5941395253238309765">"ਰੇਂਜ ਸਮਾਪਤ"</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 6029183..aaff133 100644
--- a/compose/ui/ui/src/androidMain/res/values-pl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pl/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Zamknij menu nawigacyjne"</string>
<string name="close_sheet" msgid="7573152094250666567">"Zamknij arkusz"</string>
<string name="default_error_message" msgid="8038256446254964252">"Nieprawidłowe dane wejściowe"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Wyskakujące okienko"</string>
<string name="range_start" msgid="7097486360902471446">"Początek zakresu"</string>
<string name="range_end" msgid="5941395253238309765">"Koniec zakresu"</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 0c70403..e5d9ff3 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
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Fechar menu de navegação"</string>
<string name="close_sheet" msgid="7573152094250666567">"Fechar planilha"</string>
<string name="default_error_message" msgid="8038256446254964252">"Entrada inválida"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Janela pop-up"</string>
<string name="range_start" msgid="7097486360902471446">"Início do intervalo"</string>
<string name="range_end" msgid="5941395253238309765">"Fim do intervalo"</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 c77ce8e..e9061ec 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
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Fechar menu de navegação"</string>
<string name="close_sheet" msgid="7573152094250666567">"Fechar folha"</string>
<string name="default_error_message" msgid="8038256446254964252">"Entrada inválida"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Janela pop-up"</string>
<string name="range_start" msgid="7097486360902471446">"Início do intervalo"</string>
<string name="range_end" msgid="5941395253238309765">"Fim do intervalo"</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 0c70403..e5d9ff3 100644
--- a/compose/ui/ui/src/androidMain/res/values-pt/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-pt/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Fechar menu de navegação"</string>
<string name="close_sheet" msgid="7573152094250666567">"Fechar planilha"</string>
<string name="default_error_message" msgid="8038256446254964252">"Entrada inválida"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Janela pop-up"</string>
<string name="range_start" msgid="7097486360902471446">"Início do intervalo"</string>
<string name="range_end" msgid="5941395253238309765">"Fim do intervalo"</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 c2da926..7e8a0dc 100644
--- a/compose/ui/ui/src/androidMain/res/values-ro/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ro/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Închide meniul de navigare"</string>
<string name="close_sheet" msgid="7573152094250666567">"Închide foaia"</string>
<string name="default_error_message" msgid="8038256446254964252">"Intrare nevalidă"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Fereastră pop-up"</string>
<string name="range_start" msgid="7097486360902471446">"Început de interval"</string>
<string name="range_end" msgid="5941395253238309765">"Sfârșit de interval"</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 3b9a833..218c2c1 100644
--- a/compose/ui/ui/src/androidMain/res/values-ru/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ru/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Закрыть меню навигации"</string>
<string name="close_sheet" msgid="7573152094250666567">"Закрыть лист"</string>
<string name="default_error_message" msgid="8038256446254964252">"Неправильный ввод"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Всплывающее окно"</string>
<string name="range_start" msgid="7097486360902471446">"Начало диапазона"</string>
<string name="range_end" msgid="5941395253238309765">"Конец диапазона"</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 ebf3e39..787156c 100644
--- a/compose/ui/ui/src/androidMain/res/values-si/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-si/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"සංචාලන මෙනුව වසන්න"</string>
<string name="close_sheet" msgid="7573152094250666567">"පත්රය වසන්න"</string>
<string name="default_error_message" msgid="8038256446254964252">"වලංගු නොවන ආදානයකි"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"උත්පතන කවුළුව"</string>
<string name="range_start" msgid="7097486360902471446">"පරාස ආරම්භය"</string>
<string name="range_end" msgid="5941395253238309765">"පරාස අන්තය"</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 d576b04..0b28e8c 100644
--- a/compose/ui/ui/src/androidMain/res/values-sk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sk/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Zavrieť navigačnú ponuku"</string>
<string name="close_sheet" msgid="7573152094250666567">"Zavrieť hárok"</string>
<string name="default_error_message" msgid="8038256446254964252">"Neplatný vstup"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Vyskakovacie okno"</string>
<string name="range_start" msgid="7097486360902471446">"Začiatok rozsahu"</string>
<string name="range_end" msgid="5941395253238309765">"Koniec rozsahu"</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 02e0df2..eb9301f5 100644
--- a/compose/ui/ui/src/androidMain/res/values-sl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sl/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Zapri meni za krmarjenje"</string>
<string name="close_sheet" msgid="7573152094250666567">"Zapri list"</string>
<string name="default_error_message" msgid="8038256446254964252">"Neveljaven vnos"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Pojavno okno"</string>
<string name="range_start" msgid="7097486360902471446">"Začetek razpona"</string>
<string name="range_end" msgid="5941395253238309765">"Konec razpona"</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 7d26d4c..a188d25 100644
--- a/compose/ui/ui/src/androidMain/res/values-sq/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sq/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Mbyll menynë e navigimit"</string>
<string name="close_sheet" msgid="7573152094250666567">"Mbyll fletën"</string>
<string name="default_error_message" msgid="8038256446254964252">"Hyrje e pavlefshme"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Dritare kërcyese"</string>
<string name="range_start" msgid="7097486360902471446">"Fillimi i diapazonit"</string>
<string name="range_end" msgid="5941395253238309765">"Fundi i diapazonit"</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 3502abf..9708e29 100644
--- a/compose/ui/ui/src/androidMain/res/values-sr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sr/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Затвори мени за навигацију"</string>
<string name="close_sheet" msgid="7573152094250666567">"Затворите табелу"</string>
<string name="default_error_message" msgid="8038256446254964252">"Унос је неважећи"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Искачући прозор"</string>
<string name="range_start" msgid="7097486360902471446">"Почетак опсега"</string>
<string name="range_end" msgid="5941395253238309765">"Крај опсега"</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 f005131b..0bf933b 100644
--- a/compose/ui/ui/src/androidMain/res/values-sv/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sv/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Stäng navigeringsmenyn"</string>
<string name="close_sheet" msgid="7573152094250666567">"Stäng kalkylarket"</string>
<string name="default_error_message" msgid="8038256446254964252">"Ogiltiga indata"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Popup-fönster"</string>
<string name="range_start" msgid="7097486360902471446">"Intervallets början"</string>
<string name="range_end" msgid="5941395253238309765">"Intervallets slut"</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 42d3e1f..a213605 100644
--- a/compose/ui/ui/src/androidMain/res/values-sw/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-sw/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Funga menyu ya kusogeza"</string>
<string name="close_sheet" msgid="7573152094250666567">"Funga laha"</string>
<string name="default_error_message" msgid="8038256446254964252">"Ulichoweka si sahihi"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Dirisha Ibukizi"</string>
<string name="range_start" msgid="7097486360902471446">"Mwanzo wa masafa"</string>
<string name="range_end" msgid="5941395253238309765">"Mwisho wa masafa"</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 8e584e7..cbaf6f8 100644
--- a/compose/ui/ui/src/androidMain/res/values-ta/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ta/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"வழிசெலுத்தல் மெனுவை மூடும்"</string>
<string name="close_sheet" msgid="7573152094250666567">"ஷீட்டை மூடும்"</string>
<string name="default_error_message" msgid="8038256446254964252">"தவறான உள்ளீடு"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"பாப்-அப் சாளரம்"</string>
<string name="range_start" msgid="7097486360902471446">"வரம்பு தொடக்கம்"</string>
<string name="range_end" msgid="5941395253238309765">"வரம்பு முடிவு"</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 cecac59e..fec67d4e 100644
--- a/compose/ui/ui/src/androidMain/res/values-te/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-te/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"నావిగేషన్ మెనూను మూసివేయండి"</string>
<string name="close_sheet" msgid="7573152094250666567">"షీట్ను మూసివేయండి"</string>
<string name="default_error_message" msgid="8038256446254964252">"ఇన్పుట్ చెల్లదు"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"పాప్-అప్ విండో"</string>
<string name="range_start" msgid="7097486360902471446">"పరిధి ప్రారంభమయింది"</string>
<string name="range_end" msgid="5941395253238309765">"పరిధి ముగిసింది"</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 3ceed7d..1391214 100644
--- a/compose/ui/ui/src/androidMain/res/values-th/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-th/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"ปิดเมนูการนำทาง"</string>
<string name="close_sheet" msgid="7573152094250666567">"ปิดชีต"</string>
<string name="default_error_message" msgid="8038256446254964252">"อินพุตไม่ถูกต้อง"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"หน้าต่างป๊อปอัป"</string>
<string name="range_start" msgid="7097486360902471446">"จุดเริ่มต้นของช่วง"</string>
<string name="range_end" msgid="5941395253238309765">"จุดสิ้นสุดของช่วง"</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 9ab42b2d..af0b7d2 100644
--- a/compose/ui/ui/src/androidMain/res/values-tl/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-tl/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Isara ang menu ng navigation"</string>
<string name="close_sheet" msgid="7573152094250666567">"Isara ang sheet"</string>
<string name="default_error_message" msgid="8038256446254964252">"Invalid na input"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Window ng Pop-Up"</string>
<string name="range_start" msgid="7097486360902471446">"Simula ng range"</string>
<string name="range_end" msgid="5941395253238309765">"Katapusan ng range"</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 909d057..aa8f590 100644
--- a/compose/ui/ui/src/androidMain/res/values-tr/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-tr/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Gezinme menüsünü kapat"</string>
<string name="close_sheet" msgid="7573152094250666567">"Sayfayı kapat"</string>
<string name="default_error_message" msgid="8038256446254964252">"Geçersiz giriş"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Pop-up Pencere"</string>
<string name="range_start" msgid="7097486360902471446">"Aralık başlangıcı"</string>
<string name="range_end" msgid="5941395253238309765">"Aralık sonu"</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 2cec971..b93fba07 100644
--- a/compose/ui/ui/src/androidMain/res/values-uk/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-uk/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Закрити меню навігації"</string>
<string name="close_sheet" msgid="7573152094250666567">"Закрити аркуш"</string>
<string name="default_error_message" msgid="8038256446254964252">"Введено недійсні дані"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Спливаюче вікно"</string>
<string name="range_start" msgid="7097486360902471446">"Початок діапазону"</string>
<string name="range_end" msgid="5941395253238309765">"Кінець діапазону"</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 e9c0509..7ee15a1 100644
--- a/compose/ui/ui/src/androidMain/res/values-ur/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-ur/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"نیویگیشن مینیو بند کریں"</string>
<string name="close_sheet" msgid="7573152094250666567">"شیٹ بند کریں"</string>
<string name="default_error_message" msgid="8038256446254964252">"غلط ان پٹ"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"پاپ اپ ونڈو"</string>
<string name="range_start" msgid="7097486360902471446">"رینج کی شروعات"</string>
<string name="range_end" msgid="5941395253238309765">"رینج کا اختتام"</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 7115c76..8ccc776 100644
--- a/compose/ui/ui/src/androidMain/res/values-uz/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-uz/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Navigatsiya menyusini yopish"</string>
<string name="close_sheet" msgid="7573152094250666567">"Varaqni yopish"</string>
<string name="default_error_message" msgid="8038256446254964252">"Kiritilgan axborot xato"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Qalqib chiquvchi oyna"</string>
<string name="range_start" msgid="7097486360902471446">"Oraliq boshi"</string>
<string name="range_end" msgid="5941395253238309765">"Oraliq oxiri"</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 a7728063..1313dc4 100644
--- a/compose/ui/ui/src/androidMain/res/values-vi/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-vi/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Đóng trình đơn điều hướng"</string>
<string name="close_sheet" msgid="7573152094250666567">"Đóng trang tính"</string>
<string name="default_error_message" msgid="8038256446254964252">"Giá trị nhập không hợp lệ"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Cửa sổ bật lên"</string>
<string name="range_start" msgid="7097486360902471446">"Điểm bắt đầu phạm vi"</string>
<string name="range_end" msgid="5941395253238309765">"Điểm kết thúc phạm vi"</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 2145293..b7031c6 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
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"关闭导航菜单"</string>
<string name="close_sheet" msgid="7573152094250666567">"关闭工作表"</string>
<string name="default_error_message" msgid="8038256446254964252">"输入无效"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"弹出式窗口"</string>
<string name="range_start" msgid="7097486360902471446">"范围起点"</string>
<string name="range_end" msgid="5941395253238309765">"范围终点"</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 a5804a8..155d6d3 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
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"閂導覽選單"</string>
<string name="close_sheet" msgid="7573152094250666567">"閂表單"</string>
<string name="default_error_message" msgid="8038256446254964252">"輸入嘅資料無效"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"彈出式視窗"</string>
<string name="range_start" msgid="7097486360902471446">"範圍開始"</string>
<string name="range_end" msgid="5941395253238309765">"範圍結束"</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 36aa760..198d101 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
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"關閉導覽選單"</string>
<string name="close_sheet" msgid="7573152094250666567">"關閉功能表"</string>
<string name="default_error_message" msgid="8038256446254964252">"輸入內容無效"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"彈出式視窗"</string>
<string name="range_start" msgid="7097486360902471446">"範圍起點"</string>
<string name="range_end" msgid="5941395253238309765">"範圍終點"</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 3237ddb..54fca1b 100644
--- a/compose/ui/ui/src/androidMain/res/values-zu/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values-zu/strings.xml
@@ -31,6 +31,8 @@
<string name="close_drawer" msgid="406453423630273620">"Vala imenyu yokuzulazula"</string>
<string name="close_sheet" msgid="7573152094250666567">"Vala ishidi"</string>
<string name="default_error_message" msgid="8038256446254964252">"Okufakwayo okungalungile"</string>
+ <!-- no translation found for state_empty (4139871816613051306) -->
+ <skip />
<string name="default_popup_window_title" msgid="6312721426453364202">"Iwindi Lesikhashana"</string>
<string name="range_start" msgid="7097486360902471446">"Ukuqala kobubanzi"</string>
<string name="range_end" msgid="5941395253238309765">"Umkhawulo wobubanzi"</string>
diff --git a/compose/ui/ui/src/androidMain/res/values/strings.xml b/compose/ui/ui/src/androidMain/res/values/strings.xml
index 5d955a7..ae60b26 100644
--- a/compose/ui/ui/src/androidMain/res/values/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values/strings.xml
@@ -46,6 +46,8 @@
<string name="close_sheet">"Close sheet"</string>
<!-- Default accessibility error text for an editable text field. -->
<string name="default_error_message">"Invalid input"</string>
+ <!-- Spoken description of an empty editable text field -->
+ <string name="state_empty">Empty</string>
<!-- Default title of the popup window to be read by a screen reader. -->
<string name="default_popup_window_title" tools:ignore="ExtraTranslation">"Pop-Up Window"</string>
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index bb76634..7fea049 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -652,7 +652,7 @@
fun testRemoveBeyondIndex() {
val node = LayoutNode()
node.insertAt(0, LayoutNode())
- Assert.assertThrows(IndexOutOfBoundsException::class.java) {
+ Assert.assertThrows(NullPointerException::class.java) {
node.removeAt(1, 1)
}
}
@@ -672,7 +672,7 @@
fun testRemoveWithIndexBeyondSize() {
val node = LayoutNode()
node.insertAt(0, LayoutNode())
- Assert.assertThrows(IndexOutOfBoundsException::class.java) {
+ Assert.assertThrows(NullPointerException::class.java) {
node.removeAt(0, 2)
}
}
@@ -681,7 +681,7 @@
@Test
fun testRemoveWithIndexEqualToSize() {
val node = LayoutNode()
- Assert.assertThrows(IndexOutOfBoundsException::class.java) {
+ Assert.assertThrows(NullPointerException::class.java) {
node.removeAt(0, 1)
}
}
@@ -2688,11 +2688,7 @@
val transform = Matrix()
val inverseTransform = Matrix()
return object : OwnedLayer {
- override fun updateLayerProperties(
- scope: ReusableGraphicsLayerScope,
- layoutDirection: LayoutDirection,
- density: Density
- ) {
+ override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {
transform.reset()
// This is not expected to be 100% accurate
transform.scale(scope.scaleX, scope.scaleY)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
index e9a8947..f387981 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
@@ -49,6 +49,7 @@
/**
* Paint the content using [painter].
*
+ * @param painter [Painter] to be drawn by this [Modifier]
* @param sizeToIntrinsics `true` to size the element relative to [Painter.intrinsicSize]
* @param alignment specifies alignment of the [painter] relative to content
* @param contentScale strategy for scaling [painter] if its size does not match the content size
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/Shadow.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/Shadow.kt
index f3703e0..e48553d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/Shadow.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/Shadow.kt
@@ -82,6 +82,9 @@
* Use a [androidx.compose.ui.zIndex] modifier if you want to draw the elements with larger
* [elevation] after all the elements with a smaller one.
*
+ * Note that this parameter is only supported on Android 9 (Pie) and above. On older versions,
+ * this property always returns [Color.Black] and setting new values is ignored.
+ *
* Usage of this API renders this composable into a separate graphics layer
* @see graphicsLayer
*
@@ -92,6 +95,8 @@
* @param elevation The elevation for the shadow in pixels
* @param shape Defines a shape of the physical object
* @param clip When active, the content drawing clips to the shape.
+ * @param ambientColor Color of the ambient shadow drawn when [elevation] > 0f
+ * @param spotColor Color of the spot shadow that is drawn when [elevation] > 0f
*/
@Stable
fun Modifier.shadow(
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
index 0e5badf..0f463e4 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
@@ -32,7 +32,6 @@
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.Nodes
import androidx.compose.ui.node.ObserverModifierNode
-import androidx.compose.ui.node.dispatchForKind
import androidx.compose.ui.node.observeReads
import androidx.compose.ui.node.requireOwner
import androidx.compose.ui.node.visitAncestors
@@ -81,8 +80,8 @@
/**
* Clears focus if this focus target has it.
*/
- override fun onReset() {
- // Note: onReset() is called after onEndApplyChanges, so we can't schedule any nodes for
+ override fun onDetach() {
+ // Note: this is called after onEndApplyChanges, so we can't schedule any nodes for
// invalidation here. If we do, they will be run on the next onEndApplyChanges.
when (focusState) {
// Clear focus from the current FocusTarget.
@@ -203,31 +202,6 @@
}
}
- internal fun scheduleInvalidationForFocusEvents() {
- // Since this is potentially called while _this_ node is getting detached, it is possible
- // that the nodes above us are already detached, thus, we check for isAttached here.
- // We should investigate changing the order that children.detach() is called relative to
- // actually nulling out / detaching ones self.
- visitAncestors(
- mask = Nodes.FocusEvent or Nodes.FocusTarget,
- includeSelf = true
- ) {
- // We want invalidation to propagate until the next focus target in the hierarchy, but
- // if the current node is both a FocusEvent and FocusTarget node, we still want to
- // visit this node and invalidate the focus event nodes. This case is not recommended,
- // using the state from the FocusTarget node directly is preferred to the indirection of
- // listening to events from the state you already own, but we should support this case
- // anyway to be safe.
- if (it !== this.node && it.isKind(Nodes.FocusTarget)) return@visitAncestors
-
- if (it.isAttached) {
- it.dispatchForKind(Nodes.FocusEvent) { eventNode ->
- eventNode.invalidateFocusEvent()
- }
- }
- }
- }
-
internal object FocusTargetElement : ModifierNodeElement<FocusTargetNode>() {
override fun create() = FocusTargetNode()
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope.kt
index 9cf5728..ca8fd83 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope.kt
@@ -16,6 +16,7 @@
package androidx.compose.ui.graphics
+import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ComposableOpenTarget
import androidx.compose.runtime.RememberObserver
@@ -26,6 +27,7 @@
import androidx.compose.ui.layout.PlacementScopeMarker
import androidx.compose.ui.platform.LocalGraphicsContext
import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
/**
* Default camera distance for all layers
@@ -411,6 +413,8 @@
internal var graphicsDensity: Density = Density(1.0f)
+ internal var layoutDirection: LayoutDirection = LayoutDirection.Ltr
+
override val density: Float
get() = graphicsDensity.density
@@ -425,6 +429,10 @@
}
}
+ internal var outline: Outline? = null
+ @VisibleForTesting
+ internal set
+
fun reset() {
scaleX = 1f
scaleY = 1f
@@ -444,7 +452,12 @@
renderEffect = null
compositingStrategy = CompositingStrategy.Auto
size = Size.Unspecified
+ outline = null
// mutatedFields should be reset last as all the setters above modify it.
mutatedFields = 0
}
+
+ internal fun updateOutline() {
+ outline = shape.createOutline(size, layoutDirection, graphicsDensity)
+ }
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt
index c3995f3..b678844 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt
@@ -20,6 +20,7 @@
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathFillType
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
@@ -285,7 +286,7 @@
* @param trimPathStart specifies the fraction of the path to trim from the start in the
* range from 0 to 1. Values outside the range will wrap around the length of the path.
* Default is 0.
- * @param trimPathStart specifies the fraction of the path to trim from the end in the
+ * @param trimPathEnd specifies the fraction of the path to trim from the end in the
* range from 0 to 1. Values outside the range will wrap around the length of the path.
* Default is 1.
* @param trimPathOffset specifies the fraction to shift the path trim region in the range
@@ -701,6 +702,8 @@
* @param strokeLineCap specifies the linecap for a stroked path
* @param strokeLineJoin specifies the linejoin for a stroked path
* @param strokeLineMiter specifies the miter limit for a stroked path
+ * @param pathFillType specifies the winding rule that decides how the interior of a [Path] is
+ * calculated.
* @param pathBuilder [PathBuilder] lambda for adding [PathNode]s to this path.
*/
inline fun ImageVector.Builder.path(
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt
index c381de4..3071bfc 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt
@@ -112,6 +112,8 @@
* @param [name] optional identifier used to identify the root of this vector graphic
* @param [tintColor] optional color used to tint the root group of this vector graphic
* @param [tintBlendMode] BlendMode used in combination with [tintColor]
+ * @param [autoMirror] Determines if the contents of the Vector should be mirrored for right to left
+ * layouts.
* @param [content] Composable used to define the structure and contents of the vector graphic
*/
@Composable
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt
index f442482..d84b8d3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/HitPathTracker.kt
@@ -17,6 +17,9 @@
package androidx.compose.ui.input.pointer
import androidx.collection.LongSparseArray
+import androidx.collection.MutableLongObjectMap
+import androidx.collection.MutableObjectList
+import androidx.collection.mutableObjectListOf
import androidx.compose.runtime.collection.MutableVector
import androidx.compose.runtime.collection.mutableVectorOf
import androidx.compose.ui.ExperimentalComposeUiApi
@@ -42,8 +45,7 @@
/*@VisibleForTesting*/
internal val root: NodeParent = NodeParent()
- // Only used when removing duplicate Nodes from the Node tree ([removeDuplicateNode]).
- private val vectorForHandlingDuplicateNodes: MutableVector<NodeParent> = mutableVectorOf()
+ private val hitPointerIdsAndNodes = MutableLongObjectMap<MutableObjectList<Node>>(10)
/**
* Associates a [pointerId] to a list of hit [pointerInputNodes] and keeps track of them.
@@ -56,21 +58,34 @@
* @param pointerId The id of the pointer that was hit tested against [PointerInputFilter]s
* @param pointerInputNodes The [PointerInputFilter]s that were hit by [pointerId]. Must be
* ordered from ancestor to descendant.
+ * @param prunePointerIdsAndChangesNotInNodesList Prune [PointerId]s (and associated changes)
+ * that are NOT in the pointerInputNodes parameter from the cached tree of ParentNode/Node.
*/
- fun addHitPath(pointerId: PointerId, pointerInputNodes: List<Modifier.Node>) {
+ fun addHitPath(
+ pointerId: PointerId,
+ pointerInputNodes: List<Modifier.Node>,
+ prunePointerIdsAndChangesNotInNodesList: Boolean = false
+ ) {
var parent: NodeParent = root
+ hitPointerIdsAndNodes.clear()
var merging = true
- var nodeBranchPathToSkipDuringDuplicateNodeRemoval: Node? = null
eachPin@ for (i in pointerInputNodes.indices) {
val pointerInputNode = pointerInputNodes[i]
+
if (merging) {
val node = parent.children.firstOrNull {
it.modifierNode == pointerInputNode
}
+
if (node != null) {
node.markIsIn()
node.pointerIds.add(pointerId)
+
+ val mutableObjectList =
+ hitPointerIdsAndNodes.getOrPut(pointerId.value) { mutableObjectListOf() }
+
+ mutableObjectList.add(node)
parent = node
continue@eachPin
} else {
@@ -82,52 +97,30 @@
pointerIds.add(pointerId)
}
- if (nodeBranchPathToSkipDuringDuplicateNodeRemoval == null) {
- // Null means this is the first new Node created that will need a new branch path
- // (possibly from a pre-existing cached version of the node chain).
- // If that is the case, we need to skip this path when looking for duplicate
- // nodes to remove (that may have previously existed somewhere else in the tree).
- nodeBranchPathToSkipDuringDuplicateNodeRemoval = node
- } else {
- // Every node after the top new node (that is, the top Node in the new path)
- // could have potentially existed somewhere else in the cached node tree, and
- // we need to remove it if we are adding it to this new branch.
- removeDuplicateNode(node, nodeBranchPathToSkipDuringDuplicateNodeRemoval)
- }
+ val mutableObjectList =
+ hitPointerIdsAndNodes.getOrPut(pointerId.value) { mutableObjectListOf() }
+
+ mutableObjectList.add(node)
parent.children.add(node)
parent = node
}
- }
- /*
- * Removes duplicate nodes when using a cached version of the node tree. Uses breadth-first
- * search for simplicity (and because the tree will be very small).
- */
- private fun removeDuplicateNode(
- duplicateNodeToRemove: Node,
- headOfPathToSkip: Node
- ) {
- vectorForHandlingDuplicateNodes.clear()
- vectorForHandlingDuplicateNodes.add(root)
-
- while (vectorForHandlingDuplicateNodes.isNotEmpty()) {
- val parent = vectorForHandlingDuplicateNodes.removeAt(0)
-
- for (index in parent.children.indices) {
- val child = parent.children[index]
- if (child == headOfPathToSkip) continue
- if (child.modifierNode == duplicateNodeToRemove.modifierNode) {
- // Assumes there is only one unique Node in the tree (not copies).
- // This also removes all children attached below the node.
- parent.children.remove(child)
- return
- }
- vectorForHandlingDuplicateNodes.add(child)
+ if (prunePointerIdsAndChangesNotInNodesList) {
+ hitPointerIdsAndNodes.forEach { key, value ->
+ removeInvalidPointerIdsAndChanges(key, value)
}
}
}
+ // Removes pointers/changes that are not in the latest hit test
+ private fun removeInvalidPointerIdsAndChanges(
+ pointerId: Long,
+ hitNodes: MutableObjectList<Node>
+ ) {
+ root.removeInvalidPointerIdsAndChanges(pointerId, hitNodes)
+ }
+
/**
* Dispatches [internalPointerEvent] through the hierarchy.
*
@@ -175,13 +168,13 @@
}
/**
- * Removes [PointerInputFilter]s that have been removed from the component tree.
+ * Removes detached Pointer Input Modifier Nodes.
*/
// TODO(shepshapard): Ideally, we can process the detaching of PointerInputFilters at the time
// that either their associated LayoutNode is removed from the three, or their
// associated PointerInputModifier is removed from a LayoutNode.
- fun removeDetachedPointerInputFilters() {
- root.removeDetachedPointerInputFilters()
+ fun removeDetachedPointerInputNodes() {
+ root.removeDetachedPointerInputModifierNodes()
}
}
@@ -272,19 +265,29 @@
children.clear()
}
+ open fun removeInvalidPointerIdsAndChanges(
+ pointerIdValue: Long,
+ hitNodes: MutableObjectList<Node>
+ ) {
+ children.forEach {
+ it.removeInvalidPointerIdsAndChanges(pointerIdValue, hitNodes)
+ }
+ }
+
/**
* Removes all child [Node]s that are no longer attached to the compose tree.
*/
- fun removeDetachedPointerInputFilters() {
+ fun removeDetachedPointerInputModifierNodes() {
var index = 0
while (index < children.size) {
val child = children[index]
+
if (!child.modifierNode.isAttached) {
- children.removeAt(index)
child.dispatchCancel()
+ children.removeAt(index)
} else {
index++
- child.removeDetachedPointerInputFilters()
+ child.removeDetachedPointerInputModifierNodes()
}
}
}
@@ -327,6 +330,22 @@
private var isIn = true
private var hasExited = true
+ override fun removeInvalidPointerIdsAndChanges(
+ pointerIdValue: Long,
+ hitNodes: MutableObjectList<Node>
+ ) {
+ if (this.pointerIds.contains(pointerIdValue)) {
+ if (!hitNodes.contains(this)) {
+ this.pointerIds.remove(pointerIdValue)
+ this.relevantChanges.remove(pointerIdValue)
+ }
+ }
+
+ children.forEach {
+ it.removeInvalidPointerIdsAndChanges(pointerIdValue, hitNodes)
+ }
+ }
+
override fun dispatchMainEventPass(
changes: LongSparseArray<PointerInputChange>,
parentCoordinates: LayoutCoordinates,
@@ -342,6 +361,7 @@
return dispatchIfNeeded {
val event = pointerEvent!!
val size = coordinates!!.size
+
// Dispatch on the tunneling pass.
modifierNode.dispatchForKind(Nodes.PointerInput) {
it.onPointerEvent(event, PointerEventPass.Initial, size)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.kt
index f2b624c..61e45fa 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.kt
@@ -168,24 +168,37 @@
if (pass == Main) {
// Cursor within the surface area of this node's bounds
if (pointerEvent.type == PointerEventType.Enter) {
- cursorInBoundsOfNode = true
- displayIconIfDescendantsDoNotHavePriority()
+ onEnter()
} else if (pointerEvent.type == PointerEventType.Exit) {
- cursorInBoundsOfNode = false
+ onExit()
+ }
+ }
+ }
+
+ private fun onEnter() {
+ cursorInBoundsOfNode = true
+ displayIconIfDescendantsDoNotHavePriority()
+ }
+
+ private fun onExit() {
+ if (cursorInBoundsOfNode) {
+ cursorInBoundsOfNode = false
+
+ if (isAttached) {
displayIconFromAncestorNodeWithCursorInBoundsOrDefaultIcon()
}
}
}
override fun onCancelPointerInput() {
- // We aren't processing the event (only listening for enter/exit), so we don't need to
- // do anything.
+ // While pointer icon only really cares about enter/exit, there are some cases (dynamically
+ // adding Modifier Nodes) where a modifier might be cancelled but hasn't been detached or
+ // exited, so we need to cover that case.
+ onExit()
}
override fun onDetach() {
- cursorInBoundsOfNode = false
- displayIconFromAncestorNodeWithCursorInBoundsOrDefaultIcon()
-
+ onExit()
super.onDetach()
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
index 4230b9c..e514e78 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
@@ -98,15 +98,22 @@
val isTouchEvent = pointerInputChange.type == PointerType.Touch
root.hitTest(pointerInputChange.position, hitResult, isTouchEvent)
if (hitResult.isNotEmpty()) {
- hitPathTracker.addHitPath(pointerInputChange.id, hitResult)
+ hitPathTracker.addHitPath(
+ pointerId = pointerInputChange.id,
+ pointerInputNodes = hitResult,
+ // Prunes PointerIds (and changes) to support dynamically
+ // adding/removing pointer input modifier nodes.
+ // Note: We do not do this for hover because hover relies on those
+ // non hit PointerIds to trigger hover exit events.
+ prunePointerIdsAndChangesNotInNodesList =
+ pointerInputChange.changedToDownIgnoreConsumed()
+ )
hitResult.clear()
}
}
}
- // Remove [PointerInputFilter]s that are no longer valid and refresh the offset information
- // for those that are.
- hitPathTracker.removeDetachedPointerInputFilters()
+ hitPathTracker.removeDetachedPointerInputNodes()
// Dispatch to PointerInputFilters
val dispatchedToSomething =
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.kt
index 96fe0b4..ba1f7d5 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.kt
@@ -58,17 +58,29 @@
val isAttached: Boolean
/**
- * Whether the coordinates were placed under direct manipulation.
+ * By default, most [LayoutCoordinates] introduce a new frame of reference. In this context, a
+ * frame of reference defines a point of hierarchical change, where other [LayoutCoordinates]
+ * are positioned against.
*
- * When true, reading [localPositionOf] coordinates with `excludeDirectManipulation = true` will
- * exclude the offset set by its parent. This also applies when reading coordinates from a
- * parent further up the tree, meaning, all the layouts which have this flag as `true` will not
- * report the offset from their parent.
+ * However, there are some layouts that may visually change the frame of reference, but not
+ * hierarchically, such as Scroll. These Layouts should place their children using
+ * [Placeable.PlacementScope.withCurrentFrameOfReferencePlacement].
*
- * @see Placeable.PlacementScope.withDirectManipulationPlacement
- * @see localPositionOf
+ * In those situations, the corresponding placed [LayoutCoordinates] will have their
+ * [introducesFrameOfReference] return false.
+ *
+ * You may then use [positionInLocalFrameOfReference] to query a layout's position such that it
+ * excludes all Offset that do not introduce a frame of reference.
+ *
+ * This is typically helpful when deciding when to animate an [approachLayout] using
+ * [LookaheadScope] coordinates. As you probably don't want to consider positional changes that
+ * don't affect the layout hierarchy.
+ *
+ * @see Placeable.PlacementScope.withCurrentFrameOfReferencePlacement
+ * @see positionInLocalFrameOfReference
*/
- val isPositionedByParentWithDirectManipulation: Boolean get() = false
+ @Suppress("GetterSetterNames") // Preferred name
+ val introducesFrameOfReference: Boolean get() = true
/**
* Converts [relativeToScreen] relative to the device's screen's origin into an [Offset]
@@ -107,24 +119,18 @@
fun localPositionOf(sourceCoordinates: LayoutCoordinates, relativeToSource: Offset): Offset
/**
- * Converts an [relativeToSource] in [sourceCoordinates] space into local coordinates.
- * [sourceCoordinates] may be any [LayoutCoordinates] that belong to the same
- * compose layout hierarchy.
+ * Converts an [relativeToSource] in [sourceCoordinates] space into local coordinates, such that
+ * the offset introduced on [LayoutCoordinates] where [introducesFrameOfReference] is false is
+ * excluded.
*
- * If [excludeDirectManipulationOffset] is true, the offset provided by layouts using
- * [Placeable.PlacementScope.withDirectManipulationPlacement] will be ignored.
- *
- * You can query if a [LayoutCoordinates] was placed with
- * [Placeable.PlacementScope.withDirectManipulationPlacement] through
- * [LayoutCoordinates.isPositionedByParentWithDirectManipulation].
+ * @see Placeable.PlacementScope.withCurrentFrameOfReferencePlacement
*/
- fun localPositionOf(
+ fun positionInLocalFrameOfReference(
sourceCoordinates: LayoutCoordinates,
relativeToSource: Offset,
- excludeDirectManipulationOffset: Boolean
): Offset {
throw UnsupportedOperationException(
- "localPositionOf is not implemented on this LayoutCoordinates"
+ "positionInLocalFrameOfReference is not implemented on this LayoutCoordinates"
)
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt
index 569c7b6..639920d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt
@@ -55,8 +55,8 @@
override val isAttached: Boolean
get() = coordinator.isAttached
- override val isPositionedByParentWithDirectManipulation: Boolean
- get() = lookaheadDelegate.isDirectManipulationPlacement
+ override val introducesFrameOfReference: Boolean
+ get() = !lookaheadDelegate.isPlacedUsingCurrentFrameOfReference
private val lookaheadOffset: Offset
get() = lookaheadDelegate.rootLookaheadDelegate.let {
@@ -89,7 +89,23 @@
excludeDirectManipulationOffset = false
)
- override fun localPositionOf(
+ override fun positionInLocalFrameOfReference(
+ sourceCoordinates: LayoutCoordinates,
+ relativeToSource: Offset
+ ): Offset = localPositionOf(
+ sourceCoordinates = sourceCoordinates,
+ relativeToSource = relativeToSource,
+ excludeDirectManipulationOffset = true
+ )
+
+ /**
+ * Handles local position calculation.
+ *
+ * Pass [excludeDirectManipulationOffset] as true, to exclude offsets placed under
+ * [Placeable.PlacementScope.withCurrentFrameOfReferencePlacement]. It's expected to be true for
+ * calls coming from [positionInLocalFrameOfReference].
+ */
+ internal fun localPositionOf(
sourceCoordinates: LayoutCoordinates,
relativeToSource: Offset,
excludeDirectManipulationOffset: Boolean
@@ -141,19 +157,24 @@
// `sourceCoordinates` isn't. Therefore we'll break this into two parts:
// local position in lookahead coords space && local position in regular layout coords
// space.
- val foo = localPositionOf(
+ val localLookaheadPos = localPositionOf(
sourceCoordinates = rootDelegate.lookaheadLayoutCoordinates,
relativeToSource = relativeToSource,
excludeDirectManipulationOffset = excludeDirectManipulationOffset
)
- val bar = rootDelegate.coordinator.coordinates.localPositionOf(
- sourceCoordinates = sourceCoordinates,
- relativeToSource = Offset.Zero,
- excludeDirectManipulationOffset = excludeDirectManipulationOffset
- )
-
- return foo + bar
+ val localPos = if (excludeDirectManipulationOffset) {
+ rootDelegate.coordinator.coordinates.positionInLocalFrameOfReference(
+ sourceCoordinates = sourceCoordinates,
+ relativeToSource = Offset.Zero,
+ )
+ } else {
+ rootDelegate.coordinator.coordinates.localPositionOf(
+ sourceCoordinates = sourceCoordinates,
+ relativeToSource = Offset.Zero
+ )
+ }
+ return localLookaheadPos + localPos
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt
index cbe2216..66efabf 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt
@@ -14,15 +14,12 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalComposeUiApi::class)
-
package androidx.compose.ui.layout
import androidx.compose.runtime.Applier
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReusableComposeNode
import androidx.compose.runtime.remember
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.UiComposable
import androidx.compose.ui.geometry.Offset
@@ -158,7 +155,6 @@
}
}
-@OptIn(ExperimentalComposeUiApi::class)
private class ApproachLayoutModifierNodeImpl(
var measureBlock: ApproachMeasureScope.(
measurable: Measurable,
@@ -197,57 +193,108 @@
*/
interface LookaheadScope {
/**
- * Converts a [LayoutCoordinates] into a [LayoutCoordinates] in the Lookahead coordinates space.
- * This is only applicable to child layouts within [LookaheadScope].
+ * Converts a [LayoutCoordinates] into a [LayoutCoordinates] in the Lookahead coordinate space.
+ * This can be used for layouts within [LookaheadScope].
*/
- @ExperimentalComposeUiApi
fun LayoutCoordinates.toLookaheadCoordinates(): LayoutCoordinates
/**
* Returns the [LayoutCoordinates] of the [LookaheadScope]. This is
* only accessible from [Placeable.PlacementScope] (i.e. during placement time).
+ *
+ * Note: The returned coordinates is **not** coordinates in the lookahead coordinate space.
+ * If the lookahead coordinates of the lookaheadScope is needed, suggest converting the
+ * returned coordinates using [toLookaheadCoordinates].
*/
- @ExperimentalComposeUiApi
val Placeable.PlacementScope.lookaheadScopeCoordinates: LayoutCoordinates
/**
- * Calculates the localPosition in the Lookahead coordinate space. This is a convenient
- * method for 1) converting the given [LayoutCoordinates] to lookahead coordinates using
+ * Converts [relativeToSource] in [sourceCoordinates]'s lookahead coordinate space into
+ * local lookahead coordinates. This is a convenient method for 1) converting both [this]
+ * coordinates and [sourceCoordinates] into lookahead space coordinates using
* [toLookaheadCoordinates], and 2) invoking [LayoutCoordinates.localPositionOf] with the
* converted coordinates.
+ *
+ * For layouts where [LayoutCoordinates.introducesFrameOfReference] returns false (placed under
+ * [Placeable.PlacementScope.withCurrentFrameOfReferencePlacement]) you may use
+ * [positionInLocalLookaheadFrameOfReference] to get their position while excluding the
+ * additional Offset.
*/
- @ExperimentalComposeUiApi
fun LayoutCoordinates.localLookaheadPositionOf(
- coordinates: LayoutCoordinates,
- excludeDirectManipulationOffset: Boolean = false,
- ): Offset {
- val lookaheadCoords = this.toLookaheadCoordinates()
- val source = coordinates.toLookaheadCoordinates()
+ sourceCoordinates: LayoutCoordinates,
+ relativeToSource: Offset = Offset.Zero,
+ ): Offset = localLookaheadPositionOf(
+ coordinates = this,
+ sourceCoordinates = sourceCoordinates,
+ relativeToSource = relativeToSource,
+ excludeDirectManipulationOffset = false
+ )
- return if (lookaheadCoords is LookaheadLayoutCoordinates) {
- lookaheadCoords.localPositionOf(
+ /**
+ * Similar to [localLookaheadPositionOf], converts [relativeToSource] in [sourceCoordinates]'s
+ * lookahead coordinate space into local lookahead coordinates.
+ *
+ * However, the Offset introduced on [LayoutCoordinates] when their
+ * [LayoutCoordinates.introducesFrameOfReference] property is false, will be excluded from the
+ * calculation.
+ *
+ * Those [LayoutCoordinates] correspond to when they are placed by their parent under
+ * [Placeable.PlacementScope.withCurrentFrameOfReferencePlacement], which is typically done by
+ * Layouts that change their children positioning without affecting the overall hierarchy, or
+ * they do so in small increments (such as Scroll).
+ */
+ fun LayoutCoordinates.positionInLocalLookaheadFrameOfReference(
+ sourceCoordinates: LayoutCoordinates,
+ relativeToSource: Offset = Offset.Zero,
+ ): Offset = localLookaheadPositionOf(
+ coordinates = this,
+ sourceCoordinates = sourceCoordinates,
+ relativeToSource = relativeToSource,
+ excludeDirectManipulationOffset = true
+ )
+}
+
+/**
+ * Internal implementation to handle [LookaheadScope.localLookaheadPositionOf] and
+ * [LookaheadScope.positionInLocalLookaheadFrameOfReference].
+ */
+internal fun LookaheadScope.localLookaheadPositionOf(
+ coordinates: LayoutCoordinates,
+ sourceCoordinates: LayoutCoordinates,
+ relativeToSource: Offset,
+ excludeDirectManipulationOffset: Boolean
+): Offset {
+ val lookaheadCoords = coordinates.toLookaheadCoordinates()
+ val source = sourceCoordinates.toLookaheadCoordinates()
+
+ return if (lookaheadCoords is LookaheadLayoutCoordinates) {
+ lookaheadCoords.localPositionOf(
+ sourceCoordinates = source,
+ relativeToSource = relativeToSource,
+ excludeDirectManipulationOffset = excludeDirectManipulationOffset
+ )
+ } else if (source is LookaheadLayoutCoordinates) {
+ // Relative from source, so we take its negative position
+ -source.localPositionOf(
+ sourceCoordinates = lookaheadCoords,
+ relativeToSource = relativeToSource,
+ excludeDirectManipulationOffset = excludeDirectManipulationOffset
+ )
+ } else {
+ if (excludeDirectManipulationOffset) {
+ lookaheadCoords.positionInLocalFrameOfReference(
sourceCoordinates = source,
- relativeToSource = Offset.Zero,
- excludeDirectManipulationOffset = excludeDirectManipulationOffset
- )
- } else if (source is LookaheadLayoutCoordinates) {
- // Relative from source, so we take its negative position
- -source.localPositionOf(
- sourceCoordinates = lookaheadCoords,
- relativeToSource = Offset.Zero,
- excludeDirectManipulationOffset = excludeDirectManipulationOffset
+ relativeToSource = relativeToSource
)
} else {
lookaheadCoords.localPositionOf(
- sourceCoordinates = source,
- relativeToSource = Offset.Zero,
- excludeDirectManipulationOffset = excludeDirectManipulationOffset
+ sourceCoordinates = lookaheadCoords,
+ relativeToSource = relativeToSource
)
}
}
}
-@OptIn(ExperimentalComposeUiApi::class)
internal class LookaheadScopeImpl(
var scopeCoordinates: (() -> LayoutCoordinates)? = null
) : LookaheadScope {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
index ed87875..1e5f977 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
@@ -18,7 +18,7 @@
import androidx.compose.ui.graphics.GraphicsLayerScope
import androidx.compose.ui.graphics.layer.GraphicsLayer
-import androidx.compose.ui.node.DirectManipulationDelegate
+import androidx.compose.ui.node.FrameOfReferencePlacementDelegate
import androidx.compose.ui.node.LookaheadCapablePlaceable
import androidx.compose.ui.node.Owner
import androidx.compose.ui.unit.Constraints
@@ -498,35 +498,40 @@
}
/**
- * Internal indicator to know when to tag [Placeable] under direct manipulation.
+ * Internal indicator to know when to tag [Placeable] as placed on the same frame of
+ * reference.
*/
- private var directManipulationPlacement: Boolean = false
+ private var currentFrameOfReferencePlacement: Boolean = false
/**
- * [Placeable]s placed under [block] may have their position excluded for lookahead
- * coordinates, see [LookaheadLayoutCoordinates.localPositionOf] with the
- * `excludeDirectManipulation` argument.
+ * Placement done under [block], will have their [Placeable] placed on the same frame of
+ * reference as the current layout.
+ *
+ * In [LayoutCoordinates], this means that the offset introduced under [block] may be
+ * excluded when calculating positions. See
+ * [LayoutCoordinates.positionInLocalFrameOfReference].
*
* Excluding the position set by certain layouts can be helpful to trigger lookahead based
* animation when intended. The typical case are layouts that change frequently due to a
* provided value, like [scroll][androidx.compose.foundation.verticalScroll].
*/
- fun withDirectManipulationPlacement(block: PlacementScope.() -> Unit) {
- directManipulationPlacement = true
+ fun withCurrentFrameOfReferencePlacement(block: PlacementScope.() -> Unit) {
+ currentFrameOfReferencePlacement = true
block()
- directManipulationPlacement = false
+ currentFrameOfReferencePlacement = false
}
/**
- * Updates the [DirectManipulationDelegate.isDirectManipulationPlacement] flag when called
- * a [Placeable] is placed under [withDirectManipulationPlacement].
+ * Updates the [FrameOfReferencePlacementDelegate.isPlacedUsingCurrentFrameOfReference] flag when called
+ * a [Placeable] is placed under [withCurrentFrameOfReferencePlacement].
*
* Note that the Main/Lookahead pass delegate are expected to propagate the flag to the
* proper [LookaheadCapablePlaceable].
*/
private fun Placeable.handleDirectManipulationPlacement() {
- if (this is DirectManipulationDelegate) {
- this.isDirectManipulationPlacement = [email protected]
+ if (this is FrameOfReferencePlacementDelegate) {
+ this.isPlacedUsingCurrentFrameOfReference =
+ [email protected]
}
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index 710bf6c..1deef93 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -22,7 +22,6 @@
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusDirection.Companion.Exit
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.layer.GraphicsLayer
@@ -336,8 +335,10 @@
"count ($count) must be greater than 0"
}
for (i in index + count - 1 downTo index) {
+ // Call detach callbacks before removing from _foldedChildren, so the child is still
+ // visible to parents traversing downwards, such as when clearing focus.
+ onChildRemoved(_foldedChildren[i])
val child = _foldedChildren.removeAt(i)
- onChildRemoved(child)
if (DebugChanges) {
println("$child removed from $this at index $i")
}
@@ -522,7 +523,6 @@
checkPreconditionNotNull(owner) {
"Cannot detach node that is already detached! Tree: " + parent?.debugTreeToString()
}
- invalidateFocusOnDetach()
val parent = this.parent
if (parent != null) {
parent.invalidateLayer()
@@ -1123,20 +1123,6 @@
}
}
- private fun invalidateFocusOnDetach() {
- nodes.tailToHead(FocusTarget) {
- if (it.focusState.isFocused) {
- requireOwner().focusOwner.clearFocus(
- force = true,
- refreshFocusEvents = false,
- clearOwnerFocus = true,
- focusDirection = Exit
- )
- it.scheduleInvalidationForFocusEvents()
- }
- }
- }
-
internal inline fun ignoreRemeasureRequests(block: () -> Unit) {
ignoreRemeasureRequests = true
block()
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
index 59645ca..9378bdc 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
@@ -339,7 +339,7 @@
* actual measure/layout pass.
*/
inner class MeasurePassDelegate : Measurable, Placeable(), AlignmentLinesOwner,
- DirectManipulationDelegate {
+ FrameOfReferencePlacementDelegate {
/**
* Is true during [replace] invocation. Helps to differentiate between the cases when our
* parent is measuring us during the measure block, and when we are remeasured individually
@@ -773,16 +773,16 @@
* Flag to indicate when we need to propagate coordinates updates that are not related to a
* position change.
*
- * @see isDirectManipulationPlacement
+ * @see isPlacedUsingCurrentFrameOfReference
*/
private var needsCoordinatesUpdate = false
- override var isDirectManipulationPlacement: Boolean = false
+ override var isPlacedUsingCurrentFrameOfReference: Boolean = false
set(new) {
// Delegated to outerCoordinator
- val old = outerCoordinator.isDirectManipulationPlacement
+ val old = outerCoordinator.isPlacedUsingCurrentFrameOfReference
if (new != old) {
- outerCoordinator.isDirectManipulationPlacement = old
+ outerCoordinator.isPlacedUsingCurrentFrameOfReference = old
// Affects coordinates measurements
this.needsCoordinatesUpdate = true
}
@@ -1124,7 +1124,7 @@
* the lookahead pass.
*/
inner class LookaheadPassDelegate : Placeable(), Measurable, AlignmentLinesOwner,
- DirectManipulationDelegate {
+ FrameOfReferencePlacementDelegate {
/**
* Is true during [replace] invocation. Helps to differentiate between the cases when our
@@ -1458,12 +1458,12 @@
placeSelf(position, zIndex, null, layer)
}
- override var isDirectManipulationPlacement: Boolean = false
+ override var isPlacedUsingCurrentFrameOfReference: Boolean = false
set(new) {
// Delegated to outerCoordinator
- val old = outerCoordinator.lookaheadDelegate?.isDirectManipulationPlacement
+ val old = outerCoordinator.lookaheadDelegate?.isPlacedUsingCurrentFrameOfReference
if (new != old) {
- outerCoordinator.lookaheadDelegate?.isDirectManipulationPlacement = new
+ outerCoordinator.lookaheadDelegate?.isPlacedUsingCurrentFrameOfReference = new
}
field = new
}
@@ -1935,19 +1935,23 @@
/**
* Interface for layout delegates, so that they can set the
- * [LookaheadCapablePlaceable.isDirectManipulationPlacement] to the proper placeable.
+ * [LookaheadCapablePlaceable.isPlacedUsingCurrentFrameOfReference] to the proper placeable.
*/
-internal interface DirectManipulationDelegate {
+internal interface FrameOfReferencePlacementDelegate {
/**
* Called when a layout is about to be placed.
*
* The corresponding [LookaheadCapablePlaceable] should have their
- * [LookaheadCapablePlaceable.isDirectManipulationPlacement] flag updated to the given value.
+ * [LookaheadCapablePlaceable.isPlacedUsingCurrentFrameOfReference] flag updated to the given
+ * value.
*
* The placeable should be tagged such that its corresponding coordinates reflect the
- * flag in [androidx.compose.ui.layout.LayoutCoordinates.isPositionedByParentWithDirectManipulation].
+ * flag in [androidx.compose.ui.layout.LayoutCoordinates.introducesFrameOfReference]. Note that
+ * when it's placed on the current frame of reference, it means it doesn't introduce a new frame
+ * of reference.
+ *
* This also means that coordinates consumers (onPlaced readers) are expected to be updated.
*/
- var isDirectManipulationPlacement: Boolean
+ var isPlacedUsingCurrentFrameOfReference: Boolean
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt
index 7038850..e6d5e54 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt
@@ -43,7 +43,7 @@
* functionalities between the two are extracted here.
*/
internal abstract class LookaheadCapablePlaceable : Placeable(), MeasureScopeWithLayoutNode,
- DirectManipulationDelegate {
+ FrameOfReferencePlacementDelegate {
abstract val position: IntOffset
abstract val child: LookaheadCapablePlaceable?
abstract val parent: LookaheadCapablePlaceable?
@@ -53,12 +53,12 @@
private var _rulerScope: RulerScope? = null
/**
- * Indicates whether the [Placeable] was placed under direct manipulation.
+ * Indicates whether the [Placeable] was placed on the same frame of reference.
*
- * This means, that its offset may be ignored with [LookaheadLayoutCoordinates.localPositionOf],
- * using the `excludeDirectManipulationOffset` parameter.
+ * This means, that its offset may be ignored with
+ * [LookaheadLayoutCoordinates.positionInLocalFrameOfReference].
*/
- override var isDirectManipulationPlacement: Boolean = false
+ override var isPlacedUsingCurrentFrameOfReference: Boolean = false
val rulerScope: RulerScope
get() {
@@ -479,7 +479,8 @@
var aggregatedOffset = IntOffset.Zero
var lookaheadDelegate = this
while (lookaheadDelegate != ancestor) {
- if (!lookaheadDelegate.isDirectManipulationPlacement || !excludingAgnosticOffset) {
+ if (!lookaheadDelegate.isPlacedUsingCurrentFrameOfReference ||
+ !excludingAgnosticOffset) {
aggregatedOffset += lookaheadDelegate.position
}
lookaheadDelegate = lookaheadDelegate.coordinator.wrappedBy!!.lookaheadDelegate!!
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
index f797a9c..8a6b586 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
@@ -85,8 +85,8 @@
override val coordinates: LayoutCoordinates
get() = this
- override val isPositionedByParentWithDirectManipulation: Boolean
- get() = isDirectManipulationPlacement
+ override val introducesFrameOfReference: Boolean
+ get() = !isPlacedUsingCurrentFrameOfReference
private var released = false
@@ -517,18 +517,16 @@
}
graphicsLayerScope.reset()
graphicsLayerScope.graphicsDensity = layoutNode.density
+ graphicsLayerScope.layoutDirection = layoutNode.layoutDirection
graphicsLayerScope.size = size.toSize()
snapshotObserver.observeReads(this, onCommitAffectingLayerParams) {
layerBlock.invoke(graphicsLayerScope)
+ graphicsLayerScope.updateOutline()
}
val layerPositionalProperties = layerPositionalProperties
?: LayerPositionalProperties().also { layerPositionalProperties = it }
layerPositionalProperties.copyFrom(graphicsLayerScope)
- layer.updateLayerProperties(
- graphicsLayerScope,
- layoutNode.layoutDirection,
- layoutNode.density,
- )
+ layer.updateLayerProperties(graphicsLayerScope)
isClipping = graphicsLayerScope.clip
lastLayerAlpha = graphicsLayerScope.alpha
if (invokeOnLayoutChange) {
@@ -835,7 +833,15 @@
relativeToSource: Offset
): Offset = localPositionOf(sourceCoordinates, relativeToSource, false)
- override fun localPositionOf(
+ override fun positionInLocalFrameOfReference(
+ sourceCoordinates: LayoutCoordinates,
+ relativeToSource: Offset
+ ): Offset = localPositionOf(sourceCoordinates, relativeToSource, true)
+
+ /**
+ * Common call
+ */
+ internal fun localPositionOf(
sourceCoordinates: LayoutCoordinates,
relativeToSource: Offset,
excludeDirectManipulationOffset: Boolean
@@ -1000,7 +1006,7 @@
): Offset {
val layer = layer
val targetPosition = layer?.mapOffset(position, inverse = false) ?: position
- return if (excludeDirectManipulationOffset && isDirectManipulationPlacement) {
+ return if (excludeDirectManipulationOffset && isPlacedUsingCurrentFrameOfReference) {
targetPosition
} else {
targetPosition + this.position
@@ -1016,7 +1022,7 @@
excludeDirectManipulationOffset: Boolean = false
): Offset {
val relativeToPosition =
- if (excludeDirectManipulationOffset && isDirectManipulationPlacement) {
+ if (excludeDirectManipulationOffset && isPlacedUsingCurrentFrameOfReference) {
position
} else {
position - this.position
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
index e669d1e..65e06af 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
@@ -269,10 +269,18 @@
}
}
if (Nodes.LayoutAware in selfKindSet && node is LayoutAwareModifierNode) {
- node.requireLayoutNode().invalidateMeasurements()
+ // No need to invalidate layout when removing a LayoutAwareModifierNode, as these won't be
+ // invoked anyway
+ if (phase != Removed) {
+ node.requireLayoutNode().invalidateMeasurements()
+ }
}
if (Nodes.GlobalPositionAware in selfKindSet && node is GlobalPositionAwareModifierNode) {
- node.requireLayoutNode().invalidateOnPositioned()
+ // No need to invalidate when removing a GlobalPositionAwareModifierNode, as these won't be
+ // invoked anyway
+ if (phase != Removed) {
+ node.requireLayoutNode().invalidateOnPositioned()
+ }
}
if (Nodes.Draw in selfKindSet && node is DrawModifierNode) {
node.invalidateDraw()
@@ -284,12 +292,7 @@
node.invalidateParentData()
}
if (Nodes.FocusTarget in selfKindSet && node is FocusTargetNode) {
- when (phase) {
- // when we previously had focus target modifier on a node and then this modifier
- // is removed we need to notify the focus tree about so the focus state is reset.
- Removed -> node.onReset()
- else -> node.requireOwner().focusOwner.scheduleInvalidation(node)
- }
+ node.invalidateFocusTarget()
}
if (
Nodes.FocusProperties in selfKindSet &&
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OwnedLayer.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OwnedLayer.kt
index f68b387..602a375 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OwnedLayer.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OwnedLayer.kt
@@ -22,10 +22,8 @@
import androidx.compose.ui.graphics.Matrix
import androidx.compose.ui.graphics.ReusableGraphicsLayerScope
import androidx.compose.ui.graphics.layer.GraphicsLayer
-import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
/**
* A layer returned by [Owner.createLayer] to separate drawn content.
@@ -33,13 +31,9 @@
internal interface OwnedLayer {
/**
- * Applies the new layer properties and causing this layer to be redrawn.
+ * Applies the new layer properties, causing this layer to be redrawn.
*/
- fun updateLayerProperties(
- scope: ReusableGraphicsLayerScope,
- layoutDirection: LayoutDirection,
- density: Density,
- )
+ fun updateLayerProperties(scope: ReusableGraphicsLayerScope)
/**
* Returns `false` if [position] is outside the clipped region or `true` if clipping
diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/SkiaLayerTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/SkiaLayerTest.kt
index 082fff8..adc95cf 100644
--- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/SkiaLayerTest.kt
+++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/SkiaLayerTest.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.DefaultShadowColor
@@ -351,7 +352,8 @@
layer.resize(IntSize(1, 2))
layer.updateProperties(
- clip = true
+ clip = true,
+ size = Size(1f, 2f)
)
assertFalse(layer.isInLayer(Offset(-1f, -1f)))
@@ -363,7 +365,8 @@
layer.resize(IntSize(100, 200))
layer.updateProperties(
clip = true,
- shape = CircleShape
+ shape = CircleShape,
+ size = Size(100f, 200f)
)
assertFalse(layer.isInLayer(Offset(5f, 5f)))
@@ -394,7 +397,8 @@
shape: Shape = RectangleShape,
clip: Boolean = false,
renderEffect: RenderEffect? = null,
- compositingStrategy: CompositingStrategy = CompositingStrategy.Auto
+ compositingStrategy: CompositingStrategy = CompositingStrategy.Auto,
+ size: Size = Size.Zero
) {
val scope = ReusableGraphicsLayerScope()
scope.cameraDistance = cameraDistance
@@ -415,6 +419,9 @@
scope.clip = clip
scope.renderEffect = renderEffect
scope.compositingStrategy = compositingStrategy
- updateLayerProperties(scope, LayoutDirection.Ltr, Density(1f))
+ scope.layoutDirection = LayoutDirection.Ltr
+ scope.graphicsDensity = Density(1f)
+ scope.outline = shape.createOutline(size, scope.layoutDirection, scope.graphicsDensity)
+ updateLayerProperties(scope)
}
}
diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/OutlineCache.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/OutlineCache.skiko.kt
deleted file mode 100644
index 580fd2b..0000000
--- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/OutlineCache.skiko.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.platform
-
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.toSize
-
-/**
- * Class for storing outline. Recalculates outline when [size] or [shape] is changed.
- * It' s needed so we don't have to recreate it every time we use it for rendering
- * (it can be expensive to create outline every frame).
- */
-internal class OutlineCache(
- density: Density,
- size: IntSize,
- shape: Shape,
- layoutDirection: LayoutDirection
-) {
- var density = density
- set(value) {
- if (value != field) {
- field = value
- outline = createOutline()
- }
- }
-
- var size = size
- set(value) {
- if (value != field) {
- field = value
- outline = createOutline()
- }
- }
-
- var shape = shape
- set(value) {
- if (value != field) {
- field = value
- outline = createOutline()
- }
- }
-
- var layoutDirection = layoutDirection
- set(value) {
- if (value != field) {
- field = value
- outline = createOutline()
- }
- }
-
- var outline: Outline = createOutline()
- private set
-
- private fun createOutline() =
- shape.createOutline(size.toSize(), layoutDirection, density)
-}
diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaLayer.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaLayer.skiko.kt
index 41bffb8..b39d3ea 100644
--- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaLayer.skiko.kt
+++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaLayer.skiko.kt
@@ -31,7 +31,6 @@
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.RenderEffect
import androidx.compose.ui.graphics.ReusableGraphicsLayerScope
import androidx.compose.ui.graphics.SkiaBackedCanvas
@@ -47,7 +46,6 @@
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import org.jetbrains.skia.ClipMode
@@ -64,8 +62,7 @@
) : OwnedLayer {
private var size = IntSize.Zero
private var position = IntOffset.Zero
- private var outlineCache =
- OutlineCache(density, size, RectangleShape, LayoutDirection.Ltr)
+ private var outline: Outline? = null
// Internal for testing
internal val matrix = Matrix()
private val pictureRecorder = PictureRecorder()
@@ -105,7 +102,6 @@
override fun resize(size: IntSize) {
if (size != this.size) {
this.size = size
- outlineCache.size = size
updateMatrix()
invalidate()
}
@@ -133,11 +129,10 @@
val x = position.x
val y = position.y
- if (outlineCache.shape === RectangleShape) {
- return 0f <= x && x < size.width && 0f <= y && y < size.height
- }
- return isInOutline(outlineCache.outline, x, y)
+ val outline = outline ?: return true
+
+ return isInOutline(outline, x, y)
}
private fun getMatrix(inverse: Boolean): Matrix {
@@ -152,11 +147,7 @@
}
private var mutatedFields: Int = 0
- override fun updateLayerProperties(
- scope: ReusableGraphicsLayerScope,
- layoutDirection: LayoutDirection,
- density: Density,
- ) {
+ override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {
val maybeChangedFields = scope.mutatedFields or mutatedFields
this.transformOrigin = scope.transformOrigin
this.translationX = scope.translationX
@@ -169,14 +160,12 @@
this.alpha = scope.alpha
this.clip = scope.clip
this.shadowElevation = scope.shadowElevation
- this.density = density
+ this.density = scope.graphicsDensity
this.renderEffect = scope.renderEffect
this.ambientShadowColor = scope.ambientShadowColor
this.spotShadowColor = scope.spotShadowColor
this.compositingStrategy = scope.compositingStrategy
- outlineCache.shape = scope.shape
- outlineCache.layoutDirection = layoutDirection
- outlineCache.density = density
+ this.outline = scope.outline
if (maybeChangedFields and Fields.MatrixAffectingFields != 0) {
updateMatrix()
}
@@ -245,13 +234,17 @@
drawShadow(canvas)
}
- if (clip) {
+ val outline = outline
+ val isClipping = if (clip && outline != null) {
canvas.save()
- when (val outline = outlineCache.outline) {
+ when (outline) {
is Outline.Rectangle -> canvas.clipRect(outline.rect)
is Outline.Rounded -> canvas.clipRoundRect(outline.roundRect)
is Outline.Generic -> canvas.clipPath(outline.path)
}
+ true
+ } else {
+ false
}
val currentRenderEffect = renderEffect
@@ -279,7 +272,7 @@
drawBlock(canvas, null)
canvas.restore()
- if (clip) {
+ if (isClipping) {
canvas.restore()
}
}
@@ -299,7 +292,7 @@
override fun updateDisplayList() = Unit
fun drawShadow(canvas: Canvas) = with(density) {
- val path = when (val outline = outlineCache.outline) {
+ val path = when (val outline = outline) {
is Outline.Rectangle -> Path().apply { addRect(outline.rect) }
is Outline.Rounded -> Path().apply { addRoundRect(outline.roundRect) }
is Outline.Generic -> outline.path
diff --git a/concurrent/concurrent-futures-ktx/api/1.2.0-beta01.txt b/concurrent/concurrent-futures-ktx/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..53345f4
--- /dev/null
+++ b/concurrent/concurrent-futures-ktx/api/1.2.0-beta01.txt
@@ -0,0 +1,14 @@
+// Signature format: 4.0
+package androidx.concurrent.futures {
+
+ public final class ListenableFutureKt {
+ method public static suspend <T> Object? await(com.google.common.util.concurrent.ListenableFuture<T>, kotlin.coroutines.Continuation<? super T>);
+ }
+
+ public final class SuspendToFutureAdapter {
+ method public <T> com.google.common.util.concurrent.ListenableFuture<T> launchFuture(optional kotlin.coroutines.CoroutineContext context, optional boolean launchUndispatched, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block);
+ field public static final androidx.concurrent.futures.SuspendToFutureAdapter INSTANCE;
+ }
+
+}
+
diff --git a/concurrent/concurrent-futures-ktx/api/restricted_1.2.0-beta01.txt b/concurrent/concurrent-futures-ktx/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..53345f4
--- /dev/null
+++ b/concurrent/concurrent-futures-ktx/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,14 @@
+// Signature format: 4.0
+package androidx.concurrent.futures {
+
+ public final class ListenableFutureKt {
+ method public static suspend <T> Object? await(com.google.common.util.concurrent.ListenableFuture<T>, kotlin.coroutines.Continuation<? super T>);
+ }
+
+ public final class SuspendToFutureAdapter {
+ method public <T> com.google.common.util.concurrent.ListenableFuture<T> launchFuture(optional kotlin.coroutines.CoroutineContext context, optional boolean launchUndispatched, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super T>,?> block);
+ field public static final androidx.concurrent.futures.SuspendToFutureAdapter INSTANCE;
+ }
+
+}
+
diff --git a/concurrent/concurrent-futures-ktx/build.gradle b/concurrent/concurrent-futures-ktx/build.gradle
index 692ada9..a491d54 100644
--- a/concurrent/concurrent-futures-ktx/build.gradle
+++ b/concurrent/concurrent-futures-ktx/build.gradle
@@ -41,7 +41,7 @@
androidx {
name = "Futures Kotlin Extensions"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Kotlin Extensions for Androidx implementation of Guava's ListenableFuture"
metalavaK2UastEnabled = true
diff --git a/concurrent/concurrent-futures-ktx/src/main/resources/META-INF/NOTICE.txt b/concurrent/concurrent-futures-ktx/src/main/resources/META-INF/NOTICE.txt
new file mode 100644
index 0000000..ff3ce54
--- /dev/null
+++ b/concurrent/concurrent-futures-ktx/src/main/resources/META-INF/NOTICE.txt
@@ -0,0 +1,220 @@
+List of 3rd party licenses:
+-----------------------------------------------------------------------------
+* kotlinx.coroutines library.
+
+ ****** NOTICE:
+
+=========================================================================
+== NOTICE file corresponding to the section 4 d of ==
+== the Apache License, Version 2.0, ==
+== in this case for the kotlinx.coroutines library. ==
+=========================================================================
+
+kotlinx.coroutines library.
+Copyright 2016-2024 JetBrains s.r.o and contributors
+
+ ****** LICENSE:
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2000-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
+
+ 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.
+
diff --git a/concurrent/concurrent-futures/api/1.2.0-beta01.txt b/concurrent/concurrent-futures/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..9d9c205
--- /dev/null
+++ b/concurrent/concurrent-futures/api/1.2.0-beta01.txt
@@ -0,0 +1,21 @@
+// Signature format: 4.0
+package androidx.concurrent.futures {
+
+ public final class CallbackToFutureAdapter {
+ method public static <T> com.google.common.util.concurrent.ListenableFuture<T!> getFuture(androidx.concurrent.futures.CallbackToFutureAdapter.Resolver<T!>);
+ }
+
+ public static final class CallbackToFutureAdapter.Completer<T> {
+ method public void addCancellationListener(Runnable, java.util.concurrent.Executor);
+ method protected void finalize();
+ method public boolean set(T!);
+ method public boolean setCancelled();
+ method public boolean setException(Throwable);
+ }
+
+ public static interface CallbackToFutureAdapter.Resolver<T> {
+ method public Object? attachCompleter(androidx.concurrent.futures.CallbackToFutureAdapter.Completer<T!>) throws java.lang.Exception;
+ }
+
+}
+
diff --git a/concurrent/concurrent-futures/api/restricted_1.2.0-beta01.txt b/concurrent/concurrent-futures/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..32be46b
--- /dev/null
+++ b/concurrent/concurrent-futures/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,45 @@
+// Signature format: 4.0
+package androidx.concurrent.futures {
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class AbstractResolvableFuture<V> implements com.google.common.util.concurrent.ListenableFuture<V!> {
+ ctor protected AbstractResolvableFuture();
+ method public final void addListener(Runnable!, java.util.concurrent.Executor!);
+ method protected void afterDone();
+ method public final boolean cancel(boolean);
+ method public final V! get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+ method public final V! get(long, java.util.concurrent.TimeUnit!) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+ method protected void interruptTask();
+ method public final boolean isCancelled();
+ method public final boolean isDone();
+ method protected String? pendingToString();
+ method protected boolean set(V?);
+ method protected boolean setException(Throwable!);
+ method protected boolean setFuture(com.google.common.util.concurrent.ListenableFuture<? extends V!>!);
+ method protected final boolean wasInterrupted();
+ }
+
+ public final class CallbackToFutureAdapter {
+ method public static <T> com.google.common.util.concurrent.ListenableFuture<T!> getFuture(androidx.concurrent.futures.CallbackToFutureAdapter.Resolver<T!>);
+ }
+
+ public static final class CallbackToFutureAdapter.Completer<T> {
+ method public void addCancellationListener(Runnable, java.util.concurrent.Executor);
+ method protected void finalize();
+ method public boolean set(T!);
+ method public boolean setCancelled();
+ method public boolean setException(Throwable);
+ }
+
+ public static interface CallbackToFutureAdapter.Resolver<T> {
+ method public Object? attachCompleter(androidx.concurrent.futures.CallbackToFutureAdapter.Completer<T!>) throws java.lang.Exception;
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ResolvableFuture<V> extends androidx.concurrent.futures.AbstractResolvableFuture<V!> {
+ method public static <V> androidx.concurrent.futures.ResolvableFuture<V!> create();
+ method public boolean set(V?);
+ method public boolean setException(Throwable);
+ method public boolean setFuture(com.google.common.util.concurrent.ListenableFuture<? extends V!>);
+ }
+
+}
+
diff --git a/constraintlayout/constraintlayout-compose/build.gradle b/constraintlayout/constraintlayout-compose/build.gradle
index a052358..b1c6c3b 100644
--- a/constraintlayout/constraintlayout-compose/build.gradle
+++ b/constraintlayout/constraintlayout-compose/build.gradle
@@ -104,7 +104,7 @@
androidx {
name = "ConstraintLayout Compose"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.CONSTRAINTLAYOUT_COMPOSE
inceptionYear = "2022"
description = "This library offers a flexible and adaptable way to position and animate widgets in Compose"
diff --git a/constraintlayout/constraintlayout/lint-baseline.xml b/constraintlayout/constraintlayout/lint-baseline.xml
index b84f60c..19150a6 100644
--- a/constraintlayout/constraintlayout/lint-baseline.xml
+++ b/constraintlayout/constraintlayout/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanTargetApiAnnotation"
@@ -245,618 +245,6 @@
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintHelper is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" elevation = getElevation();"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintHelper is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(view.getTranslationZ() + elevation);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintHelper is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(view.getTranslationZ() + elevation);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintLayout.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintProperties is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mView.setElevation(elevation);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintProperties.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintProperties is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mView.setTranslationZ(translationZ);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintProperties.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintSet is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" constraint.transform.translationZ = view.getTranslationZ();"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintSet is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" constraint.transform.elevation = view.getElevation();"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintSet is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" constraint.transform.translationZ = view.getTranslationZ();"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintSet is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" constraint.transform.elevation = view.getElevation();"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintSet is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(constraint.transform.translationZ);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.ConstraintSet is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setElevation(constraint.transform.elevation);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" outline.setRoundRect(0, 0, w, h, r);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setOutlineProvider(mViewOutlineProvider);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(true);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(false);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" invalidateOutline();"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" outline.setRoundRect(0, 0, w, h, mRound);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setOutlineProvider(mViewOutlineProvider);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(true);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(false);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" invalidateOutline();"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" outline.setRoundRect(0, 0, w, h, r);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setOutlineProvider(mViewOutlineProvider);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(true);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(false);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" invalidateOutline();"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" outline.setRoundRect(0, 0, w, h, mRound);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setOutlineProvider(mViewOutlineProvider);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(true);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(false);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.ImageFilterView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" invalidateOutline();"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.helper.widget.Layer is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" elevation = getElevation();"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/helper/widget/Layer.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.helper.widget.Layer is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(view.getTranslationZ() + elevation);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/helper/widget/Layer.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.helper.widget.Layer is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(view.getTranslationZ() + elevation);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/helper/widget/Layer.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" outline.setRoundRect(0, 0, w, h, r);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setOutlineProvider(mViewOutlineProvider);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(true);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(false);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" invalidateOutline();"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" outline.setRoundRect(0, 0, w, h, mRound);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setOutlineProvider(mViewOutlineProvider);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(true);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(false);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" invalidateOutline();"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.motion.widget.MotionConstrainedPoint is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" this.mElevation = view.getElevation();"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/widget/MotionConstrainedPoint.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.motion.widget.MotionConstrainedPoint is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" this.mTranslationZ = view.getTranslationZ();"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/widget/MotionConstrainedPoint.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" outline.setRoundRect(0, 0, w, h, r);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionLabel is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setOutlineProvider(mViewOutlineProvider);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionLabel is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(true);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionLabel is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(false);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionLabel is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" invalidateOutline();"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" outline.setRoundRect(0, 0, w, h, mRound);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionLabel is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setOutlineProvider(mViewOutlineProvider);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionLabel is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(true);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionLabel is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setClipToOutline(false);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.utils.widget.MotionLabel is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" invalidateOutline();"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.motion.widget.MotionLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" target.setNestedScrollingEnabled(false);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" target.setNestedScrollingEnabled(true);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.motion.utils.ViewOscillator.ElevationSet is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setElevation(get(t));"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/utils/ViewOscillator.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.motion.utils.ViewOscillator.TranslationZset is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(get(t));"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/utils/ViewOscillator.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.motion.utils.ViewSpline.ElevationSet is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setElevation(get(t));"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/utils/ViewSpline.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.motion.utils.ViewSpline.TranslationZset is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(get(t));"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/utils/ViewSpline.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.motion.utils.ViewTimeCycle.ElevationSet is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setElevation(get(t, time, view, cache));"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/utils/ViewTimeCycle.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.motion.utils.ViewTimeCycle.TranslationZset is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(get(t, time, view, cache));"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/motion/utils/ViewTimeCycle.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.VirtualLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" elevation = getElevation();"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/VirtualLayout.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.VirtualLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(view.getTranslationZ() + elevation);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/VirtualLayout.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.constraintlayout.widget.VirtualLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setTranslationZ(view.getTranslationZ() + elevation);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/constraintlayout/widget/VirtualLayout.java"/>
- </issue>
-
- <issue
id="PrivateConstructorForUtilityClass"
message="Utility class is missing private constructor"
errorLine1="public class CustomSupport {"
@@ -884,6 +272,708 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" && android.os.Build.VERSION.SDK_INT"
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @TargetApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintProperties.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintProperties.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/ConstraintSet.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/Constraints.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/Constraints.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/widget/KeyAttributes.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/widget/KeyCycle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/widget/KeyTimeCycle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/helper/widget/Layer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" && android.os.Build.VERSION.SDK_INT"
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/constraintlayout/helper/widget/Layer.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/widget/MotionConstrainedPoint.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/widget/MotionConstrainedPoint.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/utils/ViewOscillator.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/utils/ViewOscillator.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/utils/ViewSpline.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/utils/ViewSpline.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/utils/ViewTimeCycle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/motion/utils/ViewTimeCycle.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/VirtualLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" && android.os.Build.VERSION.SDK_INT"
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/constraintlayout/widget/VirtualLayout.java"/>
+ </issue>
+
+ <issue
id="KotlinPropertyAccess"
message="The getter return type (`String[]`) and setter parameter type (`String`) getter and setter methods for property `stateLabels` should have exactly the same type to allow be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
errorLine1=" public String[] getStateLabels() {"
diff --git a/contentpager/contentpager/lint-baseline.xml b/contentpager/contentpager/lint-baseline.xml
index 55b8606..31b44c8 100644
--- a/contentpager/contentpager/lint-baseline.xml
+++ b/contentpager/contentpager/lint-baseline.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `android.database.AbstractCursor#setExtras`"
+ message="Call requires API level 23 (current min is 21): `android.database.AbstractCursor#setExtras`"
errorLine1=" c.setExtras(extras);"
errorLine2=" ~~~~~~~~~">
<location
@@ -12,7 +12,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `android.database.AbstractCursor#setExtras`"
+ message="Call requires API level 23 (current min is 21): `android.database.AbstractCursor#setExtras`"
errorLine1=" cursor.setExtras(extras);"
errorLine2=" ~~~~~~~~~">
<location
diff --git a/coordinatorlayout/coordinatorlayout/lint-baseline.xml b/coordinatorlayout/coordinatorlayout/lint-baseline.xml
index b3144af..e558eb8 100644
--- a/coordinatorlayout/coordinatorlayout/lint-baseline.xml
+++ b/coordinatorlayout/coordinatorlayout/lint-baseline.xml
@@ -1,5 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-alpha07)" variant="all" version="8.0.0-alpha07">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java"/>
+ </issue>
<issue
id="UnknownNullness"
diff --git a/core/core-google-shortcuts/lint-baseline.xml b/core/core-google-shortcuts/lint-baseline.xml
new file mode 100644
index 0000000..258c5c5
--- /dev/null
+++ b/core/core-google-shortcuts/lint-baseline.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java"/>
+ </issue>
+
+</issues>
diff --git a/core/core-i18n/build.gradle b/core/core-i18n/build.gradle
index 7705edf..088ea0d 100644
--- a/core/core-i18n/build.gradle
+++ b/core/core-i18n/build.gradle
@@ -54,7 +54,6 @@
android {
namespace "androidx.core.i18n"
defaultConfig {
- minSdkVersion 19
}
lintOptions {
lintConfig = file("lint.xml")
diff --git a/core/core-i18n/lint-baseline.xml b/core/core-i18n/lint-baseline.xml
new file mode 100644
index 0000000..eeb0ca7
--- /dev/null
+++ b/core/core-i18n/lint-baseline.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidTest/java/androidx/core/i18n/CheckTheJavaApisTest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @SdkSuppress(minSdkVersion = AVAILABLE_LANGUAGE_TAG)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @SdkSuppress(minSdkVersion = AVAILABLE_LANGUAGE_TAG)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidTest/java/androidx/core/i18n/DateTimeFormatterTest.kt"/>
+ </issue>
+
+</issues>
diff --git a/core/core-ktx/build.gradle b/core/core-ktx/build.gradle
index 6832072..c425cae 100644
--- a/core/core-ktx/build.gradle
+++ b/core/core-ktx/build.gradle
@@ -34,7 +34,7 @@
androidx {
name = "Core Kotlin Extensions"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.CORE
inceptionYear = "2018"
description = "Kotlin extensions for 'core' artifact"
diff --git a/core/core-ktx/lint-baseline.xml b/core/core-ktx/lint-baseline.xml
new file mode 100644
index 0000000..7597446
--- /dev/null
+++ b/core/core-ktx/lint-baseline.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21 && value is Size) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/Bundle.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21 && value is SizeF) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/Bundle.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/Bundle.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/PersistableBundle.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/PersistableBundle.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/PersistableBundle.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/PersistableBundle.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Range.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Range.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Range.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Range.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Range.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Range.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Size.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Size.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Size.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/Size.kt"/>
+ </issue>
+
+</issues>
diff --git a/core/core-remoteviews/lint-baseline.xml b/core/core-remoteviews/lint-baseline.xml
new file mode 100644
index 0000000..6b45efe
--- /dev/null
+++ b/core/core-remoteviews/lint-baseline.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/RemoteViewsCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/RemoteViewsCompat.kt"/>
+ </issue>
+
+</issues>
diff --git a/core/core-remoteviews/src/test/java/androidx/core/widget/AppWidgetManagerCompatTest.kt b/core/core-remoteviews/src/test/java/androidx/core/widget/AppWidgetManagerCompatTest.kt
index cbee402..c78a3af 100644
--- a/core/core-remoteviews/src/test/java/androidx/core/widget/AppWidgetManagerCompatTest.kt
+++ b/core/core-remoteviews/src/test/java/androidx/core/widget/AppWidgetManagerCompatTest.kt
@@ -47,7 +47,7 @@
@RunWith(RobolectricTestRunner::class)
@DoNotInstrument
-@Config(sdk = [19])
+@Config(sdk = [21])
class AppWidgetManagerCompatTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
diff --git a/core/core/api/current.ignore b/core/core/api/current.ignore
index 7920463..dc8ac9f 100644
--- a/core/core/api/current.ignore
+++ b/core/core/api/current.ignore
@@ -13,3 +13,9 @@
Method androidx.core.view.GestureDetectorCompat.setIsLongpressEnabled has changed deprecation state false --> true
ChangedDeprecated: androidx.core.view.GestureDetectorCompat#setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener):
Method androidx.core.view.GestureDetectorCompat.setOnDoubleTapListener has changed deprecation state false --> true
+ChangedValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH:
+ Field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH has changed value from android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH to android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH
+ChangedValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX:
+ Field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX has changed value from android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX to android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX
+ChangedValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY:
+ Field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY has changed value from android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY to android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index de4ac69..a845e49 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -2026,6 +2026,7 @@
method public static android.graphics.Typeface? buildTypeface(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![]);
method public static androidx.core.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+ method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, int, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
}
public static final class FontsContractCompat.Columns implements android.provider.BaseColumns {
@@ -3665,10 +3666,10 @@
field public static final int ACTION_SELECT = 4; // 0x4
field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
- field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
- field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
- field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
+ field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+ field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
field public static final int FLAG_PREFETCH_ANCESTORS = 1; // 0x1
field public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 16; // 0x10
field public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 8; // 0x8
diff --git a/core/core/api/restricted_current.ignore b/core/core/api/restricted_current.ignore
index 7920463..dc8ac9f 100644
--- a/core/core/api/restricted_current.ignore
+++ b/core/core/api/restricted_current.ignore
@@ -13,3 +13,9 @@
Method androidx.core.view.GestureDetectorCompat.setIsLongpressEnabled has changed deprecation state false --> true
ChangedDeprecated: androidx.core.view.GestureDetectorCompat#setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener):
Method androidx.core.view.GestureDetectorCompat.setOnDoubleTapListener has changed deprecation state false --> true
+ChangedValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH:
+ Field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH has changed value from android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH to android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH
+ChangedValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX:
+ Field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX has changed value from android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX to android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX
+ChangedValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY:
+ Field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY has changed value from android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY to android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index fd443b0..a7a3c98 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -2420,6 +2420,8 @@
method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @VisibleForTesting public static android.content.pm.ProviderInfo? getProvider(android.content.pm.PackageManager, androidx.core.provider.FontRequest, android.content.res.Resources?) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static java.util.Map<android.net.Uri!,java.nio.ByteBuffer!>! prepareFontData(android.content.Context!, androidx.core.provider.FontsContractCompat.FontInfo![]!, android.os.CancellationSignal!);
method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+ method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, int, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? requestFont(android.content.Context, androidx.core.provider.FontRequest, int, boolean, @IntRange(from=0) int, android.os.Handler, androidx.core.provider.FontsContractCompat.FontRequestCallback);
method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void resetCache();
field @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final String PARCEL_FONT_RESULTS = "font_results";
}
@@ -4185,10 +4187,10 @@
field public static final int ACTION_SELECT = 4; // 0x4
field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
- field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
- field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
- field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
+ field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+ field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
field public static final int FLAG_PREFETCH_ANCESTORS = 1; // 0x1
field public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 16; // 0x10
field public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 8; // 0x8
diff --git a/core/core/lint-baseline.xml b/core/core/lint-baseline.xml
index d9f7cb4..d68cb9b1 100644
--- a/core/core/lint-baseline.xml
+++ b/core/core/lint-baseline.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="NewApi"
- message="Call requires API level 24 (current min is 19): `java.util.Optional#of`"
+ message="Call requires API level 24 (current min is 21): `java.util.Optional#of`"
errorLine1=" return Optional.of(modeCompat);"
errorLine2=" ~~">
<location
@@ -12,7 +12,7 @@
<issue
id="NewApi"
- message="Call requires API level 24 (current min is 19): `java.util.Optional#empty`"
+ message="Call requires API level 24 (current min is 21): `java.util.Optional#empty`"
errorLine1=" return Optional.empty();"
errorLine2=" ~~~~~">
<location
@@ -21,7 +21,7 @@
<issue
id="NewApi"
- message="Call requires API level 24 (current min is 19): `java.util.Optional#get`"
+ message="Call requires API level 24 (current min is 21): `java.util.Optional#get`"
errorLine1=" findNativeMode(DisplayCompat.getSupportedModes(mContext, mDefaultDisplay)).get();"
errorLine2=" ~~~">
<location
@@ -30,7 +30,7 @@
<issue
id="NewApi"
- message="Call requires API level 24 (current min is 19): `java.util.Optional#get`"
+ message="Call requires API level 24 (current min is 21): `java.util.Optional#get`"
errorLine1=" findNativeMode(DisplayCompat.getSupportedModes(mContext, secondDisplay)).get();"
errorLine2=" ~~~">
<location
@@ -39,7 +39,7 @@
<issue
id="NewApi"
- message="Call requires API level 24 (current min is 19): `java.util.Optional#get`"
+ message="Call requires API level 24 (current min is 21): `java.util.Optional#get`"
errorLine1=" findNativeMode(DisplayCompat.getSupportedModes(mContext, mDefaultDisplay)).get();"
errorLine2=" ~~~">
<location
@@ -48,7 +48,7 @@
<issue
id="NewApi"
- message="Call requires API level 24 (current min is 19): `java.util.Optional#get`"
+ message="Call requires API level 24 (current min is 21): `java.util.Optional#get`"
errorLine1=" findNativeMode(DisplayCompat.getSupportedModes(mContext, mDefaultDisplay)).get();"
errorLine2=" ~~~">
<location
@@ -57,7 +57,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getImportance`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getImportance`"
errorLine1=" boolean equality = nc1.getImportance() == nc2.getImportance()"
errorLine2=" ~~~~~~~~~~~~~">
<location
@@ -66,7 +66,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getImportance`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getImportance`"
errorLine1=" boolean equality = nc1.getImportance() == nc2.getImportance()"
errorLine2=" ~~~~~~~~~~~~~">
<location
@@ -75,7 +75,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#canBypassDnd`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#canBypassDnd`"
errorLine1=" && nc1.canBypassDnd() == nc2.canBypassDnd()"
errorLine2=" ~~~~~~~~~~~~">
<location
@@ -84,7 +84,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#canBypassDnd`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#canBypassDnd`"
errorLine1=" && nc1.canBypassDnd() == nc2.canBypassDnd()"
errorLine2=" ~~~~~~~~~~~~">
<location
@@ -93,7 +93,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getLockscreenVisibility`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getLockscreenVisibility`"
errorLine1=" && nc1.getLockscreenVisibility() == nc2.getLockscreenVisibility()"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -102,7 +102,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getLockscreenVisibility`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getLockscreenVisibility`"
errorLine1=" && nc1.getLockscreenVisibility() == nc2.getLockscreenVisibility()"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -111,7 +111,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getLightColor`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getLightColor`"
errorLine1=" && nc1.getLightColor() == nc2.getLightColor()"
errorLine2=" ~~~~~~~~~~~~~">
<location
@@ -120,7 +120,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getLightColor`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getLightColor`"
errorLine1=" && nc1.getLightColor() == nc2.getLightColor()"
errorLine2=" ~~~~~~~~~~~~~">
<location
@@ -129,7 +129,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getId`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getId`"
errorLine1=" && Objects.equals(nc1.getId(), nc2.getId())"
errorLine2=" ~~~~~">
<location
@@ -138,7 +138,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getId`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getId`"
errorLine1=" && Objects.equals(nc1.getId(), nc2.getId())"
errorLine2=" ~~~~~">
<location
@@ -147,7 +147,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getName`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getName`"
errorLine1=" && Objects.equals(nc1.getName(), nc2.getName())"
errorLine2=" ~~~~~~~">
<location
@@ -156,7 +156,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getName`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getName`"
errorLine1=" && Objects.equals(nc1.getName(), nc2.getName())"
errorLine2=" ~~~~~~~">
<location
@@ -165,7 +165,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getDescription`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getDescription`"
errorLine1=" && Objects.equals(nc1.getDescription(), nc2.getDescription())"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -174,7 +174,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getDescription`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getDescription`"
errorLine1=" && Objects.equals(nc1.getDescription(), nc2.getDescription())"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -183,7 +183,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getSound`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getSound`"
errorLine1=" && Objects.equals(nc1.getSound(), nc2.getSound())"
errorLine2=" ~~~~~~~~">
<location
@@ -192,7 +192,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getSound`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getSound`"
errorLine1=" && Objects.equals(nc1.getSound(), nc2.getSound())"
errorLine2=" ~~~~~~~~">
<location
@@ -201,7 +201,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getVibrationPattern`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getVibrationPattern`"
errorLine1=" && Arrays.equals(nc1.getVibrationPattern(), nc2.getVibrationPattern())"
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
@@ -210,7 +210,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getVibrationPattern`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getVibrationPattern`"
errorLine1=" && Arrays.equals(nc1.getVibrationPattern(), nc2.getVibrationPattern())"
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
@@ -219,7 +219,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getGroup`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getGroup`"
errorLine1=" && Objects.equals(nc1.getGroup(), nc2.getGroup())"
errorLine2=" ~~~~~~~~">
<location
@@ -228,7 +228,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getGroup`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getGroup`"
errorLine1=" && Objects.equals(nc1.getGroup(), nc2.getGroup())"
errorLine2=" ~~~~~~~~">
<location
@@ -237,7 +237,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getAudioAttributes`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getAudioAttributes`"
errorLine1=" && Objects.equals(nc1.getAudioAttributes(), nc2.getAudioAttributes());"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
<location
@@ -246,7 +246,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getAudioAttributes`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getAudioAttributes`"
errorLine1=" && Objects.equals(nc1.getAudioAttributes(), nc2.getAudioAttributes());"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
<location
@@ -255,7 +255,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getImportance`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getImportance`"
errorLine1=" assertEquals(expected.getImportance(), actual.getImportance());"
errorLine2=" ~~~~~~~~~~~~~">
<location
@@ -264,7 +264,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getLightColor`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getLightColor`"
errorLine1=" assertEquals(expected.getLightColor(), actual.getLightColor());"
errorLine2=" ~~~~~~~~~~~~~">
<location
@@ -273,7 +273,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getId`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getId`"
errorLine1=" assertEquals(expected.getId(), actual.getId());"
errorLine2=" ~~~~~">
<location
@@ -282,7 +282,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getName`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getName`"
errorLine1=" assertEquals(expected.getName(), actual.getName());"
errorLine2=" ~~~~~~~">
<location
@@ -291,7 +291,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getDescription`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getDescription`"
errorLine1=" assertEquals(expected.getDescription(), actual.getDescription());"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -300,7 +300,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getSound`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getSound`"
errorLine1=" assertEquals(expected.getSound(), actual.getSound());"
errorLine2=" ~~~~~~~~">
<location
@@ -309,7 +309,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getVibrationPattern`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getVibrationPattern`"
errorLine1=" assertArrayEquals(expected.getVibrationPattern(), actual.getVibrationPattern());"
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
@@ -318,7 +318,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getGroup`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getGroup`"
errorLine1=" assertEquals(expected.getGroup(), actual.getGroup());"
errorLine2=" ~~~~~~~~">
<location
@@ -327,7 +327,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannel#getAudioAttributes`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannel#getAudioAttributes`"
errorLine1=" assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
<location
@@ -336,7 +336,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannelGroup#getId`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannelGroup#getId`"
errorLine1=" assertEquals(expected.getId(), actual.getId());"
errorLine2=" ~~~~~">
<location
@@ -345,7 +345,7 @@
<issue
id="NewApi"
- message="Call requires API level 26 (current min is 19): `android.app.NotificationChannelGroup#getName`"
+ message="Call requires API level 26 (current min is 21): `android.app.NotificationChannelGroup#getName`"
errorLine1=" assertEquals(expected.getName(), actual.getName());"
errorLine2=" ~~~~~~~">
<location
@@ -354,24 +354,6 @@
<issue
id="NewApi"
- message="Call requires API level 21 (current min is 19): `addInvisibleAction`"
- errorLine1=" Notification nWith = builder.addInvisibleAction(0, "testAction", null)"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/core/app/NotificationCompatTest.java"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 21 (current min is 19): `getInvisibleActions`"
- errorLine1=" List<NotificationCompat.Action> actions = NotificationCompat.getInvisibleActions(nWith);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/core/app/NotificationCompatTest.java"/>
- </issue>
-
- <issue
- id="NewApi"
message="Call requires API level 28 (current min is 23): `android.graphics.drawable.Icon#getType`"
errorLine1=" assertEquals(Icon.TYPE_BITMAP, ((Icon) firstBuiltIcon).getType());"
errorLine2=" ~~~~~~~">
@@ -408,7 +390,7 @@
<issue
id="NewApi"
- message="Call requires API level 30 (current min is 19): `Builder`"
+ message="Call requires API level 30 (current min is 21): `Builder`"
errorLine1=" new NotificationCompat.BubbleMetadata.Builder(shortcutId)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -417,16 +399,7 @@
<issue
id="NewApi"
- message="Call requires API level 21 (current min is 19): `getInvisibleActions`"
- errorLine1=" NotificationCompat.getInvisibleActions(notification);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/core/app/NotificationCompatTest.java"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 23 (current min is 19): `setBreakStrategy`"
+ message="Call requires API level 23 (current min is 21): `setBreakStrategy`"
errorLine1=" .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE).build());"
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
@@ -435,7 +408,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `setBreakStrategy`"
+ message="Call requires API level 23 (current min is 21): `setBreakStrategy`"
errorLine1=" .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)"
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
@@ -444,7 +417,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `setHyphenationFrequency`"
+ message="Call requires API level 23 (current min is 21): `setHyphenationFrequency`"
errorLine1=" .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL).build());"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -453,7 +426,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `setBreakStrategy`"
+ message="Call requires API level 23 (current min is 21): `setBreakStrategy`"
errorLine1=" .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)"
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
@@ -462,7 +435,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `setHyphenationFrequency`"
+ message="Call requires API level 23 (current min is 21): `setHyphenationFrequency`"
errorLine1=" .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -471,7 +444,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `getBreakStrategy`"
+ message="Call requires API level 23 (current min is 21): `getBreakStrategy`"
errorLine1=" .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE).build().getBreakStrategy());"
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
@@ -480,7 +453,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `setBreakStrategy`"
+ message="Call requires API level 23 (current min is 21): `setBreakStrategy`"
errorLine1=" .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE).build().getBreakStrategy());"
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
@@ -489,7 +462,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `setHyphenationFrequency`"
+ message="Call requires API level 23 (current min is 21): `setHyphenationFrequency`"
errorLine1=" .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE).build()"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -498,7 +471,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `getHyphenationFrequency`"
+ message="Call requires API level 23 (current min is 21): `getHyphenationFrequency`"
errorLine1=" .getHyphenationFrequency());"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -507,7 +480,7 @@
<issue
id="NewApi"
- message="Call requires API level 25 (current min is 19): `toShortcutInfo`"
+ message="Call requires API level 25 (current min is 21): `toShortcutInfo`"
errorLine1=" .toShortcutInfo());"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -516,7 +489,7 @@
<issue
id="NewApi"
- message="Call requires API level 25 (current min is 19): `toShortcutInfo`"
+ message="Call requires API level 25 (current min is 21): `toShortcutInfo`"
errorLine1=" .toShortcutInfo());"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -525,7 +498,7 @@
<issue
id="NewApi"
- message="Call requires API level 25 (current min is 19): `toShortcutInfo`"
+ message="Call requires API level 25 (current min is 21): `toShortcutInfo`"
errorLine1=" .toShortcutInfo());"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -534,7 +507,7 @@
<issue
id="NewApi"
- message="Call requires API level 25 (current min is 19): `toShortcutInfo`"
+ message="Call requires API level 25 (current min is 21): `toShortcutInfo`"
errorLine1=" .toShortcutInfo());"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -543,7 +516,7 @@
<issue
id="NewApi"
- message="Call requires API level 25 (current min is 19): `toShortcutInfo`"
+ message="Call requires API level 25 (current min is 21): `toShortcutInfo`"
errorLine1=" .toShortcutInfo());"
errorLine2=" ~~~~~~~~~~~~~~">
<location
@@ -552,7 +525,7 @@
<issue
id="NewApi"
- message="Call requires API level 24 (current min is 19): `android.telephony.SubscriptionManager#getDefaultSubscriptionId`"
+ message="Call requires API level 24 (current min is 21): `android.telephony.SubscriptionManager#getDefaultSubscriptionId`"
errorLine1=" assertEquals(getDefaultSubscriptionId(), actual);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -561,7 +534,7 @@
<issue
id="NewApi"
- message="Call requires API level 24 (current min is 19): `android.telephony.SubscriptionManager#getDefaultSubscriptionId`"
+ message="Call requires API level 24 (current min is 21): `android.telephony.SubscriptionManager#getDefaultSubscriptionId`"
errorLine1=" assertEquals(expected, getDefaultSubscriptionId());"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -569,24 +542,6 @@
</issue>
<issue
- id="NewApi"
- message="Call requires API level 21 (current min is 19): `new android.widget.FrameLayout`"
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
- <location
- file="src/androidTest/java/androidx/core/widget/TestContentView.java"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 20 (current min is 19): `android.view.View#requestApplyInsets`"
- errorLine1=" post { requestApplyInsets() }"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/androidx/core/view/WindowInsetsCompatActivityTest.kt"/>
- </issue>
-
- <issue
id="Range"
message="Value must be ≥ 1 and ≤ 200 but `getSvid` can be 206"
errorLine1=" return mWrapped.getSvid(satelliteIndex);"
@@ -822,69 +777,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mAction = new AccessibilityNodeInfo.AccessibilityAction(id, label);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getId();"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getLabel();"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain("
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getSelectionMode();"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain("
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isSelected();"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 29; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" mInfo = new TouchDelegateInfo(targetMap);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
@@ -921,42 +813,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return mInfo.removeChild(child);"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return mInfo.removeChild(root, virtualDescendantId);"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mInfo.addAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return mInfo.removeAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 24; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return mInfo.isImportantForAccessibility();"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -993,15 +849,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" actions = (List<Object>) (List<?>) mInfo.getActionList();"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 23; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return mInfo.isContextClickable();"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
@@ -1038,24 +885,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mInfo.setError(error);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return mInfo.getError();"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 26; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return mInfo.getAvailableExtraData();"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
@@ -1074,24 +903,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mInfo.setMaxTextLength(max);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return mInfo.getMaxTextLength();"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 22; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalBefore());"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
@@ -1146,15 +957,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return AccessibilityWindowInfoCompat.wrapNonNullInstance(mInfo.getWindow());"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 28; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return mInfo.getTooltipText();"
errorLine2=" ~~~~~~~~~~~~~~">
@@ -1380,42 +1182,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" if (mPaint.getLetterSpacing() != other.getTextPaint().getLetterSpacing()) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" if (mPaint.getLetterSpacing() != other.getTextPaint().getLetterSpacing()) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" if (!TextUtils.equals(mPaint.getFontFeatureSettings(),"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" other.getTextPaint().getFontFeatureSettings())) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 24; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" if (!mPaint.getTextLocales().equals(other.getTextPaint().getTextLocales())) {"
errorLine2=" ~~~~~~~~~~~~~~">
@@ -1434,24 +1200,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mPaint.getTextSkewX(), mPaint.getLetterSpacing(), mPaint.getFlags(),"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.isElegantTextHeight(),"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 24; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.isElegantTextHeight(),"
errorLine2=" ~~~~~~~~~~~~~~">
@@ -1461,42 +1209,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mPaint.getTextSkewX(), mPaint.getLetterSpacing(), mPaint.getFlags(),"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mPaint.getTextLocale(), mPaint.getTypeface(), mPaint.isElegantTextHeight(),"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" sb.append(", letterSpacing=" + mPaint.getLetterSpacing());"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" sb.append(", elegantTextHeight=" + mPaint.isElegantTextHeight());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 24; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" sb.append(", textLocale=" + mPaint.getTextLocales());"
errorLine2=" ~~~~~~~~~~~~~~">
@@ -1731,42 +1443,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mExtras = new PersistableBundle();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mExtras.putInt(EXTRA_PERSON_COUNT, mPersons.length);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mExtras.putPersistableBundle(EXTRA_PERSON_ + (i + 1),"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mExtras.putString(EXTRA_LOCUS_ID, mLocusId.getId());"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 22; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" mExtras.putBoolean(EXTRA_LONG_LIVED, mIsLongLived);"
errorLine2=" ~~~~~~~~~~">
@@ -1776,42 +1452,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" if (bundle == null || !bundle.containsKey(EXTRA_PERSON_COUNT)) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" int personsLength = bundle.getInt(EXTRA_PERSON_COUNT);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" bundle.getPersistableBundle(EXTRA_PERSON_ + (i + 1)));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" if (bundle == null || !bundle.containsKey(EXTRA_LONG_LIVED)) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 22; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return bundle.getBoolean(EXTRA_LONG_LIVED);"
errorLine2=" ~~~~~~~~~~">
@@ -1848,15 +1488,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" final String locusId = bundle.getString(EXTRA_LOCUS_ID);"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" mInfo.mId = shortcutInfo.getId();"
errorLine2=" ~~~~~">
@@ -2055,51 +1686,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mInfo.mExtras = new PersistableBundle();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mInfo.mExtras.putStringArray("
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mInfo.mExtras.putStringArray(capability + "/" + paramName,"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mInfo.mExtras = new PersistableBundle();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mInfo.mExtras.putString(EXTRA_SLICE_URI, UriCompat.toSafeString(mSliceUri));"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return context.getSystemService(ShortcutManager.class).isRequestPinShortcutSupported();"
errorLine2=" ~~~~~~~~~~~~~~~~">
@@ -2540,6 +2126,2655 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21 && action == null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityOptionsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityOptionsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityOptionsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/ActivityOptionsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/AlarmManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/AlarmManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CheckedTextViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CheckedTextViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CheckedTextViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CheckedTextViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CheckedTextViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CompoundButtonCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CompoundButtonCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CompoundButtonCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CompoundButtonCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/CompoundButtonCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/ContextCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/ContextCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/ContextCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/ContextCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/ContextCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/provider/DocumentsContractCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/provider/DocumentsContractCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/provider/DocumentsContractCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/provider/DocumentsContractCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/provider/DocumentsContractCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/provider/DocumentsContractCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/provider/DocumentsContractCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/provider/DocumentsContractCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/provider/DocumentsContractCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/EdgeEffectCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/EdgeEffectCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/EnvironmentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/EnvironmentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/FileProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/FileProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/res/FontResourcesParserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/res/FontResourcesParserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/HapticFeedbackConstantsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/text/ICUCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/text/ICUCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/text/ICUCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/ImageViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/ImageViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/ImageViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/ImageViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/ImageViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/LayoutInflaterCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/LayoutInflaterCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" localeArray[i] = Build.VERSION.SDK_INT >= 21"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/LocaleListCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/LocaleListCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/LocaleListCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/LocaleListCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/LocaleListCompatWrapper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/os/LocaleListCompatWrapper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/LocaleManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/LocaleManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/text/util/LocalePreferences.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (VERSION.SDK_INT <= 19) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/location/LocationManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (VERSION.SDK_INT == 19) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/location/LocationManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/NestedScrollView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/NestedScrollView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/NestedScrollView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/NestedScrollView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/NestedScrollView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationChannelCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" !(Build.VERSION.SDK_INT >= 21) && mBuilder.mLargeIcon != null;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" final boolean afterLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21 && style != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT > NotificationManagerCompat.MAX_SIDE_CHANNEL_SDK_VERSION) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationCompatSideChannelService.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT <= MAX_SIDE_CHANNEL_SDK_VERSION) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT <= MAX_SIDE_CHANNEL_SDK_VERSION) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/NotificationManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/PopupWindowCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/widget/PopupWindowCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/RemoteInput.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/RemoteInput.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/RemoteInput.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/RemoteInput.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/RemoteInput.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/app/RemoteInput.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/res/ResourcesCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/res/ResourcesCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/res/ResourcesCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/RoundedBitmapDrawable21.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/RoundedBitmapDrawableFactory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/SizeFCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/SizeFCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/util/SizeFCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/TypefaceCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/TypefaceCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/TypefaceCompatApi21Impl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewGroupCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewGroupCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewGroupCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewGroupCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewParentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewParentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewParentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewParentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewParentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewParentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewParentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewParentCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/WeightTypefaceApi21.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsAnimationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsAnimationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsAnimationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsAnimationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= 21 && srcImpl instanceof Impl21) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= 20 && srcImpl instanceof Impl20) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(api = 20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/view/WindowInsetsControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi21.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/graphics/drawable/WrappedDrawableState.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `drawable`.">
+ <location
+ file="src/main/res/drawable-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" tools:targetApi="21""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/drawable/ic_call_answer.xml"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" tools:targetApi="21""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/drawable/ic_call_answer_video.xml"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" tools:targetApi="21""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/drawable/ic_call_decline.xml"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `layout`.">
+ <location
+ file="src/main/res/layout-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="src/main/res/values-v21"/>
+ </issue>
+
+ <issue
id="KotlinPropertyAccess"
message="The getter return type (`AccessibilityNodeInfoCompat`) and setter parameter type (`View`) getter and setter methods for property `parent` should have exactly the same type to allow be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
errorLine1=" public AccessibilityNodeInfoCompat getParent() {"
diff --git a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
index 53ed826..25c6094 100644
--- a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
+++ b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
@@ -124,6 +124,34 @@
}
/**
+ * Create a typeface object given a font request. The font will be asynchronously fetched,
+ * therefore the result is delivered to the given callback. See {@link FontRequest}.
+ * Only one of the methods in callback will be invoked, depending on whether the request
+ * succeeds or fails. These calls will happen on the caller thread.
+ * @param context A context to be used for fetching from font provider.
+ * @param request A {@link FontRequest} object that identifies the provider and query for the
+ * request. May not be null.
+ * @param style Typeface Style such as {@link Typeface#NORMAL}, {@link Typeface#BOLD}
+ * {@link Typeface#ITALIC}, {@link Typeface#BOLD_ITALIC}.
+ * @param callback A callback that will be triggered when results are obtained. May not be null.
+ * @param handler A handler to be processed the font fetching.
+ */
+ // maintain consistency with legacy call signature above, just adding style
+ @SuppressWarnings("ExecutorRegistration")
+ public static void requestFont(
+ final @NonNull Context context,
+ final @NonNull FontRequest request,
+ int style,
+ final @NonNull FontRequestCallback callback,
+ @SuppressWarnings("ListenerLast") final @NonNull Handler handler
+ ) {
+ CallbackWithHandler callbackWrapper = new CallbackWithHandler(callback);
+ Executor executor = RequestExecutor.createHandlerExecutor(handler);
+ FontRequestWorker.requestFontAsync(context.getApplicationContext(), request, style,
+ executor, callbackWrapper);
+ }
+
+ /**
* Loads a Typeface. Based on the parameters isBlockingFetch, and timeoutInMillis, the fetch
* is either sync or async.
* - If timeoutInMillis is infinite, and isBlockingFetch is true -> sync
@@ -146,7 +174,8 @@
* sync request.
*
*/
- @RestrictTo(LIBRARY)
+ // called by Compose Fonts, never binary change
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
@Nullable
public static Typeface requestFont(
@NonNull final Context context,
diff --git a/core/core/src/main/java/androidx/core/text/HtmlCompat.java b/core/core/src/main/java/androidx/core/text/HtmlCompat.java
index 7ae8f24..61ec500 100644
--- a/core/core/src/main/java/androidx/core/text/HtmlCompat.java
+++ b/core/core/src/main/java/androidx/core/text/HtmlCompat.java
@@ -46,49 +46,49 @@
public final class HtmlCompat {
/**
* Option for {@link #fromHtml(String, int)}: Wrap consecutive lines of text delimited by '\n'
- * inside <p> elements. {@link BulletSpan}s are ignored.
+ * inside <code><p></code> elements. {@link BulletSpan}s are ignored.
*/
public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE =
Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE;
/**
* Option for {@link #fromHtml(String, int)}: Wrap each line of text delimited by '\n' inside a
- * <p> or a <li> element. This allows {@link ParagraphStyle}s attached to be
- * encoded as CSS styles within the corresponding <p> or <li> element.
+ * <code><p></code> or a <code><li></code> element. This allows {@link ParagraphStyle}s attached to be
+ * encoded as CSS styles within the corresponding <code><p></code> or <code><li></code> element.
*/
public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL =
Html.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL;
/**
- * Flag indicating that texts inside <p> elements will be separated from other texts with
+ * Flag indicating that texts inside <code><p></code> elements will be separated from other texts with
* one newline character by default.
*/
public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH =
Html.FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH;
/**
- * Flag indicating that texts inside <h1>~<h6> elements will be separated from
+ * Flag indicating that texts inside <code><h1></code>~<code><h6></code> elements will be separated from
* other texts with one newline character by default.
*/
public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING =
Html.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING;
/**
- * Flag indicating that texts inside <li> elements will be separated from other texts
+ * Flag indicating that texts inside <code><li></code> elements will be separated from other texts
* with one newline character by default.
*/
public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM =
Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM;
/**
- * Flag indicating that texts inside <ul> elements will be separated from other texts
+ * Flag indicating that texts inside <code><ul></code> elements will be separated from other texts
* with one newline character by default.
*/
public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST =
Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST;
/**
- * Flag indicating that texts inside <div> elements will be separated from other texts
+ * Flag indicating that texts inside <code><div><c/ode> elements will be separated from other texts
* with one newline character by default.
*/
public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV =
Html.FROM_HTML_SEPARATOR_LINE_BREAK_DIV;
/**
- * Flag indicating that texts inside <blockquote> elements will be separated from other
+ * Flag indicating that texts inside <code><blockquote></code> elements will be separated from other
* texts with one newline character by default.
*/
public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE =
diff --git a/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java b/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
index dee46e1..a490031 100644
--- a/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/SoftwareKeyboardControllerCompat.java
@@ -192,10 +192,10 @@
}
if (insetsController != null) {
insetsController.show(WindowInsets.Type.ime());
- } else {
- // Couldn't find an insets controller, fallback to old implementation
- super.show();
}
+ // InputMethodManager.showSoftInput() will also send the current toolType info to IME.
+ // We always call it so that it can report update toolType correctly.
+ super.show();
}
@Override
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
index ef3d133..f7400fa 100644
--- a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
@@ -113,15 +113,23 @@
@RequiresApi(30)
@Deprecated
private WindowInsetsControllerCompat(@NonNull WindowInsetsController insetsController) {
- mImpl = new Impl30(insetsController,
- this,
- new SoftwareKeyboardControllerCompat(insetsController));
+ if (SDK_INT >= 35) {
+ mImpl = new Impl35(insetsController,
+ this,
+ new SoftwareKeyboardControllerCompat(insetsController));
+ } else {
+ mImpl = new Impl30(insetsController,
+ this,
+ new SoftwareKeyboardControllerCompat(insetsController));
+ }
}
public WindowInsetsControllerCompat(@NonNull Window window, @NonNull View view) {
SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat =
new SoftwareKeyboardControllerCompat(view);
- if (SDK_INT >= 30) {
+ if (SDK_INT >= 35) {
+ mImpl = new Impl35(window, this, softwareKeyboardControllerCompat);
+ } else if (SDK_INT >= 30) {
mImpl = new Impl30(window, this, softwareKeyboardControllerCompat);
} else if (SDK_INT >= 26) {
mImpl = new Impl26(window, softwareKeyboardControllerCompat);
@@ -830,4 +838,33 @@
| systemUiFlag);
}
}
+
+ @RequiresApi(35)
+ private static class Impl35 extends Impl30 {
+
+ Impl35(@NonNull Window window,
+ @NonNull WindowInsetsControllerCompat compatController,
+ @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
+ super(window, compatController, softwareKeyboardControllerCompat);
+ }
+
+ Impl35(@NonNull WindowInsetsController insetsController,
+ @NonNull WindowInsetsControllerCompat compatController,
+ @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
+ super(insetsController, compatController, softwareKeyboardControllerCompat);
+ }
+
+ @Override
+ public boolean isAppearanceLightStatusBars() {
+ return (mInsetsController.getSystemBarsAppearance()
+ & WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS) != 0;
+ }
+
+ @Override
+ public boolean isAppearanceLightNavigationBars() {
+ return (mInsetsController.getSystemBarsAppearance()
+ & WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
+ }
+
+ }
}
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
index 53da03f..373ebd6 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -2115,7 +2115,7 @@
*/
@SuppressWarnings("ActionValue")
public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY =
- "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
+ "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
/**
* Integer argument specifying the start index of the requested text location data. Must be
@@ -2125,7 +2125,7 @@
*/
@SuppressWarnings("ActionValue")
public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX =
- "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+ "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
/**
* Integer argument specifying the end index of the requested text location data. Must be
@@ -2135,7 +2135,7 @@
*/
@SuppressWarnings("ActionValue")
public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH =
- "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
/**
* The maximum allowed length of the requested text location data.
diff --git a/core/core/src/test/java/androidx/core/util/SizeFCompatTest.java b/core/core/src/test/java/androidx/core/util/SizeFCompatTest.java
index 0b94e93..31d5b64 100644
--- a/core/core/src/test/java/androidx/core/util/SizeFCompatTest.java
+++ b/core/core/src/test/java/androidx/core/util/SizeFCompatTest.java
@@ -31,7 +31,7 @@
@RunWith(RobolectricTestRunner.class)
@DoNotInstrument
-@Config(sdk = {19, 21})
+@Config(sdk = 21)
public class SizeFCompatTest {
@Test
diff --git a/core/core/src/test/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java b/core/core/src/test/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
new file mode 100644
index 0000000..6a72ba0
--- /dev/null
+++ b/core/core/src/test/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 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.core.view.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+@SmallTest
+public final class AccessibilityNodeInfoCompatTest {
+
+ @Test
+ @Config(minSdk = 26)
+ public void verify_extraDataTextConstants() {
+ assertThat(AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY).isEqualTo(
+ AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
+ );
+
+ assertThat(AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX)
+ .isEqualTo(AccessibilityNodeInfoCompat
+ .EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX
+ );
+
+ assertThat(AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH).isEqualTo(
+ AccessibilityNodeInfoCompat.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH
+ );
+ }
+}
diff --git a/core/haptics/haptics/integration-tests/demos/build.gradle b/core/haptics/haptics/integration-tests/demos/build.gradle
index 3bf7a05..3effc26 100644
--- a/core/haptics/haptics/integration-tests/demos/build.gradle
+++ b/core/haptics/haptics/integration-tests/demos/build.gradle
@@ -30,7 +30,6 @@
android {
defaultConfig {
applicationId "androidx.core.haptics.demos"
- minSdkVersion 19
multiDexEnabled true
}
namespace "androidx.core.haptics.demos"
diff --git a/core/haptics/haptics/lint-baseline.xml b/core/haptics/haptics/lint-baseline.xml
new file mode 100644
index 0000000..b186ddb
--- /dev/null
+++ b/core/haptics/haptics/lint-baseline.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/haptics/HapticAttributes.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/haptics/HapticAttributes.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/haptics/impl/HapticAttributesConverter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/haptics/impl/HapticAttributesConverter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/haptics/impl/HapticAttributesConverter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/haptics/impl/HapticAttributesConverter.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/haptics/extensions/HapticAttributesUtils.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/haptics/impl/VibratorWrapperImpl.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/core/haptics/impl/VibratorWrapperImpl.kt"/>
+ </issue>
+
+</issues>
diff --git a/credentials/credentials-fido/build.gradle b/credentials/credentials-fido/build.gradle
index f2ac41e..81f83b4 100644
--- a/credentials/credentials-fido/build.gradle
+++ b/credentials/credentials-fido/build.gradle
@@ -50,7 +50,6 @@
namespace "androidx.credentials.fido"
defaultConfig {
- minSdkVersion 19
multiDexEnabled = true
}
}
diff --git a/credentials/credentials-play-services-auth/build.gradle b/credentials/credentials-play-services-auth/build.gradle
index 1199346..19dc95b 100644
--- a/credentials/credentials-play-services-auth/build.gradle
+++ b/credentials/credentials-play-services-auth/build.gradle
@@ -71,7 +71,6 @@
}
defaultConfig {
- minSdkVersion 19
multiDexEnabled = true
}
}
diff --git a/credentials/credentials-play-services-auth/lint-baseline.xml b/credentials/credentials-play-services-auth/lint-baseline.xml
index 25997f7..85ca277 100644
--- a/credentials/credentials-play-services-auth/lint-baseline.xml
+++ b/credentials/credentials-play-services-auth/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="UsesNonDefaultVisibleForTesting"
@@ -127,4 +127,11 @@
file="src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="src/main/res/values-v21"/>
+ </issue>
+
</issues>
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
index d4232ce..93b6872 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
@@ -50,12 +50,12 @@
*/
@Suppress("deprecation")
internal class CredentialProviderCreatePublicKeyCredentialController(private val context: Context) :
- CredentialProviderController<
- CreatePublicKeyCredentialRequest,
- PublicKeyCredentialCreationOptions,
- PublicKeyCredential,
- CreateCredentialResponse,
- CreateCredentialException>(context) {
+ CredentialProviderController<
+ CreatePublicKeyCredentialRequest,
+ PublicKeyCredentialCreationOptions,
+ PublicKeyCredential,
+ CreateCredentialResponse,
+ CreateCredentialException>(context) {
/**
* The callback object state, used in the protected handleResponse method.
@@ -84,12 +84,17 @@
resultCode: Int,
resultData: Bundle
) {
- if (maybeReportErrorFromResultReceiver(resultData,
+ if (maybeReportErrorFromResultReceiver(
+ resultData,
CredentialProviderBaseController
.Companion::createCredentialExceptionTypeToException,
- executor = executor, callback = callback, cancellationSignal)) return
- handleResponse(resultData.getInt(ACTIVITY_REQUEST_CODE_TAG), resultCode,
- resultData.getParcelable(RESULT_DATA_TAG))
+ executor = executor, callback = callback, cancellationSignal
+ )
+ ) return
+ handleResponse(
+ resultData.getInt(ACTIVITY_REQUEST_CODE_TAG), resultCode,
+ resultData.getParcelable(RESULT_DATA_TAG)
+ )
}
}
@@ -106,13 +111,18 @@
try {
fidoRegistrationRequest = this.convertRequestToPlayServices(request)
} catch (e: JSONException) {
- cancelOrCallbackExceptionOrResult(cancellationSignal) { this.executor.execute {
- this.callback.onError(JSONExceptionToPKCError(e))
- } }
+ cancelOrCallbackExceptionOrResult(cancellationSignal) {
+ this.executor.execute {
+ this.callback.onError(JSONExceptionToPKCError(e))
+ }
+ }
return
} catch (t: Throwable) {
- cancelOrCallbackExceptionOrResult(cancellationSignal) { this.executor.execute {
- this.callback.onError(CreateCredentialUnknownException(t.message)) } }
+ cancelOrCallbackExceptionOrResult(cancellationSignal) {
+ this.executor.execute {
+ this.callback.onError(CreateCredentialUnknownException(t.message))
+ }
+ }
return
}
@@ -121,37 +131,53 @@
}
val hiddenIntent = Intent(context, HiddenActivity::class.java)
hiddenIntent.putExtra(REQUEST_TAG, fidoRegistrationRequest)
- generateHiddenActivityIntent(resultReceiver, hiddenIntent,
- CREATE_PUBLIC_KEY_CREDENTIAL_TAG)
+ generateHiddenActivityIntent(
+ resultReceiver, hiddenIntent,
+ CREATE_PUBLIC_KEY_CREDENTIAL_TAG
+ )
try {
context.startActivity(hiddenIntent)
} catch (e: Exception) {
- cancelOrCallbackExceptionOrResult(cancellationSignal) { this.executor.execute {
- this.callback.onError(
- CreateCredentialUnknownException(ERROR_MESSAGE_START_ACTIVITY_FAILED)) } }
+ cancelOrCallbackExceptionOrResult(cancellationSignal) {
+ this.executor.execute {
+ this.callback.onError(
+ CreateCredentialUnknownException(ERROR_MESSAGE_START_ACTIVITY_FAILED)
+ )
+ }
+ }
}
}
internal fun handleResponse(uniqueRequestCode: Int, resultCode: Int, data: Intent?) {
if (uniqueRequestCode != CONTROLLER_REQUEST_CODE) {
- Log.w(TAG, "Returned request code " +
- "$CONTROLLER_REQUEST_CODE does not match what was given $uniqueRequestCode")
+ Log.w(
+ TAG, "Returned request code " +
+ "$CONTROLLER_REQUEST_CODE does not match what was given $uniqueRequestCode"
+ )
return
}
if (maybeReportErrorResultCodeCreate(resultCode,
- { s, f -> cancelOrCallbackExceptionOrResult(s, f) }, { e -> this.executor.execute {
- this.callback.onError(e) } }, cancellationSignal)) return
+ { s, f -> cancelOrCallbackExceptionOrResult(s, f) }, { e ->
+ this.executor.execute {
+ this.callback.onError(e)
+ }
+ }, cancellationSignal
+ )
+ ) return
val bytes: ByteArray? = data?.getByteArrayExtra(Fido.FIDO2_KEY_CREDENTIAL_EXTRA)
if (bytes == null) {
if (CredentialProviderPlayServicesImpl.cancellationReviewer(cancellationSignal)) {
return
}
- this.executor.execute { this.callback.onError(
- CreatePublicKeyCredentialDomException(
- UnknownError(),
- "Upon handling create public key credential response, fido module giving null " +
- "bytes indicating internal error")
- ) }
+ this.executor.execute {
+ this.callback.onError(
+ CreatePublicKeyCredentialDomException(
+ UnknownError(),
+ "Upon handling create public key credential response, fido module giving" +
+ " null bytes indicating internal error"
+ )
+ )
+ }
return
}
val cred: PublicKeyCredential = PublicKeyCredential.deserializeFromBytes(bytes)
@@ -159,39 +185,57 @@
PublicKeyCredentialControllerUtility.publicKeyCredentialResponseContainsError(cred)
if (exception != null) {
cancelOrCallbackExceptionOrResult(cancellationSignal) {
- executor.execute { callback.onError(exception) } }
+ executor.execute { callback.onError(exception) }
+ }
return
}
try {
val response = this.convertResponseToCredentialManager(cred)
- cancelOrCallbackExceptionOrResult(cancellationSignal) { this.executor.execute {
- this.callback.onResult(response) } }
+ cancelOrCallbackExceptionOrResult(cancellationSignal) {
+ this.executor.execute {
+ this.callback.onResult(response)
+ }
+ }
} catch (e: JSONException) {
cancelOrCallbackExceptionOrResult(
- cancellationSignal) { executor.execute { callback.onError(
- CreatePublicKeyCredentialDomException(EncodingError(), e.message)) } }
+ cancellationSignal
+ ) {
+ executor.execute {
+ callback.onError(
+ CreatePublicKeyCredentialDomException(EncodingError(), e.message)
+ )
+ }
+ }
} catch (t: Throwable) {
- cancelOrCallbackExceptionOrResult(cancellationSignal) { executor.execute {
- callback.onError(CreatePublicKeyCredentialDomException(
- UnknownError(), t.message)) } }
+ cancelOrCallbackExceptionOrResult(cancellationSignal) {
+ executor.execute {
+ callback.onError(
+ CreatePublicKeyCredentialDomException(
+ UnknownError(), t.message
+ )
+ )
+ }
+ }
}
}
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public override fun convertRequestToPlayServices(request: CreatePublicKeyCredentialRequest):
PublicKeyCredentialCreationOptions {
- return PublicKeyCredentialControllerUtility.convert(request)
+ return PublicKeyCredentialControllerUtility.convert(request, context)
}
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public override fun convertResponseToCredentialManager(response: PublicKeyCredential):
CreateCredentialResponse {
- try {
- return CreatePublicKeyCredentialResponse(response.toJson())
- } catch (t: Throwable) {
- throw CreateCredentialUnknownException("The PublicKeyCredential response json " +
- "had an unexpected exception when parsing: ${t.message}")
- }
+ try {
+ return CreatePublicKeyCredentialResponse(response.toJson())
+ } catch (t: Throwable) {
+ throw CreateCredentialUnknownException(
+ "The PublicKeyCredential response json " +
+ "had an unexpected exception when parsing: ${t.message}"
+ )
+ }
}
private fun JSONExceptionToPKCError(exception: JSONException):
@@ -215,7 +259,7 @@
@JvmStatic
fun getInstance(context: Context):
CredentialProviderCreatePublicKeyCredentialController {
- return CredentialProviderCreatePublicKeyCredentialController(context)
+ return CredentialProviderCreatePublicKeyCredentialController(context)
}
}
}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
index b38500b..9b2544e 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
@@ -16,8 +16,14 @@
package androidx.credentials.playservices.controllers.CreatePublicKeyCredential
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.os.Build
import android.util.Base64
import android.util.Log
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
import androidx.credentials.CreatePublicKeyCredentialRequest
import androidx.credentials.GetPublicKeyCredentialOption
import androidx.credentials.exceptions.CreateCredentialCancellationException
@@ -41,6 +47,8 @@
import androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialDomException
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.SignInCredential
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.fido.common.Transport
import com.google.android.gms.fido.fido2.api.common.Attachment
import com.google.android.gms.fido.fido2.api.common.AttestationConveyancePreference
@@ -106,6 +114,8 @@
internal val JSON_KEY_RK = "rk"
internal val JSON_KEY_CRED_PROPS = "credProps"
+ private const val AUTH_MIN_VERSION_JSON_CREATE: Long = 241217000
+
/**
* This function converts a request json to a PublicKeyCredentialCreationOptions, where there
* should be a direct mapping from the input string to this data type. See
@@ -117,10 +127,35 @@
* @throws JSONException If required data is not present in the requestJson
*/
@JvmStatic
- fun convert(request: CreatePublicKeyCredentialRequest): PublicKeyCredentialCreationOptions {
+ fun convert(
+ request: CreatePublicKeyCredentialRequest,
+ context: Context
+ ): PublicKeyCredentialCreationOptions {
+ if (isDeviceGMSVersionOlderThan(context, AUTH_MIN_VERSION_JSON_CREATE)) {
+ return PublicKeyCredentialCreationOptions(request.requestJson)
+ }
+
return convertJSON(JSONObject(request.requestJson))
}
+ private fun isDeviceGMSVersionOlderThan(context: Context, version: Long): Boolean {
+ // Only do the version check if GMS is available, otherwise return false falling back to
+ // previous flow.
+ if (GoogleApiAvailability.getInstance()
+ .isGooglePlayServicesAvailable(context) != ConnectionResult.SUCCESS
+ ) return false;
+
+ val packageManager: PackageManager = context.packageManager
+ val packageName = GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE
+
+ val currentVersion = if (Build.VERSION.SDK_INT >= 28)
+ GetGMSVersion.getVersionLong(packageManager.getPackageInfo(packageName, 0))
+ else
+ @Suppress("DEPRECATION") packageManager.getPackageInfo(packageName, 0).versionCode.toLong()
+
+ return (currentVersion > version)
+ }
+
internal fun convertJSON(json: JSONObject): PublicKeyCredentialCreationOptions {
val builder = PublicKeyCredentialCreationOptions.Builder()
@@ -160,19 +195,23 @@
authenticatorResponse.errorMessage
)
}
+
is AuthenticatorAssertionResponse -> {
try {
return publicKeyCred.toJson()
} catch (t: Throwable) {
- throw GetCredentialUnknownException("The PublicKeyCredential response json had " +
- "an unexpected exception when parsing: ${t.message}")
+ throw GetCredentialUnknownException(
+ "The PublicKeyCredential response json had " +
+ "an unexpected exception when parsing: ${t.message}"
+ )
}
}
+
else -> {
Log.e(
TAG,
"AuthenticatorResponse expected assertion response but " +
- "got: ${authenticatorResponse.javaClass.name}"
+ "got: ${authenticatorResponse.javaClass.name}"
)
}
}
@@ -279,7 +318,10 @@
val exception: GetCredentialException
if (exceptionError == null) {
exception =
- GetPublicKeyCredentialDomException(UnknownError(), "unknown fido gms exception - $msg")
+ GetPublicKeyCredentialDomException(
+ UnknownError(),
+ "unknown fido gms exception - $msg"
+ )
} else {
// This fix is quite fragile because it relies on that the fido module
// does not change its error message, but is the only viable solution
@@ -307,7 +349,8 @@
if (appIdExtension.isNotEmpty()) {
extensionBuilder.setFido2Extension(FidoAppIdExtension(appIdExtension))
}
- val thirdPartyPaymentExtension = extensions.optBoolean(JSON_KEY_THIRD_PARTY_PAYMENT, false)
+ val thirdPartyPaymentExtension =
+ extensions.optBoolean(JSON_KEY_THIRD_PARTY_PAYMENT, false)
if (thirdPartyPaymentExtension) {
extensionBuilder.setGoogleThirdPartyPaymentExtension(
GoogleThirdPartyPaymentExtension(true)
@@ -315,7 +358,9 @@
}
val uvmStatus = extensions.optBoolean("uvm", false)
if (uvmStatus) {
- extensionBuilder.setUserVerificationMethodExtension(UserVerificationMethodExtension(true))
+ extensionBuilder.setUserVerificationMethodExtension(
+ UserVerificationMethodExtension(true)
+ )
}
builder.setAuthenticationExtensions(extensionBuilder.build())
}
@@ -328,7 +373,8 @@
if (json.has(JSON_KEY_AUTH_SELECTION)) {
val authenticatorSelection = json.getJSONObject(JSON_KEY_AUTH_SELECTION)
val authSelectionBuilder = AuthenticatorSelectionCriteria.Builder()
- val requireResidentKey = authenticatorSelection.optBoolean(JSON_KEY_REQUIRE_RES_KEY, false)
+ val requireResidentKey =
+ authenticatorSelection.optBoolean(JSON_KEY_REQUIRE_RES_KEY, false)
val residentKey = authenticatorSelection.optString(JSON_KEY_RES_KEY, "")
var residentKeyRequirement: ResidentKeyRequirement? = null
if (residentKey.isNotEmpty()) {
@@ -340,7 +386,11 @@
val authenticatorAttachmentString =
authenticatorSelection.optString(JSON_KEY_AUTH_ATTACHMENT, "")
if (authenticatorAttachmentString.isNotEmpty()) {
- authSelectionBuilder.setAttachment(Attachment.fromString(authenticatorAttachmentString))
+ authSelectionBuilder.setAttachment(
+ Attachment.fromString(
+ authenticatorAttachmentString
+ )
+ )
}
builder.setAuthenticatorSelection(authSelectionBuilder.build())
}
@@ -385,7 +435,10 @@
try {
transports.add(Transport.fromString(descriptorTransports.getString(j)))
} catch (e: Transport.UnsupportedTransportException) {
- throw CreatePublicKeyCredentialDomException(EncodingError(), e.message)
+ throw CreatePublicKeyCredentialDomException(
+ EncodingError(),
+ e.message
+ )
}
}
}
@@ -510,7 +563,8 @@
try {
COSEAlgorithmIdentifier.fromCoseValue(alg)
return true
- } catch (_: Throwable) {}
+ } catch (_: Throwable) {
+ }
return false
}
@@ -532,4 +586,11 @@
ErrorCode.TIMEOUT_ERR to TimeoutError()
)
}
+
+ @RequiresApi(28)
+ private object GetGMSVersion {
+ @JvmStatic
+ @DoNotInline
+ fun getVersionLong(info: PackageInfo): Long = info.getLongVersionCode()
+ }
}
diff --git a/credentials/credentials-provider/build.gradle b/credentials/credentials-provider/build.gradle
index 9f79fc5..2fc475a 100644
--- a/credentials/credentials-provider/build.gradle
+++ b/credentials/credentials-provider/build.gradle
@@ -38,7 +38,6 @@
namespace "androidx.credentials.provider"
defaultConfig {
- minSdkVersion 19
}
}
diff --git a/credentials/credentials/build.gradle b/credentials/credentials/build.gradle
index d267a0f..ec5f874 100644
--- a/credentials/credentials/build.gradle
+++ b/credentials/credentials/build.gradle
@@ -52,7 +52,6 @@
namespace "androidx.credentials"
defaultConfig {
- minSdkVersion 19
}
}
diff --git a/credentials/credentials/samples/build.gradle b/credentials/credentials/samples/build.gradle
index 7f1665e..b0b0178 100644
--- a/credentials/credentials/samples/build.gradle
+++ b/credentials/credentials/samples/build.gradle
@@ -33,7 +33,6 @@
namespace "androidx.credentials.samples"
defaultConfig {
- minSdkVersion 19
}
}
diff --git a/datastore/datastore-compose-samples/build.gradle b/datastore/datastore-compose-samples/build.gradle
index 698b179..2e5f736 100644
--- a/datastore/datastore-compose-samples/build.gradle
+++ b/datastore/datastore-compose-samples/build.gradle
@@ -33,9 +33,10 @@
}
dependencies {
+ compileOnly(projectOrArtifact(":datastore:datastore-preferences-external-protobuf"))
+
implementation(libs.protobufLite)
implementation(libs.kotlinStdlib)
-
implementation('androidx.core:core-ktx:1.7.0')
implementation('androidx.lifecycle:lifecycle-runtime-ktx:2.3.1')
implementation('androidx.activity:activity-compose:1.3.1')
diff --git a/datastore/datastore-core/src/androidMain/cpp/CMakeLists.txt b/datastore/datastore-core/src/androidMain/cpp/CMakeLists.txt
index a6bd4f3..28fe6f7 100644
--- a/datastore/datastore-core/src/androidMain/cpp/CMakeLists.txt
+++ b/datastore/datastore-core/src/androidMain/cpp/CMakeLists.txt
@@ -14,7 +14,7 @@
# the License.
#
-cmake_minimum_required(VERSION 3.22)
+cmake_minimum_required(VERSION 3.22.1)
project(datastore_shared_counter)
diff --git a/datastore/datastore-preferences-core/build.gradle b/datastore/datastore-preferences-core/build.gradle
index cff152d..8dacda8 100644
--- a/datastore/datastore-preferences-core/build.gradle
+++ b/datastore/datastore-preferences-core/build.gradle
@@ -21,7 +21,6 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.BundleInsideHelper
import androidx.build.LibraryType
import androidx.build.PlatformIdentifier
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
@@ -67,6 +66,9 @@
}
jvmMain {
dependsOn(commonMain)
+ dependencies {
+ implementation(project(":datastore:datastore-preferences-proto"))
+ }
}
jvmTest {
dependsOn(commonTest)
@@ -107,19 +109,6 @@
}
}
-BundleInsideHelper.forInsideJarKmp(
- project,
- /* from = */ "com.google.protobuf",
- /* to = */ "androidx.datastore.preferences.protobuf",
- // proto-lite dependency includes .proto files, which are not used and would clash if
- // users also use proto library directly
- /* dropResourcesWithSuffix = */ ".proto"
-)
-
-dependencies {
- bundleInside(project(":datastore:datastore-preferences-proto"))
-}
-
androidx {
name = "Preferences DataStore Core"
type = LibraryType.PUBLISHED_LIBRARY
diff --git a/datastore/datastore-preferences-external-protobuf/api/current.txt b/datastore/datastore-preferences-external-protobuf/api/current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/datastore/datastore-preferences-external-protobuf/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/datastore/datastore-preferences-external-protobuf/api/restricted_current.txt b/datastore/datastore-preferences-external-protobuf/api/restricted_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/datastore/datastore-preferences-external-protobuf/api/restricted_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/datastore/datastore-preferences-external-protobuf/build.gradle b/datastore/datastore-preferences-external-protobuf/build.gradle
new file mode 100644
index 0000000..506da76
--- /dev/null
+++ b/datastore/datastore-preferences-external-protobuf/build.gradle
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+import androidx.build.LibraryType
+
+plugins {
+ id("AndroidXPlugin")
+ id("AndroidXRepackagePlugin")
+ id("java-library")
+}
+
+repackage {
+ addRelocation {
+ sourcePackage = "com.google.protobuf"
+ targetPackage = "androidx.datastore.preferences.protobuf"
+ }
+ artifactId = "datastore-preferences-external-protobuf"
+}
+
+dependencies {
+ repackage(libs.protobufLite)
+}
+
+androidx {
+ name = "Preferences External Protobuf"
+ type = LibraryType.PUBLISHED_LIBRARY
+ inceptionYear = "2024"
+ description = "Repackaged proto-lite dependency for use by datastore preferences"
+ doNotDocumentReason = "Repackaging only"
+ license.name = "BSD-3-Clause"
+ license.url = "https://opensource.org/licenses/BSD-3-Clause"
+}
diff --git a/datastore/datastore-preferences-proto/build.gradle b/datastore/datastore-preferences-proto/build.gradle
index d5b5bb1..860174f 100644
--- a/datastore/datastore-preferences-proto/build.gradle
+++ b/datastore/datastore-preferences-proto/build.gradle
@@ -21,17 +21,30 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import androidx.build.LibraryType
+import androidx.build.RunApiTasks
plugins {
id("AndroidXPlugin")
+ id("AndroidXRepackagePlugin")
id("kotlin")
id("com.google.protobuf")
}
+repackage {
+ // Must match what is in datastore/datastore-preferences-external-protobuf/build.gradle
+ addRelocation {
+ sourcePackage = "com.google.protobuf"
+ targetPackage = "androidx.datastore.preferences.protobuf"
+ }
+}
+
dependencies {
- implementation(libs.protobufLite)
+ api(project(":datastore:datastore-preferences-external-protobuf"))
+ // Must be compileOnly to not bring in protobufLite in runtime
+ // Repackaged protobufLite brought in by
+ // project(":datastore:datastore-preferences-external-protobuf") and used at runtime
+ compileOnly(libs.protobufLite)
compileOnly(project(":datastore:datastore-core"))
}
@@ -61,8 +74,9 @@
androidx {
name = "Preferences DataStore Proto"
- publish = Publish.NONE
+ type = LibraryType.PUBLISHED_LIBRARY
inceptionYear = "2020"
- description = "Jarjar the generated proto and proto-lite dependency for use by " +
- "datastore-preferences."
+ description = "Jarjar the generated proto for use by datastore-preferences."
+ runApiTasks = new RunApiTasks.No("Metalava doesn't properly parse the proto sources " +
+ "(b/180579063)")
}
diff --git a/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt b/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt
index 89f79a8..1e8cb27 100644
--- a/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt
+++ b/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt
@@ -29,7 +29,6 @@
import org.gradle.api.artifacts.result.ResolvedArtifactResult
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
-import org.gradle.api.attributes.Bundling
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.Usage
@@ -317,7 +316,6 @@
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, libraryElement)
attribute(Usage.USAGE_ATTRIBUTE, Usage.JAVA_RUNTIME)
attribute(Category.CATEGORY_ATTRIBUTE, Category.LIBRARY)
- attribute(Bundling.BUNDLING_ATTRIBUTE, Bundling.EXTERNAL)
attribute(
TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
jvmEnvironment
@@ -339,7 +337,6 @@
createConfiguration(*dependencies) {
attributes.apply {
attribute(Category.CATEGORY_ATTRIBUTE, Category.LIBRARY)
- attribute(Bundling.BUNDLING_ATTRIBUTE, Bundling.EXTERNAL)
attribute(
TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
TargetJvmEnvironment.STANDARD_JVM
@@ -374,7 +371,6 @@
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, libraryElement)
attribute(Usage.USAGE_ATTRIBUTE, Usage.JAVA_API)
attribute(Category.CATEGORY_ATTRIBUTE, Category.LIBRARY)
- attribute(Bundling.BUNDLING_ATTRIBUTE, Bundling.EXTERNAL)
}
}
}
diff --git a/development/project-creator/native-template/groupId/artifactId/src/main/cpp/CMakeLists.txt b/development/project-creator/native-template/groupId/artifactId/src/main/cpp/CMakeLists.txt
index 3c1ddca..c1ae2b9 100644
--- a/development/project-creator/native-template/groupId/artifactId/src/main/cpp/CMakeLists.txt
+++ b/development/project-creator/native-template/groupId/artifactId/src/main/cpp/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10.2)
+cmake_minimum_required(VERSION 3.22.1)
project(<NAME> LANGUAGES CXX)
diff --git a/docs/api_guidelines/dependencies.md b/docs/api_guidelines/dependencies.md
index 770ad70..74b88cd 100644
--- a/docs/api_guidelines/dependencies.md
+++ b/docs/api_guidelines/dependencies.md
@@ -299,6 +299,11 @@
#### Protobuf {#dependencies-protobuf}
+**Note**: It is preferred to use the [`wire`](https://github.com/square/wire)
+library for handling protocol buffers in Android libraries as it has a binary
+stable runtime. An example of its usage can be found
+[here](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:benchmark/benchmark-common/build.gradle?q=wireRuntime%20file:gradle&ss=androidx%2Fplatform%2Fframeworks%2Fsupport).
+
[Protocol buffers](https://developers.google.com/protocol-buffers) provide a
language- and platform-neutral mechanism for serializing structured data. The
implementation enables developers to maintain protocol compatibility across
@@ -306,8 +311,18 @@
library versions included in their APKs.
The Protobuf library itself, however, does not guarantee ABI compatibility
-across minor versions and a specific version **must** be bundled with a library
-to avoid conflict with other dependencies used by the developer.
+across minor versions and a specific version **must** be used with a library to
+avoid conflict with other dependencies used by the developer. To do this, you
+must first create a new project to repackage the protobuf runtime classes, and
+then have it as a dependency in the project you generate protos in. In the
+project that generates protos, you must also relocate any import statements
+containing `com.google.protobuf` to your target package name. The
+[AndroidXRepackagePlugin](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/private/src/main/kotlin/androidx/build/AndroidXRepackageImplPlugin.kt)
+abstracts this for you. An example of its use to repackage the protobuf runtime
+library can be found
+[here](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:wear/protolayout/protolayout-external-protobuf/build.gradle)
+and its associated use in the library that generates protos can be found
+[here](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:wear/protolayout/protolayout-proto/build.gradle).
Additionally, the Java API surface generated by the Protobuf compiler is not
guaranteed to be stable and **must not** be exposed to developers. Library
diff --git a/documentfile/documentfile/lint-baseline.xml b/documentfile/documentfile/lint-baseline.xml
new file mode 100644
index 0000000..e6ea9a2
--- /dev/null
+++ b/documentfile/documentfile/lint-baseline.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/documentfile/provider/DocumentFile.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/documentfile/provider/TreeDocumentFile.java"/>
+ </issue>
+
+</issues>
diff --git a/drawerlayout/drawerlayout/lint-baseline.xml b/drawerlayout/drawerlayout/lint-baseline.xml
new file mode 100644
index 0000000..b56cbd5
--- /dev/null
+++ b/drawerlayout/drawerlayout/lint-baseline.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" Build.VERSION.SDK_INT >= 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/drawerlayout/widget/DrawerLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/drawerlayout/widget/DrawerLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/drawerlayout/widget/DrawerLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/drawerlayout/widget/DrawerLayout.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/drawerlayout/widget/DrawerLayout.java"/>
+ </issue>
+
+</issues>
diff --git a/dynamicanimation/dynamicanimation-ktx/build.gradle b/dynamicanimation/dynamicanimation-ktx/build.gradle
index 3271478..326d890 100644
--- a/dynamicanimation/dynamicanimation-ktx/build.gradle
+++ b/dynamicanimation/dynamicanimation-ktx/build.gradle
@@ -45,7 +45,7 @@
androidx {
name = "Dynamic animation Kotlin Extensions"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.DYNAMICANIMATION_KTX
inceptionYear = "2018"
description = "Kotlin extensions for 'dynamicanimation' artifact"
diff --git a/emoji/emoji/lint-baseline.xml b/emoji/emoji/lint-baseline.xml
index 42964c9..02eb4a3 100644
--- a/emoji/emoji/lint-baseline.xml
+++ b/emoji/emoji/lint-baseline.xml
@@ -1,56 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.emoji.widget.EmojiButton is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/emoji/widget/EmojiButton.java"/>
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.emoji.widget.EmojiEditText is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/emoji/widget/EmojiEditText.java"/>
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.emoji.widget.EmojiExtractEditText is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/emoji/widget/EmojiExtractEditText.java"/>
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.emoji.widget.EmojiExtractTextLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/emoji/widget/EmojiExtractTextLayout.java"/>
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.emoji.widget.EmojiTextView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/emoji/widget/EmojiTextView.java"/>
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.emoji.widget.ExtractButtonCompat is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/emoji/widget/ExtractButtonCompat.java"/>
</issue>
diff --git a/exifinterface/exifinterface/lint-baseline.xml b/exifinterface/exifinterface/lint-baseline.xml
new file mode 100644
index 0000000..a35f8aa
--- /dev/null
+++ b/exifinterface/exifinterface/lint-baseline.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21 && isSeekableFD(fileDescriptor)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/exifinterface/media/ExifInterface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/exifinterface/media/ExifInterface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/exifinterface/media/ExifInterface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/exifinterface/media/ExifInterface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/exifinterface/media/ExifInterface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/exifinterface/media/ExifInterface.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/exifinterface/media/ExifInterfaceUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/exifinterface/media/ExifInterfaceUtils.java"/>
+ </issue>
+
+</issues>
diff --git a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
index 48c1ab5..1e4745c 100644
--- a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
+++ b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
@@ -643,6 +643,290 @@
}
@Test
+ @SmallTest
+ public void testSetFNumber_decimalString() throws Exception {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ String value = "1.4";
+ exifInterface.setAttribute(ExifInterface.TAG_F_NUMBER, value);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_F_NUMBER)).isEqualTo(value);
+ double result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_F_NUMBER, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(1.4);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_F_NUMBER)).isEqualTo(value);
+ result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_F_NUMBER, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(1.4);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetFNumber_rationalString() throws Exception {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ exifInterface.setAttribute(ExifInterface.TAG_F_NUMBER, "7/5");
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_F_NUMBER)).isEqualTo("1.4");
+ double result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_F_NUMBER, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(1.4);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_F_NUMBER)).isEqualTo("1.4");
+ result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_F_NUMBER, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(1.4);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetDigitalZoomRatio_decimalString() throws Exception {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ String value = "0.8";
+ exifInterface.setAttribute(ExifInterface.TAG_DIGITAL_ZOOM_RATIO, value);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_DIGITAL_ZOOM_RATIO))
+ .isEqualTo("0.8");
+ double result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_DIGITAL_ZOOM_RATIO, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(0.8);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_DIGITAL_ZOOM_RATIO))
+ .isEqualTo("0.8");
+ result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_DIGITAL_ZOOM_RATIO, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(0.8);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetDigitalZoomRatio_rationalString() throws Exception {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ exifInterface.setAttribute(ExifInterface.TAG_DIGITAL_ZOOM_RATIO, "12/5");
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_DIGITAL_ZOOM_RATIO))
+ .isEqualTo("2.4");
+ double result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_DIGITAL_ZOOM_RATIO, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(2.4);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_DIGITAL_ZOOM_RATIO))
+ .isEqualTo("2.4");
+ result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_DIGITAL_ZOOM_RATIO, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(2.4);
+ }
+
+ // https://issuetracker.google.com/312680558
+ @Test
+ @SmallTest
+ public void testSetExposureTime_decimalString() throws Exception {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ exifInterface.setAttribute(ExifInterface.TAG_EXPOSURE_TIME, "0.000625");
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_EXPOSURE_TIME))
+ .isEqualTo("6.25E-4");
+ double result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_EXPOSURE_TIME, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(0.000625);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_EXPOSURE_TIME))
+ .isEqualTo("6.25E-4");
+ result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_EXPOSURE_TIME, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(0.000625);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetExposureTime_rationalString() throws Exception {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ exifInterface.setAttribute(ExifInterface.TAG_EXPOSURE_TIME, "1/1600");
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_EXPOSURE_TIME))
+ .isEqualTo("6.25E-4");
+ double result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_EXPOSURE_TIME, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(0.000625);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_EXPOSURE_TIME))
+ .isEqualTo("6.25E-4");
+ result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_EXPOSURE_TIME, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(0.000625);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetSubjectDistance_decimalString() throws Exception {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ String value = "3.5";
+ exifInterface.setAttribute(ExifInterface.TAG_SUBJECT_DISTANCE, value);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_SUBJECT_DISTANCE)).isEqualTo(value);
+ double result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_SUBJECT_DISTANCE, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(3.5);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_SUBJECT_DISTANCE)).isEqualTo(value);
+ result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_SUBJECT_DISTANCE, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(3.5);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetSubjectDistance_rationalString() throws Exception {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ exifInterface.setAttribute(ExifInterface.TAG_SUBJECT_DISTANCE, "7/2");
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_SUBJECT_DISTANCE)).isEqualTo("3.5");
+ double result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_SUBJECT_DISTANCE, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(3.5);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_SUBJECT_DISTANCE)).isEqualTo("3.5");
+ result =
+ exifInterface.getAttributeDouble(
+ ExifInterface.TAG_SUBJECT_DISTANCE, /* defaultValue= */ -1);
+ assertThat(result).isEqualTo(3.5);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetGpsTimestamp_integers() throws Exception {
+ // Deliberately use an image with an existing GPS timestamp value to overwrite.
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_with_xmp, "jpeg_with_exif_with_xmp.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ String timestamp = "11:06:52";
+ exifInterface.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, timestamp);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP))
+ .isEqualTo(timestamp);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP))
+ .isEqualTo(timestamp);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetGpsTimestamp_rationals_failsSilently() throws Exception {
+ // Deliberately use an image with an existing GPS timestamp value to overwrite.
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_with_xmp, "jpeg_with_exif_with_xmp.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ exifInterface.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, "11/2:06/5:52/8");
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP))
+ .isEqualTo(ExpectedAttributes.JPEG_WITH_EXIF_WITH_XMP.gpsTimestamp);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP))
+ .isEqualTo(ExpectedAttributes.JPEG_WITH_EXIF_WITH_XMP.gpsTimestamp);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetGpsTimestamp_decimals_failsSilently() throws Exception {
+ // Deliberately use an image with an existing GPS timestamp value to overwrite.
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_with_xmp, "jpeg_with_exif_with_xmp.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile);
+
+ exifInterface.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, "11.5:06.3:52.8");
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP))
+ .isEqualTo(ExpectedAttributes.JPEG_WITH_EXIF_WITH_XMP.gpsTimestamp);
+
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile);
+
+ assertThat(exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP))
+ .isEqualTo(ExpectedAttributes.JPEG_WITH_EXIF_WITH_XMP.gpsTimestamp);
+ }
+
+ @Test
@LargeTest
public void testAddDefaultValuesForCompatibility() throws Exception {
File imageFile =
@@ -1143,6 +1427,136 @@
assertThat(exif.getAttribute(newTag)).isEqualTo(isoValue);
}
+ @Test
+ @SmallTest
+ public void testRationalFromDouble() {
+ double value = 0.12345678;
+
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(150549);
+ expect.that(result.denominator).isEqualTo(1219447);
+ expect.that((double) result.numerator / result.denominator)
+ .isWithin(0.00000000001)
+ .of(value);
+ }
+
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_niceFraction() {
+ double value = 1.0 / 1600;
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(1);
+ expect.that(result.denominator).isEqualTo(1600);
+ expect.that((double) result.numerator / result.denominator).isEqualTo(value);
+ }
+
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_recurringDecimal() {
+ double value = 1.0 / 3;
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(1);
+ expect.that(result.denominator).isEqualTo(3);
+ expect.that((double) result.numerator / result.denominator).isEqualTo(value);
+ }
+
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_negative() {
+ double value = -0.12345678;
+
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(-150549);
+ expect.that(result.denominator).isEqualTo(1219447);
+ expect.that((double) result.numerator / result.denominator)
+ .isWithin(0.00000000001)
+ .of(value);
+ }
+
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_maxLong() {
+ double value = Long.MAX_VALUE;
+
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(Long.MAX_VALUE);
+ expect.that(result.denominator).isEqualTo(1);
+ }
+
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_justLargerThanMaxLong() {
+ double value = Math.nextUp(Long.MAX_VALUE);
+
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(Long.MAX_VALUE);
+ expect.that(result.denominator).isEqualTo(1);
+ }
+
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_muchLargerThanMaxLong() {
+ double value = Long.MAX_VALUE + 10000.0;
+
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(Long.MAX_VALUE);
+ expect.that(result.denominator).isEqualTo(1);
+ }
+
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_minLong() {
+ double value = Math.nextDown(Long.MIN_VALUE);
+
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(Long.MIN_VALUE);
+ expect.that(result.denominator).isEqualTo(1);
+ }
+
+ // Ensure that a very large negative number, which is just higher (closer to positive infinity)
+ // than Long.MIN_VALUE doesn't cause overflow.
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_justHigherThanMinLong() {
+ double value = Math.nextUp(Long.MIN_VALUE);
+
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ long expectedNumerator = Math.round(value);
+ expect.that(result.numerator).isEqualTo(expectedNumerator);
+ expect.that(result.denominator).isEqualTo(1);
+ }
+
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_justLowerThanMinLong() {
+ double value = Math.nextDown(Long.MIN_VALUE);
+
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(Long.MIN_VALUE);
+ expect.that(result.denominator).isEqualTo(1);
+ }
+
+ @Test
+ @SmallTest
+ public void testRationalFromDouble_muchLowerThanMinLong() {
+ double value = Long.MIN_VALUE - 1000.0;
+
+ ExifInterface.Rational result = ExifInterface.Rational.createFromDouble(value);
+
+ expect.that(result.numerator).isEqualTo(Long.MIN_VALUE);
+ expect.that(result.denominator).isEqualTo(1);
+ }
+
private void printExifTagsAndValues(String fileName, ExifInterface exifInterface) {
// Prints thumbnail information.
if (exifInterface.hasThumbnail()) {
@@ -1215,7 +1629,9 @@
if (expectedAttributes.hasLatLong) {
expect.that(latLong)
.usingExactEquality()
- .containsExactly(expectedAttributes.latitude, expectedAttributes.longitude)
+ .containsExactly(
+ expectedAttributes.computedLatitude,
+ expectedAttributes.computedLongitude)
.inOrder();
expect.that(exifInterface.hasAttribute(ExifInterface.TAG_GPS_LATITUDE)).isTrue();
expect.that(exifInterface.hasAttribute(ExifInterface.TAG_GPS_LONGITUDE)).isTrue();
@@ -1224,7 +1640,7 @@
expect.that(exifInterface.hasAttribute(ExifInterface.TAG_GPS_LATITUDE)).isFalse();
expect.that(exifInterface.hasAttribute(ExifInterface.TAG_GPS_LONGITUDE)).isFalse();
}
- expect.that(exifInterface.getAltitude(.0)).isEqualTo(expectedAttributes.altitude);
+ expect.that(exifInterface.getAltitude(.0)).isEqualTo(expectedAttributes.computedAltitude);
// Checks values.
expectStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedAttributes.make);
@@ -1424,7 +1840,8 @@
expect.that(exifInterface.getAttributeRange(ExifInterface.TAG_GPS_LATITUDE))
.asList()
.containsExactly(
- expectedAttributes.latitudeOffset, expectedAttributes.latitudeLength)
+ expectedAttributes.gpsLatitudeOffset,
+ expectedAttributes.gpsLatitudeLength)
.inOrder();
// TODO: Add code for retrieving raw latitude data using offset and length
} else {
diff --git a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExpectedAttributes.java b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExpectedAttributes.java
index c5611c4..6a4b30e 100644
--- a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExpectedAttributes.java
+++ b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExpectedAttributes.java
@@ -39,7 +39,7 @@
.setThumbnailSize(512, 288)
.setIsThumbnailCompressed(true)
.setMake("SAMSUNG")
- .setMakeOffsetAndLength(160, 8)
+ .setMakeOffset(160)
.setModel("SM-N900S")
.setAperture(2.2)
.setDateTimeOriginal("2016:01:29 18:32:27")
@@ -64,11 +64,11 @@
/** Expected attributes for {@link R.raw#jpeg_with_exif_byte_order_mm}. */
public static final ExpectedAttributes JPEG_WITH_EXIF_BYTE_ORDER_MM =
new Builder()
- .setLatitudeOffsetAndLength(584, 24)
- .setLatLong(0, 0)
- .setAltitude(0)
+ .setGpsLatitudeOffsetAndLength(584, 24)
+ .setComputedLatLong(0, 0)
+ .setComputedAltitude(0)
.setMake("LGE")
- .setMakeOffsetAndLength(414, 4)
+ .setMakeOffset(414)
.setModel("Nexus 5")
.setAperture(2.4)
.setDateTimeOriginal("2016:01:29 15:44:58")
@@ -94,7 +94,7 @@
public static final ExpectedAttributes JPEG_WITH_EXIF_BYTE_ORDER_MM_STANDALONE =
JPEG_WITH_EXIF_BYTE_ORDER_MM
.buildUpon()
- .setLatitudeOffset(JPEG_WITH_EXIF_BYTE_ORDER_MM.latitudeOffset - 6)
+ .setGpsLatitudeOffset(JPEG_WITH_EXIF_BYTE_ORDER_MM.gpsLatitudeOffset - 6)
.setMakeOffset(JPEG_WITH_EXIF_BYTE_ORDER_MM.makeOffset - 6)
.setImageSize(0, 0)
.build();
@@ -116,11 +116,11 @@
.setThumbnailOffsetAndLength(12570, 15179)
.setThumbnailSize(256, 144)
.setIsThumbnailCompressed(true)
- .setLatitudeOffsetAndLength(12486, 24)
- .setLatLong(53.83450833333334, 10.69585)
- .setAltitude(0)
+ .setGpsLatitudeOffsetAndLength(12486, 24)
+ .setComputedLatLong(53.83450833333334, 10.69585)
+ .setComputedAltitude(0)
.setMake("LGE")
- .setMakeOffsetAndLength(102, 4)
+ .setMakeOffset(102)
.setModel("LG-H815")
.setAperture(1.8)
.setDateTimeOriginal("2015:11:12 16:46:18")
@@ -143,7 +143,7 @@
DNG_WITH_EXIF_WITH_XMP
.buildUpon()
.clearThumbnail()
- .setLatitudeOffset(1692)
+ .setGpsLatitudeOffset(1692)
.setMakeOffset(84)
.setOrientation(ExifInterface.ORIENTATION_NORMAL)
.setXmpResourceId(R.raw.jpeg_xmp)
@@ -175,7 +175,7 @@
public static final ExpectedAttributes HEIF_WITH_EXIF_BELOW_API_31 =
new Builder()
.setMake("LGE")
- .setMakeOffsetAndLength(3519, 4)
+ .setMakeOffset(3519)
.setModel("Nexus 5")
.setImageSize(1920, 1080)
.setOrientation(ExifInterface.ORIENTATION_NORMAL)
@@ -203,14 +203,22 @@
// GPS information.
private boolean mHasLatLong;
- private long mLatitudeOffset;
- private long mLatitudeLength;
- private double mLatitude;
- private double mLongitude;
- private double mAltitude;
+ private double mComputedLatitude;
+ private double mComputedLongitude;
+ private double mComputedAltitude;
+ @Nullable private String mGpsAltitude;
+ @Nullable private String mGpsAltitudeRef;
+ @Nullable private String mGpsDatestamp;
+ @Nullable private String mGpsLatitude;
+ private long mGpsLatitudeOffset;
+ private long mGpsLatitudeLength;
+ @Nullable private String mGpsLatitudeRef;
+ @Nullable private String mGpsLongitude;
+ @Nullable private String mGpsLongitudeRef;
+ @Nullable private String mGpsProcessingMethod;
+ @Nullable private String mGpsTimestamp;
// Make information
- private boolean mHasMake;
private long mMakeOffset;
private long mMakeLength;
@Nullable private String mMake;
@@ -222,15 +230,6 @@
private double mExposureTime;
private double mFlash;
@Nullable private String mFocalLength;
- @Nullable private String mGpsAltitude;
- @Nullable private String mGpsAltitudeRef;
- @Nullable private String mGpsDatestamp;
- @Nullable private String mGpsLatitude;
- @Nullable private String mGpsLatitudeRef;
- @Nullable private String mGpsLongitude;
- @Nullable private String mGpsLongitudeRef;
- @Nullable private String mGpsProcessingMethod;
- @Nullable private String mGpsTimestamp;
private int mImageLength;
private int mImageWidth;
@Nullable private String mIso;
@@ -254,12 +253,20 @@
mThumbnailHeight = attributes.thumbnailHeight;
mIsThumbnailCompressed = attributes.isThumbnailCompressed;
mHasLatLong = attributes.hasLatLong;
- mLatitude = attributes.latitude;
- mLatitudeOffset = attributes.latitudeOffset;
- mLatitudeLength = attributes.latitudeLength;
- mLongitude = attributes.longitude;
- mAltitude = attributes.altitude;
- mHasMake = attributes.hasMake;
+ mComputedLatitude = attributes.computedLatitude;
+ mComputedLongitude = attributes.computedLongitude;
+ mComputedAltitude = attributes.computedAltitude;
+ mGpsAltitude = attributes.gpsAltitude;
+ mGpsAltitudeRef = attributes.gpsAltitudeRef;
+ mGpsDatestamp = attributes.gpsDatestamp;
+ mGpsLatitude = attributes.gpsLatitude;
+ mGpsLatitudeOffset = attributes.gpsLatitudeOffset;
+ mGpsLatitudeLength = attributes.gpsLatitudeLength;
+ mGpsLatitudeRef = attributes.gpsLatitudeRef;
+ mGpsLongitude = attributes.gpsLongitude;
+ mGpsLongitudeRef = attributes.gpsLongitudeRef;
+ mGpsProcessingMethod = attributes.gpsProcessingMethod;
+ mGpsTimestamp = attributes.gpsTimestamp;
mMakeOffset = attributes.makeOffset;
mMakeLength = attributes.makeLength;
mMake = attributes.make;
@@ -268,15 +275,6 @@
mDateTimeOriginal = attributes.dateTimeOriginal;
mExposureTime = attributes.exposureTime;
mFocalLength = attributes.focalLength;
- mGpsAltitude = attributes.gpsAltitude;
- mGpsAltitudeRef = attributes.gpsAltitudeRef;
- mGpsDatestamp = attributes.gpsDatestamp;
- mGpsLatitude = attributes.gpsLatitude;
- mGpsLatitudeRef = attributes.gpsLatitudeRef;
- mGpsLongitude = attributes.gpsLongitude;
- mGpsLongitudeRef = attributes.gpsLongitudeRef;
- mGpsProcessingMethod = attributes.gpsProcessingMethod;
- mGpsTimestamp = attributes.gpsTimestamp;
mImageLength = attributes.imageLength;
mImageWidth = attributes.imageWidth;
mIso = attributes.iso;
@@ -328,68 +326,101 @@
return this;
}
- public Builder setLatLong(double latitude, double longitude) {
+ public Builder setComputedLatLong(double computedLatitude, double computedLongitude) {
mHasLatLong = true;
- mLatitude = latitude;
- mLongitude = longitude;
+ mComputedLatitude = computedLatitude;
+ mComputedLongitude = computedLongitude;
return this;
}
- public Builder setLatitudeOffsetAndLength(long offset, long length) {
- mHasLatLong = true;
- mLatitudeOffset = offset;
- mLatitudeLength = length;
+ public Builder clearComputedLatLong() {
+ mHasLatLong = false;
+ mComputedLatitude = 0;
+ mComputedLongitude = 0;
return this;
}
- public Builder setLatitudeOffset(long offset) {
+ public Builder setGpsAltitude(@Nullable String gpsAltitude) {
+ mGpsAltitude = gpsAltitude;
+ return this;
+ }
+
+ public Builder setGpsAltitudeRef(@Nullable String gpsAltitudeRef) {
+ mGpsAltitudeRef = gpsAltitudeRef;
+ return this;
+ }
+
+ public Builder setGpsDatestamp(@Nullable String gpsDatestamp) {
+ mGpsDatestamp = gpsDatestamp;
+ return this;
+ }
+
+ public Builder setGpsLatitude(@Nullable String gpsLatitude) {
+ mGpsLatitude = gpsLatitude;
+ return this;
+ }
+
+ public Builder setGpsLatitudeOffsetAndLength(long offset, long length) {
+ mHasLatLong = true;
+ mGpsLatitudeOffset = offset;
+ mGpsLatitudeLength = length;
+ return this;
+ }
+
+ public Builder setGpsLatitudeOffset(long offset) {
if (!mHasLatLong) {
throw new IllegalStateException(
"Latitude position in the file must first be "
+ "set with setLatitudeOffsetAndLength(...)");
}
- mLatitudeOffset = offset;
+ mGpsLatitudeOffset = offset;
return this;
}
- public Builder clearLatLong() {
- mHasLatLong = false;
- mLatitude = 0;
- mLongitude = 0;
+ public Builder setGpsLatitudeRef(@Nullable String gpsLatitudeRef) {
+ mGpsLatitudeRef = gpsLatitudeRef;
return this;
}
- public Builder setAltitude(double altitude) {
- mAltitude = altitude;
+ public Builder setGpsLongitude(@Nullable String gpsLongitude) {
+ mGpsLongitude = gpsLongitude;
+ return this;
+ }
+
+ public Builder setGpsLongitudeRef(@Nullable String gpsLongitudeRef) {
+ mGpsLongitudeRef = gpsLongitudeRef;
+ return this;
+ }
+
+ public Builder setGpsProcessingMethod(@Nullable String gpsProcessingMethod) {
+ mGpsProcessingMethod = gpsProcessingMethod;
+ return this;
+ }
+
+ public Builder setGpsTimestamp(@Nullable String gpsTimestamp) {
+ mGpsTimestamp = gpsTimestamp;
+ return this;
+ }
+
+ public Builder setComputedAltitude(double computedAltitude) {
+ mComputedAltitude = computedAltitude;
return this;
}
public Builder setMake(@Nullable String make) {
if (make == null) {
- mHasMake = false;
mMakeOffset = 0;
mMakeLength = 0;
} else {
- mHasMake = true;
mMake = make;
+ mMakeLength = make.length() + 1;
}
return this;
}
- // TODO: b/270554381 - consider deriving length automatically from `make.length() + 1`
- // (since the string is null-terminated in the format).
- public Builder setMakeOffsetAndLength(long offset, long length) {
- mHasMake = true;
- mMakeOffset = offset;
- mMakeLength = length;
- return this;
- }
-
public Builder setMakeOffset(long offset) {
- if (!mHasMake) {
- throw new IllegalStateException(
- "Make position in the file must first be set with"
- + " setMakeOffsetAndLength(...)");
+ if (mMake == null) {
+ throw new IllegalStateException("Make must first be set with setMake(...)");
}
mMakeOffset = offset;
return this;
@@ -425,51 +456,6 @@
return this;
}
- public Builder setGpsAltitude(@Nullable String gpsAltitude) {
- mGpsAltitude = gpsAltitude;
- return this;
- }
-
- public Builder setGpsAltitudeRef(@Nullable String gpsAltitudeRef) {
- mGpsAltitudeRef = gpsAltitudeRef;
- return this;
- }
-
- public Builder setGpsDatestamp(@Nullable String gpsDatestamp) {
- mGpsDatestamp = gpsDatestamp;
- return this;
- }
-
- public Builder setGpsLatitude(@Nullable String gpsLatitude) {
- mGpsLatitude = gpsLatitude;
- return this;
- }
-
- public Builder setGpsLatitudeRef(@Nullable String gpsLatitudeRef) {
- mGpsLatitudeRef = gpsLatitudeRef;
- return this;
- }
-
- public Builder setGpsLongitude(@Nullable String gpsLongitude) {
- mGpsLongitude = gpsLongitude;
- return this;
- }
-
- public Builder setGpsLongitudeRef(@Nullable String gpsLongitudeRef) {
- mGpsLongitudeRef = gpsLongitudeRef;
- return this;
- }
-
- public Builder setGpsProcessingMethod(@Nullable String gpsProcessingMethod) {
- mGpsProcessingMethod = gpsProcessingMethod;
- return this;
- }
-
- public Builder setGpsTimestamp(@Nullable String gpsTimestamp) {
- mGpsTimestamp = gpsTimestamp;
- return this;
- }
-
public Builder setImageSize(int imageWidth, int imageLength) {
mImageWidth = imageWidth;
mImageLength = imageLength;
@@ -546,81 +532,77 @@
}
}
- // TODO: b/270554381 - Add nullability annotations below.
-
// Thumbnail information.
public final boolean hasThumbnail;
public final int thumbnailWidth;
public final int thumbnailHeight;
public final boolean isThumbnailCompressed;
- // TODO: b/270554381 - Merge these offset and length (and others) into long[] arrays, and
- // move them down to their own section. This may also allow removing some of the hasXXX
- // fields.
public final long thumbnailOffset;
public final long thumbnailLength;
// GPS information.
public final boolean hasLatLong;
- // TODO: b/270554381 - Merge this and longitude into a double[]
- public final double latitude;
- public final long latitudeOffset;
- public final long latitudeLength;
- public final double longitude;
- public final double altitude;
+ public final double computedLatitude;
+ public final double computedLongitude;
+ public final double computedAltitude;
+ @Nullable public final String gpsAltitude;
+ @Nullable public final String gpsAltitudeRef;
+ @Nullable public final String gpsDatestamp;
+ @Nullable public final String gpsLatitude;
+ public final long gpsLatitudeOffset;
+ public final long gpsLatitudeLength;
+ @Nullable public final String gpsLatitudeRef;
+ @Nullable public final String gpsLongitude;
+ @Nullable public final String gpsLongitudeRef;
+ @Nullable public final String gpsProcessingMethod;
+ @Nullable public final String gpsTimestamp;
// Make information
- public final boolean hasMake;
public final long makeOffset;
public final long makeLength;
- public final String make;
+ @Nullable public final String make;
// Values.
- public final String model;
+ @Nullable public final String model;
public final double aperture;
- public final String dateTimeOriginal;
+ @Nullable public final String dateTimeOriginal;
public final double exposureTime;
- public final String focalLength;
- // TODO: b/270554381 - Rename these to make them clear they're strings, or original values,
- // and move them closer to the (computed) latitude/longitude/altitude values. Consider
- // also having a verification check that they are consistent with latitude/longitude (but
- // not sure how to reconcile that with "don't duplicate business logic in tests").
- public final String gpsAltitude;
- public final String gpsAltitudeRef;
- public final String gpsDatestamp;
- public final String gpsLatitude;
- public final String gpsLatitudeRef;
- public final String gpsLongitude;
- public final String gpsLongitudeRef;
- public final String gpsProcessingMethod;
- public final String gpsTimestamp;
+ @Nullable public final String focalLength;
public final int imageLength;
public final int imageWidth;
- public final String iso;
+ @Nullable public final String iso;
public final int orientation;
// XMP information.
+ public final boolean hasXmp;
@Nullable private final String mXmp;
@Nullable private final Integer mXmpResourceId;
@Nullable private String mMemoizedXmp;
- public final boolean hasXmp;
public final long xmpOffset;
public final long xmpLength;
private ExpectedAttributes(Builder builder) {
- // TODO: b/270554381 - Re-order these assignments to match the fields above.
hasThumbnail = builder.mHasThumbnail;
- thumbnailOffset = builder.mThumbnailOffset;
- thumbnailLength = builder.mThumbnailLength;
thumbnailWidth = builder.mThumbnailWidth;
thumbnailHeight = builder.mThumbnailHeight;
isThumbnailCompressed = builder.mIsThumbnailCompressed;
+ thumbnailOffset = builder.mThumbnailOffset;
+ thumbnailLength = builder.mThumbnailLength;
hasLatLong = builder.mHasLatLong;
- latitudeOffset = builder.mLatitudeOffset;
- latitudeLength = builder.mLatitudeLength;
- latitude = builder.mLatitude;
- longitude = builder.mLongitude;
- altitude = builder.mAltitude;
- hasMake = builder.mHasMake;
+ computedLatitude = builder.mComputedLatitude;
+ computedLongitude = builder.mComputedLongitude;
+ computedAltitude = builder.mComputedAltitude;
+ gpsAltitude = builder.mGpsAltitude;
+ gpsAltitudeRef = builder.mGpsAltitudeRef;
+ gpsDatestamp = builder.mGpsDatestamp;
+ gpsLatitude = builder.mGpsLatitude;
+ gpsLatitudeOffset = builder.mGpsLatitudeOffset;
+ gpsLatitudeLength = builder.mGpsLatitudeLength;
+ gpsLatitudeRef = builder.mGpsLatitudeRef;
+ gpsLongitude = builder.mGpsLongitude;
+ gpsLongitudeRef = builder.mGpsLongitudeRef;
+ gpsProcessingMethod = builder.mGpsProcessingMethod;
+ gpsTimestamp = builder.mGpsTimestamp;
makeOffset = builder.mMakeOffset;
makeLength = builder.mMakeLength;
make = builder.mMake;
@@ -629,15 +611,6 @@
dateTimeOriginal = builder.mDateTimeOriginal;
exposureTime = builder.mExposureTime;
focalLength = builder.mFocalLength;
- gpsAltitude = builder.mGpsAltitude;
- gpsAltitudeRef = builder.mGpsAltitudeRef;
- gpsDatestamp = builder.mGpsDatestamp;
- gpsLatitude = builder.mGpsLatitude;
- gpsLatitudeRef = builder.mGpsLatitudeRef;
- gpsLongitude = builder.mGpsLongitude;
- gpsLongitudeRef = builder.mGpsLongitudeRef;
- gpsProcessingMethod = builder.mGpsProcessingMethod;
- gpsTimestamp = builder.mGpsTimestamp;
imageLength = builder.mImageLength;
imageWidth = builder.mImageWidth;
iso = builder.mIso;
diff --git a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
index 5eb80b4..863584a0 100644
--- a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
+++ b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
@@ -42,6 +42,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.exifinterface.media.ExifInterfaceUtils.Api21Impl;
import androidx.exifinterface.media.ExifInterfaceUtils.Api23Impl;
@@ -69,6 +70,7 @@
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -875,27 +877,41 @@
// G. Tags related to picture-taking condition
/**
- * <p>Exposure time, given in seconds.</p>
+ * Exposure time, given in seconds.
*
- * <ul>
- * <li>Tag = 33434</li>
- * <li>Type = Unsigned rational</li>
- * <li>Count = 1</li>
- * <li>Default = None</li>
- * </ul>
+ * <p>Note: For backwards compatibility this attribute is returned from {@link
+ * #getAttribute(String)} in decimal form (i.e. the format produced by {@link
+ * Double#toString(double)}). It is accepted into {@link #setAttribute(String, String)} in both
+ * rational (e.g. {@code "1/3"}) and decimal forms. The decimal format is anything accepted by
+ * {@link Double#parseDouble(String)}, e.g. {@code "0.125"}.
+ *
+ * <ul>
+ * <li>Tag = 33434
+ * <li>Type = Unsigned rational
+ * <li>Count = 1
+ * <li>Default = None
+ * </ul>
*/
public static final String TAG_EXPOSURE_TIME = "ExposureTime";
+
/**
- * <p>The F number.</p>
+ * The F number.
*
- * <ul>
- * <li>Tag = 33437</li>
- * <li>Type = Unsigned rational</li>
- * <li>Count = 1</li>
- * <li>Default = None</li>
- * </ul>
+ * <p>Note: For backwards compatibility this attribute is returned from {@link
+ * #getAttribute(String)} in decimal form (i.e. the format produced by {@link
+ * Double#toString(double)}). It is accepted into {@link #setAttribute(String, String)} in both
+ * rational (e.g. {@code "1/3"}) and decimal forms. The decimal format is anything accepted by
+ * {@link Double#parseDouble(String)}, e.g. {@code "0.125"}.
+ *
+ * <ul>
+ * <li>Tag = 33437
+ * <li>Type = Unsigned rational
+ * <li>Count = 1
+ * <li>Default = None
+ * </ul>
*/
public static final String TAG_F_NUMBER = "FNumber";
+
/**
* <p>The class of the program used by the camera to set exposure when the picture is taken.
* The tag values are as follows.</p>
@@ -1109,19 +1125,28 @@
* </ul>
*/
public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
+
/**
- * <p>The distance to the subject, given in meters. Note that if the numerator of the recorded
- * value is 0xFFFFFFFF, Infinity shall be indicated; and if the numerator is 0, Distance
- * unknown shall be indicated.</p>
+ * The distance to the subject, given in meters.
*
- * <ul>
- * <li>Tag = 37382</li>
- * <li>Type = Unsigned rational</li>
- * <li>Count = 1</li>
- * <li>Default = None</li>
- * </ul>
+ * <p>Note that if the numerator of the recorded value is 0xFFFFFFFF, Infinity shall be
+ * indicated; and if the numerator is 0, Distance unknown shall be indicated.
+ *
+ * <p>Note: For backwards compatibility this attribute is returned from {@link
+ * #getAttribute(String)} in decimal form (i.e. the format produced by {@link
+ * Double#toString(double)}). It is accepted into {@link #setAttribute(String, String)} in both
+ * rational (e.g. {@code "1/3"}) and decimal forms. The decimal format is anything accepted by
+ * {@link Double#parseDouble(String)}, e.g. {@code "0.125"}.
+ *
+ * <ul>
+ * <li>Tag = 37382
+ * <li>Type = Unsigned rational
+ * <li>Count = 1
+ * <li>Default = None
+ * </ul>
*/
public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+
/**
* <p>The metering mode.</p>
*
@@ -1443,18 +1468,26 @@
* @see #WHITEBALANCE_MANUAL
*/
public static final String TAG_WHITE_BALANCE = "WhiteBalance";
+
/**
- * <p>This tag indicates the digital zoom ratio when the image was shot. If the numerator of
- * the recorded value is 0, this indicates that digital zoom was not used.</p>
+ * This tag indicates the digital zoom ratio when the image was shot. If the numerator of the
+ * recorded value is 0, this indicates that digital zoom was not used.
*
- * <ul>
- * <li>Tag = 41988</li>
- * <li>Type = Unsigned rational</li>
- * <li>Count = 1</li>
- * <li>Default = None</li>
- * </ul>
+ * <p>Note: For backwards compatibility this attribute is returned from {@link
+ * #getAttribute(String)} in decimal form (i.e. the format produced by {@link
+ * Double#toString(double)}). It is accepted into {@link #setAttribute(String, String)} in both
+ * rational (e.g. {@code "1/3"}) and decimal forms. The decimal format is anything accepted by
+ * {@link Double#parseDouble(String)}, e.g. {@code "0.125"}.
+ *
+ * <ul>
+ * <li>Tag = 41988
+ * <li>Type = Unsigned rational
+ * <li>Count = 1
+ * <li>Default = None
+ * </ul>
*/
public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+
/**
* <p>This tag indicates the equivalent focal length assuming a 35mm film camera, in mm.
* A value of 0 means the focal length is unknown. Note that this tag differs from
@@ -1784,18 +1817,24 @@
* </ul>
*/
public static final String TAG_GPS_ALTITUDE = "GPSAltitude";
+
/**
- * <p>Indicates the time as UTC (Coordinated Universal Time). TimeStamp is expressed as three
- * unsigned rational values giving the hour, minute, and second.</p>
+ * Indicates the time as UTC (Coordinated Universal Time). TimeStamp is expressed as three
+ * unsigned rational values giving the hour, minute, and second.
*
- * <ul>
- * <li>Tag = 7</li>
- * <li>Type = Unsigned rational</li>
- * <li>Count = 3</li>
- * <li>Default = None</li>
- * </ul>
+ * <p>Note: This attribute is returned from {@link #getAttribute(String)} and accepted into
+ * {@link #setAttribute(String, String)} as 3 colon-separated integers, e.g. {@code "11:05:32"}.
+ * Decimal or rational hours, minutes or seconds parts are not supported.
+ *
+ * <ul>
+ * <li>Tag = 7
+ * <li>Type = Unsigned rational
+ * <li>Count = 3
+ * <li>Default = None
+ * </ul>
*/
public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+
/**
* <p>Indicates the GPS satellites used for measurements. This tag may be used to describe
* the number of satellites, their ID number, angle of elevation, azimuth, SNR and other
@@ -2997,12 +3036,9 @@
(byte) 0x47, (byte) 0x0d, (byte) 0x0a, (byte) 0x1a, (byte) 0x0a};
// See "Extensions to the PNG 1.2 Specification, Version 1.5.0",
// 3.7. eXIf Exchangeable Image File (Exif) Profile
- private static final byte[] PNG_CHUNK_TYPE_EXIF = new byte[]{(byte) 0x65, (byte) 0x58,
- (byte) 0x49, (byte) 0x66};
- private static final byte[] PNG_CHUNK_TYPE_IHDR = new byte[]{(byte) 0x49, (byte) 0x48,
- (byte) 0x44, (byte) 0x52};
- private static final byte[] PNG_CHUNK_TYPE_IEND = new byte[]{(byte) 0x49, (byte) 0x45,
- (byte) 0x4e, (byte) 0x44};
+ private static final int PNG_CHUNK_TYPE_EXIF = intFromBytes('e', 'X', 'I', 'f');
+ private static final int PNG_CHUNK_TYPE_IHDR = intFromBytes('I', 'H', 'D', 'R');
+ private static final int PNG_CHUNK_TYPE_IEND = intFromBytes('I', 'E', 'N', 'D');
private static final int PNG_CHUNK_TYPE_BYTE_LENGTH = 4;
private static final int PNG_CHUNK_CRC_BYTE_LENGTH = 4;
@@ -3074,16 +3110,13 @@
};
// A class for indicating EXIF rational type.
- private static class Rational {
+ // TODO: b/308978831 - Migrate to android.util.Rational when the min API is 21.
+ @VisibleForTesting
+ static class Rational {
public final long numerator;
public final long denominator;
@SuppressWarnings("WeakerAccess") /* synthetic access */
- Rational(double value) {
- this((long) (value * 10000), 10000);
- }
-
- @SuppressWarnings("WeakerAccess") /* synthetic access */
Rational(long numerator, long denominator) {
// Handle erroneous case
if (denominator == 0) {
@@ -3095,6 +3128,43 @@
this.denominator = denominator;
}
+ /**
+ * Creates a new {@code Rational} which approximates the provided {@code double} value by
+ * using <a href="https://en.wikipedia.org/wiki/Continued_fraction">continued fractions</a>.
+ */
+ @NonNull
+ public static Rational createFromDouble(double value) {
+ if (value >= Long.MAX_VALUE || value <= Long.MIN_VALUE) {
+ // value is too large to represent as a long, so just return the max/min value.
+ return new Rational(
+ /* numerator= */ value > 0 ? Long.MAX_VALUE : Long.MIN_VALUE,
+ /* denominator= */ 1);
+ }
+
+ double absoluteValue = Math.abs(value);
+ double threshold = 0.00000001 * absoluteValue;
+ double remainingValue = absoluteValue;
+ long numerator = 1;
+ long previousNumerator = 0;
+ long denominator = 0;
+ long previousDenominator = 1;
+ do {
+ double remainder = remainingValue % 1;
+ long wholePart = (long) (remainingValue - remainder);
+ long tmp = numerator;
+ numerator = wholePart * numerator + previousNumerator;
+ previousNumerator = tmp;
+
+ tmp = denominator;
+ denominator = wholePart * denominator + previousDenominator;
+ previousDenominator = tmp;
+
+ remainingValue = 1 / remainder;
+ } while ((Math.abs(absoluteValue - numerator / (double) denominator) > threshold));
+
+ return new Rational(value < 0 ? -numerator : numerator, denominator);
+ }
+
@NonNull
@Override
public String toString() {
@@ -3793,9 +3863,30 @@
@SuppressWarnings("unchecked")
private static final HashMap<String, ExifTag>[] sExifTagMapsForWriting =
new HashMap[EXIF_TAGS.length];
- private static final HashSet<String> sTagSetForCompatibility = new HashSet<>(Arrays.asList(
- TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
- TAG_GPS_TIMESTAMP));
+
+ /**
+ * These are tags of type 'Unsigned rational' but which are handled in decimal form.
+ *
+ * <p>This means they are output from {@link #getAttribute(String)}, and accepted into {@link
+ * #setAttribute(String, String)}, as strings in decimal form (e.g. {@code "0.125"}, {@code
+ * "6.25E-4"}).
+ *
+ * <p>This is to maintain backwards compatibility with a previous implementation of the {@link
+ * android.media.ExifInterface} (the platform variant of this class).
+ *
+ * <p>See <a
+ * href="http://ag/c/platform/frameworks/base/+/909922/2..9/api/current.txt#b20093">this
+ * internal code review comment from 2016</a> for more details.
+ */
+ private static final Set<String> RATIONAL_TAGS_HANDLED_AS_DECIMALS_FOR_COMPATIBILITY =
+ Collections.unmodifiableSet(
+ new HashSet<>(
+ Arrays.asList(
+ TAG_F_NUMBER,
+ TAG_DIGITAL_ZOOM_RATIO,
+ TAG_EXPOSURE_TIME,
+ TAG_SUBJECT_DISTANCE)));
+
// Mappings from tag number to IFD type for pointer tags.
private static final HashMap<Integer, Integer> sExifPointerTagMap = new HashMap<>();
@@ -4116,34 +4207,35 @@
throw new NullPointerException("tag shouldn't be null");
}
ExifAttribute attribute = getExifAttribute(tag);
- if (attribute != null) {
- if (!sTagSetForCompatibility.contains(tag)) {
- return attribute.getStringValue(mExifByteOrder);
+ if (attribute == null) {
+ return null;
+ }
+ if (tag.equals(TAG_GPS_TIMESTAMP)) {
+ // Convert GPS timestamp value to a custom format for backwards compatibility.
+ if (attribute.format != IFD_FORMAT_URATIONAL
+ && attribute.format != IFD_FORMAT_SRATIONAL) {
+ Log.w(TAG, "GPS Timestamp format is not rational. format=" + attribute.format);
+ return null;
}
- if (tag.equals(TAG_GPS_TIMESTAMP)) {
- // Convert the rational values to the custom formats for backwards compatibility.
- if (attribute.format != IFD_FORMAT_URATIONAL
- && attribute.format != IFD_FORMAT_SRATIONAL) {
- Log.w(TAG, "GPS Timestamp format is not rational. format=" + attribute.format);
- return null;
- }
- Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder);
- if (array == null || array.length != 3) {
- Log.w(TAG, "Invalid GPS Timestamp array. array=" + Arrays.toString(array));
- return null;
- }
- return String.format("%02d:%02d:%02d",
- (int) ((float) array[0].numerator / array[0].denominator),
- (int) ((float) array[1].numerator / array[1].denominator),
- (int) ((float) array[2].numerator / array[2].denominator));
+ Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder);
+ if (array == null || array.length != 3) {
+ Log.w(TAG, "Invalid GPS Timestamp array. array=" + Arrays.toString(array));
+ return null;
}
+ return String.format("%02d:%02d:%02d",
+ (int) ((float) array[0].numerator / array[0].denominator),
+ (int) ((float) array[1].numerator / array[1].denominator),
+ (int) ((float) array[2].numerator / array[2].denominator));
+ } else if (RATIONAL_TAGS_HANDLED_AS_DECIMALS_FOR_COMPATIBILITY.contains(tag)) {
+ // Convert the rational values to the custom formats for backwards compatibility.
try {
return Double.toString(attribute.getDoubleValue(mExifByteOrder));
} catch (NumberFormatException e) {
return null;
}
+ } else {
+ return attribute.getStringValue(mExifByteOrder);
}
- return null;
}
/**
@@ -4205,10 +4297,45 @@
if (tag == null) {
throw new NullPointerException("tag shouldn't be null");
}
- // Validate and convert if necessary.
- if (TAG_DATETIME.equals(tag) || TAG_DATETIME_ORIGINAL.equals(tag)
- || TAG_DATETIME_DIGITIZED.equals(tag)) {
- if (value != null) {
+
+ // Maintain compatibility.
+ if (TAG_ISO_SPEED_RATINGS.equals(tag)) {
+ if (DEBUG) {
+ Log.d(TAG, "setAttribute: Replacing TAG_ISO_SPEED_RATINGS with "
+ + "TAG_PHOTOGRAPHIC_SENSITIVITY.");
+ }
+ tag = TAG_PHOTOGRAPHIC_SENSITIVITY;
+ }
+ // Maybe convert the given value for backwards compatibility.
+ if (value != null) {
+ if (RATIONAL_TAGS_HANDLED_AS_DECIMALS_FOR_COMPATIBILITY.contains(tag)
+ && !value.contains("/")) {
+ // Convert floating point values to rational for rational tags that are emitted and
+ // consumed as floating point values for backwards compatibility.
+ try {
+ double doubleValue = Double.parseDouble(value);
+ value = Rational.createFromDouble(doubleValue).toString();
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Invalid value for " + tag + " : " + value);
+ return;
+ }
+ } else if (tag.equals(TAG_GPS_TIMESTAMP)) {
+ Matcher m = GPS_TIMESTAMP_PATTERN.matcher(value);
+ if (!m.find()) {
+ Log.w(TAG, "Invalid value for " + tag + " : " + value);
+ return;
+ }
+ value =
+ Integer.parseInt(m.group(1))
+ + "/1,"
+ + Integer.parseInt(m.group(2))
+ + "/1,"
+ + Integer.parseInt(m.group(3))
+ + "/1";
+ } else if (TAG_DATETIME.equals(tag)
+ || TAG_DATETIME_ORIGINAL.equals(tag)
+ || TAG_DATETIME_DIGITIZED.equals(tag)) {
+ // Validate and convert datetime values if necessary.
boolean isPrimaryFormat = DATETIME_PRIMARY_FORMAT_PATTERN.matcher(value).find();
boolean isSecondaryFormat = DATETIME_SECONDARY_FORMAT_PATTERN.matcher(value).find();
// Validate
@@ -4218,8 +4345,8 @@
return;
}
// If datetime value has secondary format (e.g. 2020-01-01 00:00:00), convert it to
- // primary format (e.g. 2020:01:01 00:00:00) since it is the format in the
- // official documentation.
+ // primary format (e.g. 2020:01:01 00:00:00) since it is the format in the official
+ // documentation.
// See JEITA CP-3451C Section 4.6.4. D. Other Tags, DateTime
if (isSecondaryFormat) {
// Replace "-" with ":" to match the primary format.
@@ -4227,34 +4354,6 @@
}
}
}
- // Maintain compatibility.
- if (TAG_ISO_SPEED_RATINGS.equals(tag)) {
- if (DEBUG) {
- Log.d(TAG, "setAttribute: Replacing TAG_ISO_SPEED_RATINGS with "
- + "TAG_PHOTOGRAPHIC_SENSITIVITY.");
- }
- tag = TAG_PHOTOGRAPHIC_SENSITIVITY;
- }
- // Convert the given value to rational values for backwards compatibility.
- if (value != null && sTagSetForCompatibility.contains(tag)) {
- if (tag.equals(TAG_GPS_TIMESTAMP)) {
- Matcher m = GPS_TIMESTAMP_PATTERN.matcher(value);
- if (!m.find()) {
- Log.w(TAG, "Invalid value for " + tag + " : " + value);
- return;
- }
- value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
- + Integer.parseInt(m.group(3)) + "/1";
- } else {
- try {
- double doubleValue = Double.parseDouble(value);
- value = new Rational(doubleValue).toString();
- } catch (NumberFormatException e) {
- Log.w(TAG, "Invalid value for " + tag + " : " + value);
- return;
- }
- }
- }
for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) {
@@ -5058,8 +5157,10 @@
setAltitude(location.getAltitude());
// Location objects store speeds in m/sec. Translates it to km/hr here.
setAttribute(TAG_GPS_SPEED_REF, "K");
- setAttribute(TAG_GPS_SPEED, new Rational(location.getSpeed()
- * TimeUnit.HOURS.toSeconds(1) / 1000).toString());
+ setAttribute(
+ TAG_GPS_SPEED,
+ Rational.createFromDouble(location.getSpeed() * TimeUnit.HOURS.toSeconds(1) / 1000)
+ .toString());
String[] dateTime = sFormatterPrimary.format(
new Date(location.getTime())).split("\\s+", -1);
setAttribute(ExifInterface.TAG_GPS_DATESTAMP, dateTime[0]);
@@ -5111,7 +5212,7 @@
*/
public void setAltitude(double altitude) {
String ref = altitude >= 0 ? "0" : "1";
- setAttribute(TAG_GPS_ALTITUDE, new Rational(Math.abs(altitude)).toString());
+ setAttribute(TAG_GPS_ALTITUDE, Rational.createFromDouble(Math.abs(altitude)).toString());
setAttribute(TAG_GPS_ALTITUDE_REF, ref);
}
@@ -5312,7 +5413,7 @@
}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
// Not valid
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException(e);
}
}
@@ -5953,7 +6054,7 @@
}
} catch (RuntimeException e) {
throw new UnsupportedOperationException("Failed to read EXIF from HEIF file. "
- + "Given stream is either malformed or unsupported.");
+ + "Given stream is either malformed or unsupported.", e);
} finally {
try {
retriever.release();
@@ -6115,11 +6216,10 @@
// 2.1. Integers and byte order
in.setByteOrder(BIG_ENDIAN);
- int bytesRead = 0;
+ int startPosition = in.position();
// Skip the signature bytes
in.skipFully(PNG_SIGNATURE.length);
- bytesRead += PNG_SIGNATURE.length;
// Each chunk is made up of four parts:
// 1) Length: 4-byte unsigned integer indicating the number of bytes in the
@@ -6134,22 +6234,23 @@
try {
while (true) {
int length = in.readInt();
- bytesRead += 4;
- byte[] type = new byte[PNG_CHUNK_TYPE_BYTE_LENGTH];
- in.readFully(type);
- bytesRead += PNG_CHUNK_TYPE_BYTE_LENGTH;
+ int type = in.readInt();
// The first chunk must be the IHDR chunk
- if (bytesRead == 16 && !Arrays.equals(type, PNG_CHUNK_TYPE_IHDR)) {
- throw new IOException("Encountered invalid PNG file--IHDR chunk should appear"
- + "as the first chunk");
+ if (in.position() - startPosition == 16 && type != PNG_CHUNK_TYPE_IHDR) {
+ throw new IOException(
+ "Encountered invalid PNG file--IHDR chunk should appear as the first "
+ + "chunk");
}
- if (Arrays.equals(type, PNG_CHUNK_TYPE_IEND)) {
+ if (type == PNG_CHUNK_TYPE_IEND) {
// IEND marks the end of the image.
break;
- } else if (Arrays.equals(type, PNG_CHUNK_TYPE_EXIF)) {
+ } else if (type == PNG_CHUNK_TYPE_EXIF) {
+ // Save offset to EXIF data for handling thumbnail and attribute offsets.
+ mOffsetToExifData = in.position() - startPosition;
+
// TODO: Need to handle potential OutOfMemoryError
byte[] data = new byte[length];
in.readFully(data);
@@ -6158,15 +6259,13 @@
int dataCrcValue = in.readInt();
// Cyclic Redundancy Code used to check for corruption of the data
CRC32 crc = new CRC32();
- crc.update(type);
+ updateCrcWithInt(crc, type);
crc.update(data);
if ((int) crc.getValue() != dataCrcValue) {
throw new IOException("Encountered invalid CRC value for PNG-EXIF chunk."
+ "\n recorded CRC value: " + dataCrcValue + ", calculated CRC "
+ "value: " + crc.getValue());
}
- // Save offset to EXIF data for handling thumbnail and attribute offsets.
- mOffsetToExifData = bytesRead;
readExifSegment(data, IFD_TYPE_PRIMARY);
validateImages();
@@ -6175,16 +6274,22 @@
} else {
// Skip to next chunk
in.skipFully(length + PNG_CHUNK_CRC_BYTE_LENGTH);
- bytesRead += length + PNG_CHUNK_CRC_BYTE_LENGTH;
}
}
} catch (EOFException e) {
// Should not reach here. Will only reach here if the file is corrupted or
// does not follow the PNG specifications
- throw new IOException("Encountered corrupt PNG file.");
+ throw new IOException("Encountered corrupt PNG file.", e);
}
}
+ private static void updateCrcWithInt(CRC32 crc, int value) {
+ crc.update(value >>> 24);
+ crc.update(value >>> 16);
+ crc.update(value >>> 8);
+ crc.update(value);
+ }
+
// WebP contains EXIF data as a RIFF File Format Chunk
// All references below can be found in the following link.
// https://developers.google.com/speed/webp/docs/riff_container
@@ -6264,7 +6369,7 @@
} catch (EOFException e) {
// Should not reach here. Will only reach here if the file is corrupted or
// does not follow the WebP specifications
- throw new IOException("Encountered corrupt WebP file.");
+ throw new IOException("Encountered corrupt WebP file.", e);
}
}
@@ -7535,7 +7640,7 @@
case IMAGE_TYPE_PNG:
// Write PNG specific data (chunk size, chunk type)
dataOutputStream.writeInt(totalSize);
- dataOutputStream.write(PNG_CHUNK_TYPE_EXIF);
+ dataOutputStream.writeInt(PNG_CHUNK_TYPE_EXIF);
break;
case IMAGE_TYPE_WEBP:
// Write WebP specific data (chunk type, chunk size)
@@ -8149,4 +8254,12 @@
}
return false;
}
+
+ /*
+ * Combines the lower eight bits of each parameter into a 32-bit int. {@code b1} is the highest
+ * byte of the result, {@code b4} is the lowest.
+ */
+ private static int intFromBytes(int b1, int b2, int b3, int b4) {
+ return ((b1 & 0xFF) << 24) | ((b2 & 0xFF) << 16) | ((b3 & 0xFF) << 8) | (b4 & 0xFF);
+ }
}
diff --git a/fragment/fragment-compose/build.gradle b/fragment/fragment-compose/build.gradle
index ea43573..ba45262 100644
--- a/fragment/fragment-compose/build.gradle
+++ b/fragment/fragment-compose/build.gradle
@@ -21,8 +21,6 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import androidx.build.RunApiTasks
import androidx.build.LibraryType
@@ -59,8 +57,7 @@
androidx {
name = "Fragment Compose"
- type = LibraryType.PUBLISHED_LIBRARY
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2024"
description = "Integrate Fragments with Compose to provide helper APIs for using Fragments in" +
"Compose or Compose inside of Fragments"
diff --git a/fragment/fragment-ktx/build.gradle b/fragment/fragment-ktx/build.gradle
index 83d6c56..1070d06 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -21,8 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -62,7 +61,7 @@
androidx {
name = "Fragment Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'fragment' artifact"
metalavaK2UastEnabled = true
diff --git a/fragment/fragment/lint-baseline.xml b/fragment/fragment/lint-baseline.xml
new file mode 100644
index 0000000..3d7c5db8
--- /dev/null
+++ b/fragment/fragment/lint-baseline.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/fragment/app/FragmentContainerView.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/fragment/app/FragmentContainerView.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/fragment/app/FragmentContainerView.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) FragmentTransitionCompat21() else null"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/fragment/app/FragmentTransition.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/fragment/app/FragmentTransitionCompat21.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `anim`.">
+ <location
+ file="src/main/res/anim-v21"/>
+ </issue>
+
+</issues>
diff --git a/fragment/integration-tests/testapp/lint-baseline.xml b/fragment/integration-tests/testapp/lint-baseline.xml
new file mode 100644
index 0000000..4ad37fa
--- /dev/null
+++ b/fragment/integration-tests/testapp/lint-baseline.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/DetailsTransition.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/GridFragment.kt"/>
+ </issue>
+
+</issues>
diff --git a/glance/glance-appwidget-preview/api/1.1.0-beta02.txt b/glance/glance-appwidget-preview/api/1.1.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/1.1.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/graphics/graphics-shapes/api/res-1.0.0-beta02.txt b/glance/glance-appwidget-preview/api/res-1.1.0-beta02.txt
similarity index 100%
copy from graphics/graphics-shapes/api/res-1.0.0-beta02.txt
copy to glance/glance-appwidget-preview/api/res-1.1.0-beta02.txt
diff --git a/glance/glance-appwidget-preview/api/restricted_1.1.0-beta02.txt b/glance/glance-appwidget-preview/api/restricted_1.1.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/glance/glance-appwidget-preview/api/restricted_1.1.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/glance/glance-appwidget-preview/build.gradle b/glance/glance-appwidget-preview/build.gradle
index b9d7f35..ecfd0c61 100644
--- a/glance/glance-appwidget-preview/build.gradle
+++ b/glance/glance-appwidget-preview/build.gradle
@@ -59,7 +59,6 @@
androidx {
name = "Glance AppWidget Preview"
type = LibraryType.PUBLISHED_LIBRARY
- mavenVersion = LibraryVersions.GLANCE_PREVIEW
inceptionYear = "2022"
description = "Glance tooling library. This library provides the API required for the " +
"GlanceAppWidget components and its Glance @Composable to be previewable in the IDE."
diff --git a/glance/glance-appwidget-testing/build.gradle b/glance/glance-appwidget-testing/build.gradle
index 95bbf25..9390065 100644
--- a/glance/glance-appwidget-testing/build.gradle
+++ b/glance/glance-appwidget-testing/build.gradle
@@ -62,7 +62,7 @@
androidx {
name = "androidx.glance:glance-appwidget-testing"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2023"
description = "This library provides APIs for developers to use for testing their appWidget specific Glance composables."
samples(projectOrArtifact(":glance:glance-appwidget-testing:glance-appwidget-testing-samples"))
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index e0dbb33..b863b58 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -106,7 +106,7 @@
androidx {
name = "Glance For App Widgets"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "Glance-appwidgets allows developers to build layouts for Android AppWidgets " +
"using a Jetpack Compose-style API."
diff --git a/glance/glance-material3/build.gradle b/glance/glance-material3/build.gradle
index 10e4fe4..46cec12 100644
--- a/glance/glance-material3/build.gradle
+++ b/glance/glance-material3/build.gradle
@@ -32,7 +32,7 @@
androidx {
name = "Glance Material"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2022"
description = "Glance Material integration library." +
" This library provides interop APIs with Material 3."
diff --git a/glance/glance-preview/api/1.1.0-beta01.txt b/glance/glance-preview/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..e234443
--- /dev/null
+++ b/glance/glance-preview/api/1.1.0-beta01.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.glance.preview {
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGlancePreviewApi {
+ }
+
+ @SuppressCompatibility @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface Preview {
+ method public abstract int heightDp() default -1;
+ method public abstract int widthDp() default -1;
+ property public abstract int heightDp;
+ property public abstract int widthDp;
+ }
+
+ @SuppressCompatibility @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public static @interface Preview.Container {
+ method public abstract androidx.glance.preview.Preview[] value();
+ }
+
+}
+
diff --git a/glance/glance-preview/api/1.1.0-beta02.txt b/glance/glance-preview/api/1.1.0-beta02.txt
new file mode 100644
index 0000000..e234443
--- /dev/null
+++ b/glance/glance-preview/api/1.1.0-beta02.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.glance.preview {
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGlancePreviewApi {
+ }
+
+ @SuppressCompatibility @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface Preview {
+ method public abstract int heightDp() default -1;
+ method public abstract int widthDp() default -1;
+ property public abstract int heightDp;
+ property public abstract int widthDp;
+ }
+
+ @SuppressCompatibility @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public static @interface Preview.Container {
+ method public abstract androidx.glance.preview.Preview[] value();
+ }
+
+}
+
diff --git a/graphics/graphics-shapes/api/res-1.0.0-beta02.txt b/glance/glance-preview/api/res-1.1.0-beta01.txt
similarity index 100%
copy from graphics/graphics-shapes/api/res-1.0.0-beta02.txt
copy to glance/glance-preview/api/res-1.1.0-beta01.txt
diff --git a/graphics/graphics-shapes/api/res-1.0.0-beta02.txt b/glance/glance-preview/api/res-1.1.0-beta02.txt
similarity index 100%
rename from graphics/graphics-shapes/api/res-1.0.0-beta02.txt
rename to glance/glance-preview/api/res-1.1.0-beta02.txt
diff --git a/glance/glance-preview/api/restricted_1.1.0-beta01.txt b/glance/glance-preview/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..e234443
--- /dev/null
+++ b/glance/glance-preview/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.glance.preview {
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGlancePreviewApi {
+ }
+
+ @SuppressCompatibility @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface Preview {
+ method public abstract int heightDp() default -1;
+ method public abstract int widthDp() default -1;
+ property public abstract int heightDp;
+ property public abstract int widthDp;
+ }
+
+ @SuppressCompatibility @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public static @interface Preview.Container {
+ method public abstract androidx.glance.preview.Preview[] value();
+ }
+
+}
+
diff --git a/glance/glance-preview/api/restricted_1.1.0-beta02.txt b/glance/glance-preview/api/restricted_1.1.0-beta02.txt
new file mode 100644
index 0000000..e234443
--- /dev/null
+++ b/glance/glance-preview/api/restricted_1.1.0-beta02.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.glance.preview {
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalGlancePreviewApi {
+ }
+
+ @SuppressCompatibility @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface Preview {
+ method public abstract int heightDp() default -1;
+ method public abstract int widthDp() default -1;
+ property public abstract int heightDp;
+ property public abstract int widthDp;
+ }
+
+ @SuppressCompatibility @androidx.glance.preview.ExperimentalGlancePreviewApi @kotlin.annotation.Repeatable @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public static @interface Preview.Container {
+ method public abstract androidx.glance.preview.Preview[] value();
+ }
+
+}
+
diff --git a/glance/glance-preview/build.gradle b/glance/glance-preview/build.gradle
index ac6f17c..ac7fc83 100644
--- a/glance/glance-preview/build.gradle
+++ b/glance/glance-preview/build.gradle
@@ -30,7 +30,6 @@
androidx {
name = "Glance Preview"
type = LibraryType.PUBLISHED_LIBRARY
- mavenVersion = LibraryVersions.GLANCE_PREVIEW
inceptionYear = "2022"
description = "Glance preview library. This library provides the API required for marking the" +
"glance @Composable components that should have preview in the Android Studio."
diff --git a/glance/glance-template/build.gradle b/glance/glance-template/build.gradle
index 0b87855..623fb45 100644
--- a/glance/glance-template/build.gradle
+++ b/glance/glance-template/build.gradle
@@ -79,7 +79,7 @@
androidx {
name = "Glance Templates"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.GLANCE_TEMPLATE
inceptionYear = "2021"
description = "Glance allows developers to build layouts for remote surfaces using a Jetpack " +
diff --git a/glance/glance-testing/build.gradle b/glance/glance-testing/build.gradle
index de552f5..ea041d0 100644
--- a/glance/glance-testing/build.gradle
+++ b/glance/glance-testing/build.gradle
@@ -59,7 +59,7 @@
androidx {
name = "Glance Testing"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2023"
description = "This library provides base APIs to enable testing Glance"
metalavaK2UastEnabled = true
diff --git a/glance/glance-wear-tiles/build.gradle b/glance/glance-wear-tiles/build.gradle
index 7d8a4d5..67e95fe 100644
--- a/glance/glance-wear-tiles/build.gradle
+++ b/glance/glance-wear-tiles/build.gradle
@@ -93,7 +93,7 @@
androidx {
name = "Glance for Wear Tiles"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.GLANCE_WEAR_TILES
inceptionYear = "2021"
description = "Glance allows developers to build layouts for Wear Tiles using a Jetpack " +
diff --git a/glance/glance/build.gradle b/glance/glance/build.gradle
index a15d0fa..e54af2c 100644
--- a/glance/glance/build.gradle
+++ b/glance/glance/build.gradle
@@ -92,7 +92,7 @@
androidx {
name = "Glance"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "Glance allows developers to build layouts for remote surfaces using a Jetpack " +
"Compose-style API."
diff --git a/glance/glance/lint-baseline.xml b/glance/glance/lint-baseline.xml
index 523dde1..d5aa0ec 100644
--- a/glance/glance/lint-baseline.xml
+++ b/glance/glance/lint-baseline.xml
@@ -1,23 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
-
- <issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.glance`)"
- errorLine1=" .result.await()"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/glance/session/SessionManager.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.glance`)"
- errorLine1=" .await()"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/glance/session/SessionManager.kt"/>
- </issue>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="ListIterator"
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 4785dff..9c01869 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -26,7 +26,7 @@
autoValue = "1.6.3"
binaryCompatibilityValidator = "0.15.0-Beta.2"
byteBuddy = "1.14.9"
-asm = "9.3"
+asm = "9.7"
cmake = "3.22.1"
composeCompilerPlugin = "1.5.11" # Update when pulling in new stable binaries
dagger = "2.49"
@@ -115,7 +115,6 @@
dexmakerMockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker" }
dexmakerMockitoInline = { module = "com.linkedin.dexmaker:dexmaker-mockito-inline", version.ref = "dexmaker" }
dexmakerMockitoInlineExtended = { module = "com.linkedin.dexmaker:dexmaker-mockito-inline-extended", version.ref = "dexmaker" }
-dexMemberList = { module = "com.jakewharton.dex:dex-member-list", version = "4.1.1" }
dokkaCli = { module = "org.jetbrains.dokka:dokka-cli", version.ref = "dokka" }
dokkaAnalysis = { module = "org.jetbrains.dokka:dokka-analysis", version.ref = "dokka" }
dokkaAnalysisIntellij = { module = "org.jetbrains.dokka:kotlin-analysis-intellij", version.ref = "dokka" }
@@ -245,7 +244,7 @@
playServicesBase = { module = "com.google.android.gms:play-services-base", version = "17.0.0" }
playServicesBasement = { module = "com.google.android.gms:play-services-basement", version = "17.0.0" }
playServicesDevicePerformance = { module = "com.google.android.gms:play-services-deviceperformance", version = "16.0.0" }
-playServicesFido = {module = "com.google.android.gms:play-services-fido", version = "20.1.0"}
+playServicesFido = {module = "com.google.android.gms:play-services-fido", version = "21.0.0"}
playServicesWearable = { module = "com.google.android.gms:play-services-wearable", version = "17.1.0" }
protobuf = { module = "com.google.protobuf:protobuf-java", version.ref = "protobuf" }
protobufCompiler = { module = "com.google.protobuf:protoc", version.ref = "protobuf" }
diff --git a/graphics/graphics-core/lint-baseline.xml b/graphics/graphics-core/lint-baseline.xml
index 2e01a7f..7048670 100644
--- a/graphics/graphics-core/lint-baseline.xml
+++ b/graphics/graphics-core/lint-baseline.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha10" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha10)" variant="all" version="8.3.0-alpha10">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_0 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_90"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -12,7 +12,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_90 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_180"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -21,7 +21,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_180 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_270"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -30,7 +30,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_270 -> SurfaceControlCompat.BUFFER_TRANSFORM_IDENTITY"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -39,7 +39,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_0 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_180"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -48,7 +48,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_90 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_270"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -57,7 +57,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_180 -> SurfaceControlCompat.BUFFER_TRANSFORM_IDENTITY"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -66,7 +66,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_270 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_90"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -75,7 +75,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_0 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_270"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -84,7 +84,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_90 -> SurfaceControlCompat.BUFFER_TRANSFORM_IDENTITY"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -93,7 +93,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_180 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_90"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -102,7 +102,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_270 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_180"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -111,7 +111,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_0 -> SurfaceControlCompat.BUFFER_TRANSFORM_IDENTITY"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -120,7 +120,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_90 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_90"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -129,7 +129,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_180 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_180"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
@@ -138,7 +138,7 @@
<issue
id="NewApi"
- message="Field requires API level 29 (current min is 19): `Companion`"
+ message="Field requires API level 29 (current min is 21): `Companion`"
errorLine1=" Surface.ROTATION_270 -> SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_270"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
diff --git a/graphics/graphics-core/src/main/cpp/CMakeLists.txt b/graphics/graphics-core/src/main/cpp/CMakeLists.txt
index bc21166..91e7e97 100644
--- a/graphics/graphics-core/src/main/cpp/CMakeLists.txt
+++ b/graphics/graphics-core/src/main/cpp/CMakeLists.txt
@@ -4,7 +4,7 @@
# Sets the minimum version of CMake required to build the native library.
-cmake_minimum_required(VERSION 3.18.1)
+cmake_minimum_required(VERSION 3.22.1)
# Declares and names the project.
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLSpec.kt b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLSpec.kt
index 97d6f65..469140c 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLSpec.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLSpec.kt
@@ -111,6 +111,7 @@
* See https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglMakeCurrent.xhtml for more
* information
*
+ * @param context EGL rendering context to be attached to the surfaces
* @param drawSurface EGLSurface to draw pixels into.
* @param readSurface EGLSurface used for read/copy operations.
*/
diff --git a/graphics/graphics-path/build.gradle b/graphics/graphics-path/build.gradle
index 753e958..f24d85e 100644
--- a/graphics/graphics-path/build.gradle
+++ b/graphics/graphics-path/build.gradle
@@ -55,7 +55,6 @@
"-std=c++17",
"-Wno-unused-command-line-argument",
"-Wl,--hash-style=both", // Required to support API levels below 23
- "-fno-stack-protector",
"-fno-exceptions",
"-fno-unwind-tables",
"-fno-asynchronous-unwind-tables",
@@ -67,7 +66,6 @@
"-fomit-frame-pointer",
"-ffunction-sections",
"-fdata-sections",
- "-fstack-protector",
"-Wl,--gc-sections",
"-Wl,-Bsymbolic-functions",
"-nostdlib++"
diff --git a/graphics/graphics-path/src/main/cpp/CMakeLists.txt b/graphics/graphics-path/src/main/cpp/CMakeLists.txt
index 722eb2d..540919f 100644
--- a/graphics/graphics-path/src/main/cpp/CMakeLists.txt
+++ b/graphics/graphics-path/src/main/cpp/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.18.1)
+cmake_minimum_required(VERSION 3.22.1)
project("androidx.graphics.path")
set(VERSION_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/libandroidx.graphics.path.map")
diff --git a/graphics/graphics-shapes/api/1.0.0-beta02.txt b/graphics/graphics-shapes/api/1.0.0-beta02.txt
deleted file mode 100644
index f9d62d6f..0000000
--- a/graphics/graphics-shapes/api/1.0.0-beta02.txt
+++ /dev/null
@@ -1,163 +0,0 @@
-// Signature format: 4.0
-package androidx.graphics.shapes {
-
- public final class CornerRounding {
- ctor public CornerRounding(optional @FloatRange(from=0.0) float radius, optional @FloatRange(from=0.0, to=1.0) float smoothing);
- method public float getRadius();
- method public float getSmoothing();
- property public final float radius;
- property public final float smoothing;
- field public static final androidx.graphics.shapes.CornerRounding.Companion Companion;
- field public static final androidx.graphics.shapes.CornerRounding Unrounded;
- }
-
- public static final class CornerRounding.Companion {
- }
-
- public class Cubic {
- method public static final androidx.graphics.shapes.Cubic circularArc(float centerX, float centerY, float x0, float y0, float x1, float y1);
- method public final operator androidx.graphics.shapes.Cubic div(float x);
- method public final operator androidx.graphics.shapes.Cubic div(int x);
- method public final float getAnchor0X();
- method public final float getAnchor0Y();
- method public final float getAnchor1X();
- method public final float getAnchor1Y();
- method public final float getControl0X();
- method public final float getControl0Y();
- method public final float getControl1X();
- method public final float getControl1Y();
- method public final operator androidx.graphics.shapes.Cubic plus(androidx.graphics.shapes.Cubic o);
- method public final androidx.graphics.shapes.Cubic reverse();
- method public final kotlin.Pair<androidx.graphics.shapes.Cubic,androidx.graphics.shapes.Cubic> split(float t);
- method public static final androidx.graphics.shapes.Cubic straightLine(float x0, float y0, float x1, float y1);
- method public final operator androidx.graphics.shapes.Cubic times(float x);
- method public final operator androidx.graphics.shapes.Cubic times(int x);
- method public final androidx.graphics.shapes.Cubic transformed(androidx.graphics.shapes.PointTransformer f);
- property public final float anchor0X;
- property public final float anchor0Y;
- property public final float anchor1X;
- property public final float anchor1Y;
- property public final float control0X;
- property public final float control0Y;
- property public final float control1X;
- property public final float control1Y;
- field public static final androidx.graphics.shapes.Cubic.Companion Companion;
- }
-
- public static final class Cubic.Companion {
- method public androidx.graphics.shapes.Cubic circularArc(float centerX, float centerY, float x0, float y0, float x1, float y1);
- method public androidx.graphics.shapes.Cubic straightLine(float x0, float y0, float x1, float y1);
- }
-
- public final class CubicKt {
- method public static androidx.graphics.shapes.Cubic Cubic(float anchor0X, float anchor0Y, float control0X, float control0Y, float control1X, float control1Y, float anchor1X, float anchor1Y);
- }
-
- public final class Morph {
- ctor public Morph(androidx.graphics.shapes.RoundedPolygon start, androidx.graphics.shapes.RoundedPolygon end);
- method public java.util.List<androidx.graphics.shapes.Cubic> asCubics(float progress);
- method public float[] calculateBounds();
- method public float[] calculateBounds(optional float[] bounds);
- method public float[] calculateBounds(optional float[] bounds, optional boolean approximate);
- method public float[] calculateMaxBounds(optional float[] bounds);
- method public inline void forEachCubic(float progress, optional androidx.graphics.shapes.MutableCubic mutableCubic, kotlin.jvm.functions.Function1<? super androidx.graphics.shapes.MutableCubic,kotlin.Unit> callback);
- method public inline void forEachCubic(float progress, kotlin.jvm.functions.Function1<? super androidx.graphics.shapes.MutableCubic,kotlin.Unit> callback);
- }
-
- public final class MutableCubic extends androidx.graphics.shapes.Cubic {
- ctor public MutableCubic();
- method public void interpolate(androidx.graphics.shapes.Cubic c1, androidx.graphics.shapes.Cubic c2, float progress);
- method public void transform(androidx.graphics.shapes.PointTransformer f);
- }
-
- public interface MutablePoint {
- method public float getX();
- method public float getY();
- method public void setX(float);
- method public void setY(float);
- property public abstract float x;
- property public abstract float y;
- }
-
- public fun interface PointTransformer {
- method public long transform(float x, float y);
- }
-
- public final class RoundedPolygon {
- method public float[] calculateBounds();
- method public float[] calculateBounds(optional float[] bounds);
- method public float[] calculateBounds(optional float[] bounds, optional boolean approximate);
- method public float[] calculateMaxBounds(optional float[] bounds);
- method public float getCenterX();
- method public float getCenterY();
- method public java.util.List<androidx.graphics.shapes.Cubic> getCubics();
- method public androidx.graphics.shapes.RoundedPolygon normalized();
- method public androidx.graphics.shapes.RoundedPolygon transformed(androidx.graphics.shapes.PointTransformer f);
- property public final float centerX;
- property public final float centerY;
- property public final java.util.List<androidx.graphics.shapes.Cubic> cubics;
- field public static final androidx.graphics.shapes.RoundedPolygon.Companion Companion;
- }
-
- public static final class RoundedPolygon.Companion {
- }
-
- public final class RoundedPolygonKt {
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY, optional androidx.graphics.shapes.CornerRounding rounding);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
- }
-
- public final class ShapesKt {
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
- }
-
- public final class Shapes_androidKt {
- method public static android.graphics.Path toPath(androidx.graphics.shapes.Morph, float progress, optional android.graphics.Path path);
- method public static android.graphics.Path toPath(androidx.graphics.shapes.RoundedPolygon);
- method public static android.graphics.Path toPath(androidx.graphics.shapes.RoundedPolygon, optional android.graphics.Path path);
- method public static androidx.graphics.shapes.RoundedPolygon transformed(androidx.graphics.shapes.RoundedPolygon, android.graphics.Matrix matrix);
- }
-
-}
-
diff --git a/graphics/graphics-shapes/api/restricted_1.0.0-beta02.txt b/graphics/graphics-shapes/api/restricted_1.0.0-beta02.txt
deleted file mode 100644
index d691f29..0000000
--- a/graphics/graphics-shapes/api/restricted_1.0.0-beta02.txt
+++ /dev/null
@@ -1,164 +0,0 @@
-// Signature format: 4.0
-package androidx.graphics.shapes {
-
- public final class CornerRounding {
- ctor public CornerRounding(optional @FloatRange(from=0.0) float radius, optional @FloatRange(from=0.0, to=1.0) float smoothing);
- method public float getRadius();
- method public float getSmoothing();
- property public final float radius;
- property public final float smoothing;
- field public static final androidx.graphics.shapes.CornerRounding.Companion Companion;
- field public static final androidx.graphics.shapes.CornerRounding Unrounded;
- }
-
- public static final class CornerRounding.Companion {
- }
-
- public class Cubic {
- method public static final androidx.graphics.shapes.Cubic circularArc(float centerX, float centerY, float x0, float y0, float x1, float y1);
- method public final operator androidx.graphics.shapes.Cubic div(float x);
- method public final operator androidx.graphics.shapes.Cubic div(int x);
- method public final float getAnchor0X();
- method public final float getAnchor0Y();
- method public final float getAnchor1X();
- method public final float getAnchor1Y();
- method public final float getControl0X();
- method public final float getControl0Y();
- method public final float getControl1X();
- method public final float getControl1Y();
- method public final operator androidx.graphics.shapes.Cubic plus(androidx.graphics.shapes.Cubic o);
- method public final androidx.graphics.shapes.Cubic reverse();
- method public final kotlin.Pair<androidx.graphics.shapes.Cubic,androidx.graphics.shapes.Cubic> split(float t);
- method public static final androidx.graphics.shapes.Cubic straightLine(float x0, float y0, float x1, float y1);
- method public final operator androidx.graphics.shapes.Cubic times(float x);
- method public final operator androidx.graphics.shapes.Cubic times(int x);
- method public final androidx.graphics.shapes.Cubic transformed(androidx.graphics.shapes.PointTransformer f);
- property public final float anchor0X;
- property public final float anchor0Y;
- property public final float anchor1X;
- property public final float anchor1Y;
- property public final float control0X;
- property public final float control0Y;
- property public final float control1X;
- property public final float control1Y;
- field public static final androidx.graphics.shapes.Cubic.Companion Companion;
- }
-
- public static final class Cubic.Companion {
- method public androidx.graphics.shapes.Cubic circularArc(float centerX, float centerY, float x0, float y0, float x1, float y1);
- method public androidx.graphics.shapes.Cubic straightLine(float x0, float y0, float x1, float y1);
- }
-
- public final class CubicKt {
- method public static androidx.graphics.shapes.Cubic Cubic(float anchor0X, float anchor0Y, float control0X, float control0Y, float control1X, float control1Y, float anchor1X, float anchor1Y);
- }
-
- public final class Morph {
- ctor public Morph(androidx.graphics.shapes.RoundedPolygon start, androidx.graphics.shapes.RoundedPolygon end);
- method public java.util.List<androidx.graphics.shapes.Cubic> asCubics(float progress);
- method public float[] calculateBounds();
- method public float[] calculateBounds(optional float[] bounds);
- method public float[] calculateBounds(optional float[] bounds, optional boolean approximate);
- method public float[] calculateMaxBounds(optional float[] bounds);
- method public inline void forEachCubic(float progress, optional androidx.graphics.shapes.MutableCubic mutableCubic, kotlin.jvm.functions.Function1<? super androidx.graphics.shapes.MutableCubic,kotlin.Unit> callback);
- method public inline void forEachCubic(float progress, kotlin.jvm.functions.Function1<? super androidx.graphics.shapes.MutableCubic,kotlin.Unit> callback);
- property @kotlin.PublishedApi internal final java.util.List<kotlin.Pair<androidx.graphics.shapes.Cubic,androidx.graphics.shapes.Cubic>> morphMatch;
- }
-
- public final class MutableCubic extends androidx.graphics.shapes.Cubic {
- ctor public MutableCubic();
- method public void interpolate(androidx.graphics.shapes.Cubic c1, androidx.graphics.shapes.Cubic c2, float progress);
- method public void transform(androidx.graphics.shapes.PointTransformer f);
- }
-
- public interface MutablePoint {
- method public float getX();
- method public float getY();
- method public void setX(float);
- method public void setY(float);
- property public abstract float x;
- property public abstract float y;
- }
-
- public fun interface PointTransformer {
- method public long transform(float x, float y);
- }
-
- public final class RoundedPolygon {
- method public float[] calculateBounds();
- method public float[] calculateBounds(optional float[] bounds);
- method public float[] calculateBounds(optional float[] bounds, optional boolean approximate);
- method public float[] calculateMaxBounds(optional float[] bounds);
- method public float getCenterX();
- method public float getCenterY();
- method public java.util.List<androidx.graphics.shapes.Cubic> getCubics();
- method public androidx.graphics.shapes.RoundedPolygon normalized();
- method public androidx.graphics.shapes.RoundedPolygon transformed(androidx.graphics.shapes.PointTransformer f);
- property public final float centerX;
- property public final float centerY;
- property public final java.util.List<androidx.graphics.shapes.Cubic> cubics;
- field public static final androidx.graphics.shapes.RoundedPolygon.Companion Companion;
- }
-
- public static final class RoundedPolygon.Companion {
- }
-
- public final class RoundedPolygonKt {
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY, optional androidx.graphics.shapes.CornerRounding rounding);
- method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
- }
-
- public final class ShapesKt {
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX);
- method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
- }
-
- public final class Shapes_androidKt {
- method public static android.graphics.Path toPath(androidx.graphics.shapes.Morph, float progress, optional android.graphics.Path path);
- method public static android.graphics.Path toPath(androidx.graphics.shapes.RoundedPolygon);
- method public static android.graphics.Path toPath(androidx.graphics.shapes.RoundedPolygon, optional android.graphics.Path path);
- method public static androidx.graphics.shapes.RoundedPolygon transformed(androidx.graphics.shapes.RoundedPolygon, android.graphics.Matrix matrix);
- }
-
-}
-
diff --git a/hilt/hilt-navigation-compose/build.gradle b/hilt/hilt-navigation-compose/build.gradle
index 5383e0d..cba9404 100644
--- a/hilt/hilt-navigation-compose/build.gradle
+++ b/hilt/hilt-navigation-compose/build.gradle
@@ -21,8 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import androidx.build.RunApiTasks
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -72,11 +71,10 @@
androidx {
name = "Navigation Compose Hilt Integration"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.HILT_NAVIGATION_COMPOSE
inceptionYear = "2021"
description = "Navigation Compose Hilt Integration"
- runApiTasks = new RunApiTasks.Yes()
legacyDisableKotlinStrictApiMode = true
metalavaK2UastEnabled = true
samples(projectOrArtifact(":hilt:hilt-navigation-compose-samples"))
diff --git a/hilt/hilt-navigation/build.gradle b/hilt/hilt-navigation/build.gradle
index 4fe350f..07b1c52 100644
--- a/hilt/hilt-navigation/build.gradle
+++ b/hilt/hilt-navigation/build.gradle
@@ -27,7 +27,7 @@
id("AndroidXPlugin")
id("com.android.library")
id("kotlin-android")
- id("kotlin-kapt")
+ id("com.google.devtools.ksp")
}
dependencies {
@@ -35,7 +35,7 @@
api("androidx.annotation:annotation:1.1.0")
api("androidx.navigation:navigation-runtime:2.5.1")
api(libs.hiltAndroid)
- kapt(libs.hiltCompiler)
+ ksp(libs.hiltCompiler)
}
androidx {
diff --git a/input/input-motionprediction/api/1.0.0-beta04.txt b/input/input-motionprediction/api/1.0.0-beta04.txt
new file mode 100644
index 0000000..b0eef8e
--- /dev/null
+++ b/input/input-motionprediction/api/1.0.0-beta04.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.input.motionprediction {
+
+ public interface MotionEventPredictor {
+ method public static androidx.input.motionprediction.MotionEventPredictor newInstance(android.view.View);
+ method public android.view.MotionEvent? predict();
+ method public void record(android.view.MotionEvent);
+ }
+
+}
+
diff --git a/graphics/graphics-shapes/api/res-1.0.0-beta02.txt b/input/input-motionprediction/api/res-1.0.0-beta04.txt
similarity index 100%
copy from graphics/graphics-shapes/api/res-1.0.0-beta02.txt
copy to input/input-motionprediction/api/res-1.0.0-beta04.txt
diff --git a/input/input-motionprediction/api/restricted_1.0.0-beta04.txt b/input/input-motionprediction/api/restricted_1.0.0-beta04.txt
new file mode 100644
index 0000000..b0eef8e
--- /dev/null
+++ b/input/input-motionprediction/api/restricted_1.0.0-beta04.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.input.motionprediction {
+
+ public interface MotionEventPredictor {
+ method public static androidx.input.motionprediction.MotionEventPredictor newInstance(android.view.View);
+ method public android.view.MotionEvent? predict();
+ method public void record(android.view.MotionEvent);
+ }
+
+}
+
diff --git a/input/input-motionprediction/build.gradle b/input/input-motionprediction/build.gradle
index cb3871b..e87252d 100644
--- a/input/input-motionprediction/build.gradle
+++ b/input/input-motionprediction/build.gradle
@@ -49,7 +49,6 @@
android {
defaultConfig {
- minSdkVersion 19
}
namespace "androidx.input.motionprediction"
}
diff --git a/input/input-motionprediction/lint-baseline.xml b/input/input-motionprediction/lint-baseline.xml
new file mode 100644
index 0000000..c2602a3
--- /dev/null
+++ b/input/input-motionprediction/lint-baseline.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/input/motionprediction/common/PredictionEstimator.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/input/motionprediction/common/PredictionEstimator.java"/>
+ </issue>
+
+</issues>
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
index 65a95d3..d5a7f7d 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
@@ -176,7 +176,11 @@
fun packageInspector(libraryProject: Project, inspectorProjectPath: String) {
val inspectorProject = libraryProject.rootProject.findProject(inspectorProjectPath)
if (inspectorProject == null) {
- check(libraryProject.property("androidx.studio.type") == "playground") {
+ val extraProperties = libraryProject.extensions.extraProperties
+ check(
+ extraProperties.has("androidx.studio.type") &&
+ extraProperties.get("androidx.studio.type") == "playground"
+ ) {
"Cannot find $inspectorProjectPath. This is optional only for playground builds."
}
// skip setting up inspector project
diff --git a/inspection/inspection/src/main/native/CMakeLists.txt b/inspection/inspection/src/main/native/CMakeLists.txt
index 0897442..b030418 100644
--- a/inspection/inspection/src/main/native/CMakeLists.txt
+++ b/inspection/inspection/src/main/native/CMakeLists.txt
@@ -14,7 +14,7 @@
# the License.
#
-cmake_minimum_required(VERSION 3.4.1)
+cmake_minimum_required(VERSION 3.22.1)
project(compose_inspection_inspection)
diff --git a/kruth/kruth/lint-baseline.xml b/kruth/kruth/lint-baseline.xml
index 7c9829d..1fb889c 100644
--- a/kruth/kruth/lint-baseline.xml
+++ b/kruth/kruth/lint-baseline.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanUncheckedReflection"
message="Method.invoke requires both an upper and lower SDK bounds checks to be safe, and the upper bound must be below SdkVersionInfo.HIGHEST_KNOWN_API."
- errorLine1=" requireNonNull(getSuppressed.invoke(throwable)) as Array<Throwable>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" val result = requireNonNull(getSuppressed.invoke(throwable))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/jvmMain/kotlin/androidx/kruth/Platform.jvm.kt"/>
</issue>
diff --git a/leanback/leanback/lint-baseline.xml b/leanback/leanback/lint-baseline.xml
index 2a61548..32fe49d 100644
--- a/leanback/leanback/lint-baseline.xml
+++ b/leanback/leanback/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -1344,87 +1344,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.leanback.app.GuidedStepFragment is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" ft.addSharedElement(subView, transitionName);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.leanback.app.GuidedStepFragment is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setEnterTransition((android.transition.Transition) enterTransition);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.leanback.app.GuidedStepFragment is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setSharedElementEnterTransition((android.transition.Transition) sharedElementTransition);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.leanback.app.GuidedStepFragment is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setEnterTransition((android.transition.Transition) enterTransition);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.leanback.app.GuidedStepFragment is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setEnterTransition((android.transition.Transition) enterTransition);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.leanback.app.GuidedStepFragment is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setSharedElementEnterTransition(null);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.leanback.app.GuidedStepFragment is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setEnterTransition(null);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.leanback.app.GuidedStepFragment is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setSharedElementEnterTransition(null);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.leanback.app.GuidedStepFragment is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" setExitTransition((android.transition.Transition) exitTransition);"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 23; however, the containing class androidx.leanback.app.PermissionHelper is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" fragment.requestPermissions(permissions, requestCode);"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
@@ -1433,15 +1352,6 @@
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" ? !mProgressBar.isAccessibilityFocused() : true);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/leanback/widget/PlaybackTransportRowPresenter.java"/>
- </issue>
-
- <issue
id="PrivateConstructorForUtilityClass"
message="Utility class is missing private constructor"
errorLine1="public class FocusHighlightHelper {"
@@ -1452,26 +1362,540 @@
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v19`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `transition`.">
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/app/DetailsFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/app/DetailsSupportFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="api21/androidx/leanback/transition/FadeAndShortSlide.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" return VERSION.SDK_INT >= 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/GuidedActionsStylist.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (IS_FRAMEWORK_FRAGMENT && Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 23"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/app/GuidedStepFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (IS_FRAMEWORK_FRAGMENT && Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 23"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/app/GuidedStepSupportFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/app/GuidedStepSupportFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/app/GuidedStepSupportFragment.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/ParallaxTransition.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" stopSeek(Build.VERSION.SDK_INT >= 21"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/PlaybackTransportRowPresenter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" return Build.VERSION.SDK_INT >= 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/RoundedRectHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/RoundedRectHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/RoundedRectHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/RoundedRectHelperApi21.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/system/Settings.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" return Build.VERSION.SDK_INT >= 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/ShadowHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/ShadowHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/ShadowHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/ShadowHelperApi21.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="api21/androidx/leanback/transition/SlideNoPropagation.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" return Build.VERSION.SDK_INT >= 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/StaticShadowHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/StaticShadowHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/StaticShadowHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/widget/StaticShadowHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" return Build.VERSION.SDK_INT >= 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/leanback/transition/TransitionHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="api21/androidx/leanback/transition/TranslationAnimationCreator.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `animator`.">
+ <location
+ file="src/main/res/animator-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `drawable`.">
+ <location
+ file="src/main/res/drawable-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v19`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `transition`.">
<location
file="src/main/res/transition-v19"/>
</issue>
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v18`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `values`.">
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `transition`.">
+ <location
+ file="src/main/res/transition-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v18`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
<location
file="src/main/res/values-v18"/>
</issue>
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v19`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `values`.">
+ message="This folder configuration (`v19`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
<location
file="src/main/res/values-v19"/>
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="src/main/res/values-v21"/>
+ </issue>
+
+ <issue
id="KotlinPropertyAccess"
message="This method should be called `getHasMediaRowSeparator` such that `hasMediaRowSeparator` can be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
errorLine1=" public boolean hasMediaRowSeparator() {"
diff --git a/libraryversions.toml b/libraryversions.toml
index 0616576..9df5691 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -4,7 +4,7 @@
ANNOTATION_EXPERIMENTAL = "1.4.0-rc01"
APPACTIONS_BUILTINTYPES = "1.0.0-alpha01"
APPACTIONS_INTERACTION = "1.0.0-alpha01"
-APPCOMPAT = "1.7.0-alpha04"
+APPCOMPAT = "1.7.0-beta01"
APPSEARCH = "1.1.0-alpha04"
ARCH_CORE = "2.3.0-alpha01"
ASYNCLAYOUTINFLATER = "1.1.0-alpha02"
@@ -23,10 +23,10 @@
CAR_APP = "1.7.0-alpha02"
COLLECTION = "1.5.0-alpha01"
COMPOSE = "1.7.0-alpha08"
-COMPOSE_COMPILER = "1.5.12" # Update when preparing for a release
-COMPOSE_MATERIAL3 = "1.3.0-alpha05"
-COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha11"
-COMPOSE_MATERIAL3_ADAPTIVE_NAVIGATION_SUITE = "1.0.0-alpha06"
+COMPOSE_COMPILER = "1.5.13" # Update when preparing for a release
+COMPOSE_MATERIAL3 = "1.3.0-alpha06"
+COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha12"
+COMPOSE_MATERIAL3_ADAPTIVE_NAVIGATION_SUITE = "1.0.0-alpha07"
COMPOSE_MATERIAL3_COMMON = "1.0.0-alpha01"
COMPOSE_RUNTIME_TRACING = "1.0.0-beta01"
CONSTRAINTLAYOUT = "2.2.0-alpha13"
@@ -65,15 +65,14 @@
ENTERPRISE = "1.1.0-rc01"
EXIFINTERFACE = "1.4.0-alpha01"
FRAGMENT = "1.8.0-alpha02"
-FUTURES = "1.2.0-alpha03"
+FUTURES = "1.2.0-beta01"
GLANCE = "1.1.0-beta02"
-GLANCE_PREVIEW = "1.0.0-alpha06"
GLANCE_TEMPLATE = "1.0.0-alpha06"
GLANCE_WEAR_TILES = "1.0.0-alpha06"
GRAPHICS_CORE = "1.0.0-rc01"
GRAPHICS_FILTERS = "1.0.0-alpha01"
GRAPHICS_PATH = "1.0.0-rc01"
-GRAPHICS_SHAPES = "1.0.0-beta02"
+GRAPHICS_SHAPES = "1.0.0-beta01"
GRIDLAYOUT = "1.1.0-beta02"
HEALTH_CONNECT = "1.1.0-alpha08"
HEALTH_SERVICES_CLIENT = "1.1.0-alpha03"
@@ -81,7 +80,7 @@
HILT = "1.2.0-rc01"
HILT_NAVIGATION = "1.2.0-rc01"
HILT_NAVIGATION_COMPOSE = "1.2.0-rc01"
-INPUT_MOTIONPREDICTION = "1.0.0-beta03"
+INPUT_MOTIONPREDICTION = "1.0.0-beta04"
INSPECTION = "1.0.0"
INTERPOLATOR = "1.1.0-alpha01"
JAVASCRIPTENGINE = "1.0.0-beta01"
@@ -108,7 +107,7 @@
PREFERENCE = "1.3.0-alpha01"
PRINT = "1.1.0-beta01"
PRIVACYSANDBOX_ACTIVITY = "1.0.0-alpha01"
-PRIVACYSANDBOX_ADS = "1.1.0-beta06"
+PRIVACYSANDBOX_ADS = "1.1.0-beta07"
PRIVACYSANDBOX_PLUGINS = "1.0.0-alpha03"
PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha13"
PRIVACYSANDBOX_TOOLS = "1.0.0-alpha08"
@@ -157,8 +156,8 @@
VIEWPAGER = "1.1.0-alpha02"
VIEWPAGER2 = "1.1.0-rc01"
WEAR = "1.4.0-alpha01"
-WEAR_COMPOSE = "1.4.0-alpha07"
-WEAR_COMPOSE_MATERIAL3 = "1.0.0-alpha21"
+WEAR_COMPOSE = "1.4.0-alpha08"
+WEAR_COMPOSE_MATERIAL3 = "1.0.0-alpha22"
WEAR_CORE = "1.0.0-alpha01"
WEAR_INPUT = "1.2.0-alpha03"
WEAR_INPUT_TESTING = "1.2.0-alpha03"
@@ -172,7 +171,7 @@
WEBKIT = "1.12.0-alpha01"
# Adding a comment to prevent merge conflicts for Window artifact
WINDOW = "1.3.0-beta02"
-WINDOW_EXTENSIONS = "1.3.0-beta01"
+WINDOW_EXTENSIONS = "1.3.0-rc01"
WINDOW_EXTENSIONS_CORE = "1.1.0-alpha01"
WINDOW_SIDECAR = "1.0.0-rc01"
WORK = "2.10.0-alpha02"
diff --git a/lifecycle/lifecycle-compiler/src/main/resources/NOTICE.txt b/lifecycle/lifecycle-compiler/src/main/resources/META-INF/NOTICE.txt
similarity index 100%
rename from lifecycle/lifecycle-compiler/src/main/resources/NOTICE.txt
rename to lifecycle/lifecycle-compiler/src/main/resources/META-INF/NOTICE.txt
diff --git a/lifecycle/lifecycle-livedata-core-ktx/build.gradle b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
index 249fa37..df08ba90 100644
--- a/lifecycle/lifecycle-livedata-core-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
@@ -21,8 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -37,7 +36,7 @@
androidx {
name = "LiveData Core Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'livedata-core' artifact"
metalavaK2UastEnabled = true
diff --git a/lifecycle/lifecycle-livedata-ktx/build.gradle b/lifecycle/lifecycle-livedata-ktx/build.gradle
index b54fd32..d059f30 100644
--- a/lifecycle/lifecycle-livedata-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-ktx/build.gradle
@@ -21,8 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -51,7 +50,7 @@
androidx {
name = "LiveData Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'livedata' artifact"
metalavaK2UastEnabled = true
diff --git a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
index 3bf9a40..e1ae335 100644
--- a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
+++ b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
@@ -21,8 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -44,7 +43,7 @@
androidx {
name = "Lifecycle ReactiveStreams KTX"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for Lifecycle ReactiveStreams"
metalavaK2UastEnabled = true
diff --git a/lifecycle/lifecycle-runtime-compose/build.gradle b/lifecycle/lifecycle-runtime-compose/build.gradle
index 57cbcba..31bae0b 100644
--- a/lifecycle/lifecycle-runtime-compose/build.gradle
+++ b/lifecycle/lifecycle-runtime-compose/build.gradle
@@ -72,7 +72,7 @@
androidx {
name = "Lifecycle Runtime Compose"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "Compose integration with Lifecycle"
samples(project(":lifecycle:lifecycle-runtime-compose:lifecycle-runtime-compose-samples"))
diff --git a/lifecycle/lifecycle-runtime-ktx/build.gradle b/lifecycle/lifecycle-runtime-ktx/build.gradle
index 78fe658..ff62f2b 100644
--- a/lifecycle/lifecycle-runtime-ktx/build.gradle
+++ b/lifecycle/lifecycle-runtime-ktx/build.gradle
@@ -22,8 +22,8 @@
* modifying its settings.
*/
+import androidx.build.LibraryType
import androidx.build.PlatformIdentifier
-import androidx.build.Publish
plugins {
id("AndroidXPlugin")
@@ -55,7 +55,7 @@
androidx {
name = "Lifecycle Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Kotlin extensions for 'lifecycle' artifact"
metalavaK2UastEnabled = true
diff --git a/lifecycle/lifecycle-viewmodel-compose/build.gradle b/lifecycle/lifecycle-viewmodel-compose/build.gradle
index b3f68562..6341854 100644
--- a/lifecycle/lifecycle-viewmodel-compose/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/build.gradle
@@ -21,11 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-
+import androidx.build.LibraryType
import androidx.build.PlatformIdentifier
-import androidx.build.Publish
-import androidx.build.RunApiTasks
-import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
plugins {
id("AndroidXPlugin")
@@ -94,10 +91,9 @@
androidx {
name = "Lifecycle ViewModel Compose"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "Compose integration with Lifecycle ViewModel"
- runApiTasks = new RunApiTasks.Yes()
samples(projectOrArtifact(":lifecycle:lifecycle-viewmodel-compose:lifecycle-viewmodel-compose-samples"))
}
diff --git a/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaverTest.android.kt b/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaverTest.android.kt
index 1463fc7..8bcf5f0 100644
--- a/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaverTest.android.kt
+++ b/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaverTest.android.kt
@@ -312,6 +312,53 @@
assertThat(getCount!!()).isEqualTo(1)
assertThat(savedStateHandle?.keys()).isEqualTo(setOf("count"))
}
+
+ @OptIn(SavedStateHandleSaveableApi::class)
+ @Test
+ fun nullableMutableState_delegate_simpleRestore() {
+ var savedStateHandle: SavedStateHandle? = null
+ var getOptionalCount: (() -> Int?)? = null
+ var setOptionalCount: ((Int?) -> Unit)? = null
+ activityTestRuleScenario.scenario.onActivity { activity ->
+ activity.setContent {
+ val viewModel = viewModel<SavingTestViewModel>(activity)
+ savedStateHandle = viewModel.savedStateHandle
+ var count: Int? by viewModel.savedStateHandle.saveable {
+ mutableStateOf(null)
+ }
+ getOptionalCount = { count }
+ setOptionalCount = { count = it }
+ }
+ }
+
+ assertThat(getOptionalCount!!()).isNull()
+ assertThat(savedStateHandle?.keys()).isEqualTo(setOf("count"))
+
+ activityTestRuleScenario.scenario.onActivity {
+ setOptionalCount!!(1)
+ // we null all to ensure recomposition happened
+ getOptionalCount = null
+ setOptionalCount = null
+ savedStateHandle = null
+ }
+
+ activityTestRuleScenario.scenario.recreate()
+
+ activityTestRuleScenario.scenario.onActivity { activity ->
+ activity.setContent {
+ val viewModel = viewModel<SavingTestViewModel>(activity)
+ savedStateHandle = viewModel.savedStateHandle
+ var count: Int? by viewModel.savedStateHandle.saveable {
+ mutableStateOf(null)
+ }
+ getOptionalCount = { count }
+ setOptionalCount = { count = it }
+ }
+ }
+
+ assertThat(getOptionalCount!!()).isEqualTo(1)
+ assertThat(savedStateHandle?.keys()).isEqualTo(setOf("count"))
+ }
}
class SavingTestViewModel(val savedStateHandle: SavedStateHandle) : ViewModel()
diff --git a/lifecycle/lifecycle-viewmodel-compose/src/androidMain/kotlin/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaver.android.kt b/lifecycle/lifecycle-viewmodel-compose/src/androidMain/kotlin/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaver.android.kt
index 69011f9..ada4c35 100644
--- a/lifecycle/lifecycle-viewmodel-compose/src/androidMain/kotlin/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaver.android.kt
+++ b/lifecycle/lifecycle-viewmodel-compose/src/androidMain/kotlin/androidx/lifecycle/viewmodel/compose/SavedStateHandleSaver.android.kt
@@ -151,7 +151,7 @@
*/
@SavedStateHandleSaveableApi
@JvmName("saveableMutableState")
-fun <T : Any, M : MutableState<T>> SavedStateHandle.saveable(
+fun <T, M : MutableState<T>> SavedStateHandle.saveable(
stateSaver: Saver<T, out Any> = autoSaver(),
init: () -> M,
): PropertyDelegateProvider<Any?, ReadWriteProperty<Any?, T>> =
diff --git a/lifecycle/lifecycle-viewmodel-ktx/build.gradle b/lifecycle/lifecycle-viewmodel-ktx/build.gradle
index 5f99fe4..c488ac6 100644
--- a/lifecycle/lifecycle-viewmodel-ktx/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -37,7 +37,7 @@
androidx {
name = "Lifecycle ViewModel Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'viewmodel' artifact"
metalavaK2UastEnabled = true
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/lint-baseline.xml b/lifecycle/lifecycle-viewmodel-savedstate/lint-baseline.xml
new file mode 100644
index 0000000..d1cab9b
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-savedstate/lint-baseline.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/lifecycle/SavedStateHandle.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/lifecycle/SavedStateHandle.kt"/>
+ </issue>
+
+</issues>
diff --git a/lint-checks/integration-tests/lint-baseline.xml b/lint-checks/integration-tests/lint-baseline.xml
index 99036fe..f0e1d46 100644
--- a/lint-checks/integration-tests/lint-baseline.xml
+++ b/lint-checks/integration-tests/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="MissingClass"
@@ -12,7 +12,7 @@
<issue
id="NewApi"
- message="Call requires API level 23 (current min is 19): `android.view.View#getAccessibilityClassName`"
+ message="Call requires API level 23 (current min is 21): `android.view.View#getAccessibilityClassName`"
errorLine1=" return view.getAccessibilityClassName();"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -147,24 +147,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.sample.appcompat.widget.ActionBarBackgroundDrawable is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mContainer.mSplitBackground.getOutline(outline);"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/sample/appcompat/widget/ActionBarBackgroundDrawable.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.sample.appcompat.widget.ActionBarBackgroundDrawable is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mContainer.mBackground.getOutline(outline);"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/sample/appcompat/widget/ActionBarBackgroundDrawable.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 27; however, the containing class androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" adapter.setAutofillOptions();"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
@@ -201,42 +183,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeCallToThis is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" getClipToPadding();"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/AutofixUnsafeCallToThis.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeCallToThis is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" this.getClipToPadding();"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/AutofixUnsafeCallToThis.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeCallToThis is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super.getClipToPadding();"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/AutofixUnsafeCallToThis.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 20; however, the containing class androidx.AutofixUnsafeCallWithImplicitParamCast is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" builder.extend(extender);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/AutofixUnsafeCallWithImplicitParamCast.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return new AdaptiveIconDrawable(null, null);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -309,51 +255,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeReferenceWithExistingClassJava is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setBackgroundTintList(new ColorStateList(null, null));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/AutofixUnsafeReferenceWithExistingClassJava.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeReferenceWithExistingFix is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setBackgroundTintList(new ColorStateList(null, null));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/AutofixUnsafeReferenceWithExistingFix.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeReferenceWithExistingFix is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" drawable.getOutline(null);"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/AutofixUnsafeReferenceWithExistingFix.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeVoidMethodReferenceJava is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setBackgroundTintList(new ColorStateList(null, null));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/AutofixUnsafeVoidMethodReferenceJava.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.ClassVerificationFailureFromJava is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" view.setBackgroundTintList(tint);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/ClassVerificationFailureFromJava.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 23; however, the containing class androidx.ClassVerificationFailureFromJava is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return view.getAccessibilityClassName();"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -615,6 +516,42 @@
<issue
id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/sample/appcompat/widget/ActionBarBackgroundDrawable.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/AutofixUnsafeCallToThis.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/AutofixUnsafeCallToThis.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/AutofixUnsafeCallToThis.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 19"
errorLine1=" @RequiresApi(19)"
errorLine2=" ~~~~~~~~~~~~~~~~">
@@ -624,7 +561,43 @@
<issue
id="ObsoleteSdkInt"
- message="Unnecessary; SDK_INT is always >= 19"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/AutofixUnsafeReferenceWithExistingClassJava.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/AutofixUnsafeReferenceWithExistingFix.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/AutofixUnsafeReferenceWithExistingFix.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/AutofixUnsafeReferenceWithExistingFix.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
errorLine1=" if (Build.VERSION.SDK_INT >= 17) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -633,7 +606,16 @@
<issue
id="ObsoleteSdkInt"
- message="Unnecessary; SDK_INT is always >= 19"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/AutofixUnsafeVoidMethodReferenceJava.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
errorLine1=" if (Build.VERSION.SDK_INT >= 17) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -642,7 +624,7 @@
<issue
id="ObsoleteSdkInt"
- message="Unnecessary; SDK_INT is always >= 19"
+ message="Unnecessary; SDK_INT is always >= 21"
errorLine1=" if (Build.VERSION.SDK_INT >= 19) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -651,7 +633,7 @@
<issue
id="ObsoleteSdkInt"
- message="Unnecessary; SDK_INT is always >= 19"
+ message="Unnecessary; SDK_INT is always >= 21"
errorLine1=" if (Build.VERSION.SDK_INT >= 19) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -660,7 +642,7 @@
<issue
id="ObsoleteSdkInt"
- message="Unnecessary; SDK_INT is always >= 19"
+ message="Unnecessary; SDK_INT is always >= 21"
errorLine1=" if (Build.VERSION.SDK_INT >= 19) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -669,7 +651,7 @@
<issue
id="ObsoleteSdkInt"
- message="Unnecessary; SDK_INT is always >= 19"
+ message="Unnecessary; SDK_INT is always >= 21"
errorLine1=" return if (Build.VERSION.SDK_INT >= 19) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
diff --git a/lint/lint-gradle/src/main/java/androidx/lint/gradle/DiscouragedGradleMethodDetector.kt b/lint/lint-gradle/src/main/java/androidx/lint/gradle/DiscouragedGradleMethodDetector.kt
index a8f7b18..b944fbb 100644
--- a/lint/lint-gradle/src/main/java/androidx/lint/gradle/DiscouragedGradleMethodDetector.kt
+++ b/lint/lint-gradle/src/main/java/androidx/lint/gradle/DiscouragedGradleMethodDetector.kt
@@ -131,6 +131,8 @@
"findByPath" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
"findProperty" to
Replacement(PROJECT, "providers.gradleProperty", PROJECT_ISOLATION_ISSUE),
+ "property" to
+ Replacement(PROJECT, "providers.gradleProperty", PROJECT_ISOLATION_ISSUE),
"iterator" to Replacement(TASK_CONTAINER, null, EAGER_CONFIGURATION_ISSUE),
"get" to Replacement(TASK_PROVIDER, null, EAGER_CONFIGURATION_ISSUE),
"getAt" to Replacement(TASK_COLLECTION, "named", EAGER_CONFIGURATION_ISSUE),
diff --git a/loader/loader-ktx/build.gradle b/loader/loader-ktx/build.gradle
index 6472e3a..c700ba7 100644
--- a/loader/loader-ktx/build.gradle
+++ b/loader/loader-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -49,7 +49,7 @@
androidx {
name = "Loader Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Kotlin extensions for 'loader' artifact"
metalavaK2UastEnabled = true
diff --git a/media/media/lint-baseline.xml b/media/media/lint-baseline.xml
index 5604b10..68be819 100644
--- a/media/media/lint-baseline.xml
+++ b/media/media/lint-baseline.xml
@@ -1,5 +1,606 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/AudioAttributesCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/AudioAttributesCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/AudioAttributesCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/AudioAttributesImplApi21.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/AudioAttributesImplApi21.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/AudioFocusRequestCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/AudioManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/AudioManagerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (itemObj == null || Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (itemList == null || Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaBrowserCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/MediaBrowserServiceCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaControllerCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (mDescriptionFwk != null || Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (descriptionObj != null && Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaDescriptionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (metadataObj != null && Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaMetadataCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (mMetadataFwk == null && Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/MediaMetadataCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21 || context == null || mediaSession == null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (token != null && android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (mItemFwk != null || android.os.Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (queueItem == null || Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (itemList == null || Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/MediaSessionCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/MediaSessionManager.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/MediaSessionManagerImplApi21.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/app/NotificationCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (stateObj != null && Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (mStateFwk == null && Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (customActionObj == null || Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (mCustomActionFwk != null || Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/android/support/v4/media/session/PlaybackStateCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/VolumeProviderCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/VolumeProviderCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/media/VolumeProviderCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="src/main/res/values-v21"/>
+ </issue>
<issue
id="LambdaLast"
diff --git a/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java b/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
index d3653f5..1a4f88e 100644
--- a/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
@@ -393,8 +393,9 @@
bob.setIconBitmap(Api21Impl.getIconBitmap(description));
bob.setIconUri(Api21Impl.getIconUri(description));
Bundle extras = Api21Impl.getExtras(description);
+ extras = MediaSessionCompat.unparcelWithClassLoader(extras);
if (extras != null) {
- extras = new Bundle(MediaSessionCompat.unparcelWithClassLoader(extras));
+ extras = new Bundle(extras);
}
Uri mediaUri = null;
if (extras != null) {
diff --git a/mediarouter/mediarouter/lint-baseline.xml b/mediarouter/mediarouter/lint-baseline.xml
index d338482..a74d5b0 100644
--- a/mediarouter/mediarouter/lint-baseline.xml
+++ b/mediarouter/mediarouter/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -46,4 +46,58 @@
file="src/main/java/androidx/mediarouter/media/RemotePlaybackClient.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/app/DeviceUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/app/MediaRouteControllerDialog.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/app/MediaRouteControllerDialog.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/mediarouter/app/MediaRouteControllerDialog.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (what == CLIENT_MSG_REGISTER && Build.VERSION.SDK_INT"
+ errorLine2=" ^">
+ <location
+ file="src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java"/>
+ </issue>
+
</issues>
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java
index 68bdfd0..bf75d84 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java
@@ -1065,8 +1065,10 @@
route.getProviderInstance()
.onCreateRouteController(
route.mDescriptorId, mSelectedRoute.mDescriptorId);
- controller.onSelect();
- mRouteControllerMap.put(route.mUniqueId, controller);
+ if (controller != null) {
+ controller.onSelect();
+ mRouteControllerMap.put(route.mUniqueId, controller);
+ }
}
}
}
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemotePlaybackClient.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemotePlaybackClient.java
index 51fbfe1..19045af 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemotePlaybackClient.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemotePlaybackClient.java
@@ -62,6 +62,7 @@
/**
* Creates a remote playback client for a route.
*
+ * @param context The {@link Context}.
* @param route The media route.
*/
public RemotePlaybackClient(@NonNull Context context, @NonNull MediaRouter.RouteInfo route) {
diff --git a/navigation/integration-tests/testapp/lint-baseline.xml b/navigation/integration-tests/testapp/lint-baseline.xml
index 5c97efc..b1b8eb9 100644
--- a/navigation/integration-tests/testapp/lint-baseline.xml
+++ b/navigation/integration-tests/testapp/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha04" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha04)" variant="all" version="8.3.0-alpha04">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="ClassVerificationFailure"
@@ -20,19 +20,10 @@
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.navigation.testapp.HelpActivity is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" window.exitTransition = fade"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/navigation/testapp/HelpActivity.kt"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.navigation.testapp.HelpActivity is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" window.enterTransition = fade"
- errorLine2=" ~~~~~~~~~~~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/navigation/testapp/HelpActivity.kt"/>
</issue>
diff --git a/navigation/navigation-common-ktx/build.gradle b/navigation/navigation-common-ktx/build.gradle
index b78112e..142ea22 100644
--- a/navigation/navigation-common-ktx/build.gradle
+++ b/navigation/navigation-common-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode
plugins {
@@ -40,7 +40,7 @@
androidx {
name = "Navigation Common Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Android Navigation-Common-Ktx"
metalavaK2UastEnabled = true
diff --git a/navigation/navigation-common/api/current.txt b/navigation/navigation-common/api/current.txt
index 099e27d..d8aade0e 100644
--- a/navigation/navigation-common/api/current.txt
+++ b/navigation/navigation-common/api/current.txt
@@ -227,6 +227,7 @@
method public final String? getRoute();
method public boolean hasDeepLink(android.net.Uri deepLink);
method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+ method public static final <T> boolean hasRoute(androidx.navigation.NavDestination, kotlin.reflect.KClass<T> route);
method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
method protected static final <C> Class<? extends C?> parseClassFromName(android.content.Context context, String name, Class<? extends C?> expectedClassType);
method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
@@ -253,6 +254,7 @@
public static final class NavDestination.Companion {
method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
method public inline <reified T> boolean hasRoute(androidx.navigation.NavDestination);
+ method public <T> boolean hasRoute(androidx.navigation.NavDestination, kotlin.reflect.KClass<T> route);
method protected <C> Class<? extends C?> parseClassFromName(android.content.Context context, String name, Class<? extends C?> expectedClassType);
}
diff --git a/navigation/navigation-common/api/restricted_current.txt b/navigation/navigation-common/api/restricted_current.txt
index 099e27d..d8aade0e 100644
--- a/navigation/navigation-common/api/restricted_current.txt
+++ b/navigation/navigation-common/api/restricted_current.txt
@@ -227,6 +227,7 @@
method public final String? getRoute();
method public boolean hasDeepLink(android.net.Uri deepLink);
method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+ method public static final <T> boolean hasRoute(androidx.navigation.NavDestination, kotlin.reflect.KClass<T> route);
method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
method protected static final <C> Class<? extends C?> parseClassFromName(android.content.Context context, String name, Class<? extends C?> expectedClassType);
method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
@@ -253,6 +254,7 @@
public static final class NavDestination.Companion {
method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
method public inline <reified T> boolean hasRoute(androidx.navigation.NavDestination);
+ method public <T> boolean hasRoute(androidx.navigation.NavDestination, kotlin.reflect.KClass<T> route);
method protected <C> Class<? extends C?> parseClassFromName(android.content.Context context, String name, Class<? extends C?> expectedClassType);
}
diff --git a/navigation/navigation-common/lint-baseline.xml b/navigation/navigation-common/lint-baseline.xml
deleted file mode 100644
index f57c19e..0000000
--- a/navigation/navigation-common/lint-baseline.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha04" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha04)" variant="all" version="8.3.0-alpha04">
-
- <issue
- id="NewApi"
- message="Call requires API level 21 (current min is 19): `android.os.BaseBundle#get`"
- errorLine1=" if (!isNullable && bundle.containsKey(name) && bundle[name] == null) {"
- errorLine2=" ~~">
- <location
- file="src/main/java/androidx/navigation/NavArgument.kt"/>
- </issue>
-
-</issues>
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavDestination.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavDestination.kt
index f1515cd..468e617 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavDestination.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavDestination.kt
@@ -31,6 +31,7 @@
import androidx.navigation.common.R
import java.util.regex.Pattern
import kotlin.reflect.KClass
+import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.serializer
/**
@@ -828,7 +829,18 @@
* @param T the route from KClass
*/
@JvmStatic
- public inline fun <reified T : Any> NavDestination.hasRoute() =
- serializer<T>().hashCode() == id
+ public inline fun <reified T : Any> NavDestination.hasRoute() = hasRoute(T::class)
+
+ /**
+ * Checks if the NavDestination's route was generated from [T]
+ *
+ * Returns true if equal, false otherwise.
+ *
+ * @param route the route from KClass
+ */
+ @OptIn(InternalSerializationApi::class)
+ @JvmStatic
+ public fun <T : Any> NavDestination.hasRoute(route: KClass<T>) =
+ route.serializer().hashCode() == id
}
}
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavGraph.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavGraph.kt
index 8a849eb..0eabbd1 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavGraph.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavGraph.kt
@@ -232,6 +232,24 @@
?: if (searchParents && parent != null) parent!!.findNode(route) else null
}
+ // searches through child nodes, does not search through parents
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public fun findChildNode(
+ @IdRes resId: Int,
+ ): NavDestination? {
+ // first search through children directly added to graph
+ var destination = nodes[resId]
+ if (destination != null) return destination
+
+ // then search through child graphs
+ destination = nodes.valueIterator().asSequence().firstNotNullOfOrNull { child ->
+ if (child is NavGraph) {
+ child.findChildNode(resId)
+ } else null
+ }
+ return destination
+ }
+
/**
* @throws NoSuchElementException if there no more elements
*/
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/serialization/RouteEncoder.kt b/navigation/navigation-common/src/main/java/androidx/navigation/serialization/RouteEncoder.kt
index 719dab4..417450f 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/serialization/RouteEncoder.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/serialization/RouteEncoder.kt
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-
package androidx.navigation.serialization
-import androidx.annotation.RestrictTo
import androidx.navigation.NavType
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
@@ -36,8 +33,7 @@
private val serializer: KSerializer<T>,
private val typeMap: Map<String, NavType<Any?>>
) : AbstractEncoder() {
- @Suppress("DEPRECATION") // deprecated in 1.6.3
- override val serializersModule: SerializersModule = EmptySerializersModule
+ override val serializersModule: SerializersModule = EmptySerializersModule()
private val builder = RouteBuilder.Filled(serializer, typeMap)
/**
diff --git a/navigation/navigation-compose/build.gradle b/navigation/navigation-compose/build.gradle
index 12c8132..1dfe8fc 100644
--- a/navigation/navigation-compose/build.gradle
+++ b/navigation/navigation-compose/build.gradle
@@ -62,7 +62,7 @@
androidx {
name = "Compose Navigation"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose integration with Navigation"
legacyDisableKotlinStrictApiMode = true
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle b/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle
index 140b0aa..03a6aca 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle
@@ -28,10 +28,12 @@
id("com.android.library")
id("AndroidXComposePlugin")
id("org.jetbrains.kotlin.android")
+ alias(libs.plugins.kotlinSerialization)
}
dependencies {
implementation(libs.kotlinStdlib)
+ implementation(libs.kotlinSerializationCore)
implementation(project(":compose:integration-tests:demos:common"))
implementation(project(":compose:foundation:foundation"))
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt
index cc5f036..d2a4ef6 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/BottomBarNavDemo.kt
@@ -27,15 +27,18 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.dialog
import androidx.navigation.compose.rememberNavController
import androidx.navigation.compose.samples.Dashboard
+import androidx.navigation.compose.samples.Dialog
+import androidx.navigation.compose.samples.DialogContent
import androidx.navigation.compose.samples.Profile
-import androidx.navigation.compose.samples.Screen
import androidx.navigation.compose.samples.Scrollable
@Composable
@@ -43,9 +46,9 @@
val navController = rememberNavController()
val items = listOf(
- stringResource(R.string.profile) to Screen.Profile.route,
- stringResource(R.string.dashboard) to Screen.Dashboard.route,
- stringResource(R.string.scrollable) to Screen.Scrollable.route
+ stringResource(R.string.profile) to Profile,
+ stringResource(R.string.dashboard) to Dashboard(),
+ stringResource(R.string.scrollable) to Scrollable
)
Scaffold(
@@ -53,13 +56,15 @@
BottomNavigation {
val navBackStackEntry = navController.currentBackStackEntryAsState().value
val currentDestination = navBackStackEntry?.destination
- items.forEach { (name, route) ->
+ items.forEach { (name, destination) ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(name) },
- selected = currentDestination?.hierarchy?.any { it.route == route } == true,
+ selected = currentDestination?.hierarchy?.any {
+ it.hasRoute(destination::class)
+ } == true,
onClick = {
- navController.navigate(route) {
+ navController.navigate(destination) {
launchSingleTop = true
restoreState = true
popUpTo(navController.graph.findStartDestination().id) {
@@ -72,10 +77,11 @@
}
}
) { innerPadding ->
- NavHost(navController, Screen.Profile.route, Modifier.padding(innerPadding)) {
- composable(Screen.Profile.route) { Profile(navController) }
- composable(Screen.Dashboard.route) { Dashboard(navController) }
- composable(Screen.Scrollable.route) { Scrollable(navController) }
+ NavHost(navController, Profile, Modifier.padding(innerPadding)) {
+ composable<Profile> { Profile(navController) }
+ composable<Dashboard> { Dashboard(navController) }
+ composable<Scrollable> { Scrollable(navController) }
+ dialog<Dialog> { DialogContent(navController) }
}
}
}
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
index 9951b1d..c715ee7 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavByDeepLinkDemo.kt
@@ -39,23 +39,27 @@
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.compose.samples.Dashboard
-import androidx.navigation.compose.samples.Screen
-import androidx.navigation.navArgument
+import androidx.navigation.compose.samples.Profile
import androidx.navigation.navDeepLink
+import androidx.navigation.toRoute
@Composable
-@Suppress("DEPRECATION")
fun NavByDeepLinkDemo() {
val navController = rememberNavController()
- val uri = "https://example.com/dashboard?userId="
- NavHost(navController, startDestination = Screen.Profile.route) {
- composable(Screen.Profile.route) { ProfileWithDeepLink(navController, uri) }
- composable(
- Screen.Dashboard.route,
- arguments = listOf(navArgument("userId") { defaultValue = "no value given" }),
- deepLinks = listOf(navDeepLink { uriPattern = "$uri{userId}" })
+ val basePath = "https://example.com"
+ NavHost(navController, startDestination = Profile) {
+ composable<Profile> {
+ ProfileWithDeepLink(
+ navController,
+ "$basePath?userId="
+ )
+ }
+ composable<Dashboard>(
+ // use the same args from Destination.Dashboard with custom uri base path
+ deepLinks = listOf(navDeepLink<Dashboard>(basePath))
) { backStackEntry ->
- Dashboard(navController, backStackEntry.arguments?.get("userId") as? String)
+ val dashboard = backStackEntry.toRoute<Dashboard>()
+ Dashboard(navController, dashboard.userId)
}
}
}
@@ -63,7 +67,7 @@
@Composable
fun ProfileWithDeepLink(navController: NavController, uri: String) {
Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
- Text(text = stringResource(Screen.Profile.resourceId))
+ Text(text = stringResource(Profile.resourceId))
Divider(color = Color.Black)
val state = rememberSaveable { mutableStateOf("") }
Box {
@@ -75,6 +79,7 @@
}
Divider(color = Color.Black)
Button(
+ // navigate with deeplink
onClick = { navController.navigate(Uri.parse(uri + state.value)) },
colors = ButtonDefaults.buttonColors(backgroundColor = Color.LightGray),
modifier = Modifier.fillMaxWidth()
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavPopUpToDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavPopUpToDemo.kt
index a39e8ab..eed030c 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavPopUpToDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavPopUpToDemo.kt
@@ -31,16 +31,19 @@
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
+import androidx.navigation.toRoute
+import kotlinx.serialization.Serializable
+
+@Serializable
+class NumberedDestination(val number: Int)
@Composable
fun NavPopUpToDemo() {
val navController = rememberNavController()
- NavHost(navController, startDestination = "1") {
- composable("1") { NumberedScreen(navController, 1) }
- composable("2") { NumberedScreen(navController, 2) }
- composable("3") { NumberedScreen(navController, 3) }
- composable("4") { NumberedScreen(navController, 4) }
- composable("5") { NumberedScreen(navController, 5) }
+ NavHost(navController, startDestination = NumberedDestination(1)) {
+ composable<NumberedDestination> {
+ NumberedScreen(navController, it.toRoute<NumberedDestination>().number)
+ }
}
}
@@ -50,7 +53,7 @@
val next = number + 1
if (number < 5) {
Button(
- onClick = { navController.navigate("$next") },
+ onClick = { navController.navigate(NumberedDestination(next)) },
colors = ButtonDefaults.buttonColors(backgroundColor = Color.LightGray),
modifier = Modifier.fillMaxWidth()
) {
@@ -59,8 +62,11 @@
}
Text("This is screen $number", Modifier.weight(1f))
if (navController.previousBackStackEntry != null) {
+ val firstScreen = NumberedDestination(1)
Button(
- onClick = { navController.navigate("1") { popUpTo("1") { inclusive = true } } },
+ onClick = { navController.navigate(firstScreen) {
+ popUpTo(firstScreen) { inclusive = true }
+ } },
colors = ButtonDefaults.buttonColors(backgroundColor = Color.LightGray),
modifier = Modifier.fillMaxWidth()
) {
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavSingleTopDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavSingleTopDemo.kt
index 051d40b..6250ce4 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavSingleTopDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavSingleTopDemo.kt
@@ -32,6 +32,8 @@
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.compose.samples.NavigateButton
+import androidx.navigation.toRoute
+import kotlinx.serialization.Serializable
@Composable
fun NavSingleTopDemo() {
@@ -44,21 +46,28 @@
placeholder = { Text("Search") }
)
NavigateButton("Search") {
- navController.navigate("search/" + query.value) {
+ navController.navigate(SearchScreen(query.value)) {
launchSingleTop = true
}
}
- NavHost(navController, startDestination = "start") {
- composable("start") { StartScreen() }
- composable("search/{query}") { backStackEntry ->
+ NavHost(navController, startDestination = StartScreen::class) {
+ composable<StartScreen> { StartScreen() }
+ composable<SearchScreen> { backStackEntry ->
+ val args = backStackEntry.toRoute<SearchScreen>()
SearchResultScreen(
- backStackEntry.arguments!!.getString("query", "no query entered")
+ args.query
)
}
}
}
}
+@Serializable
+object StartScreen
+
+@Serializable
+data class SearchScreen(val query: String)
+
@Composable
fun StartScreen() {
Divider(color = Color.Black)
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsDemo.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsDemo.kt
index 3b6d9f1..289eab6 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsDemo.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsDemo.kt
@@ -16,56 +16,23 @@
package androidx.navigation.compose.demos
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material.Divider
-import androidx.compose.material.Text
-import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.compose.samples.Dashboard
-import androidx.navigation.compose.samples.NavigateButton
-import androidx.navigation.compose.samples.Screen
+import androidx.navigation.compose.samples.Profile
+import androidx.navigation.compose.samples.ProfileWithArgs
+import androidx.navigation.toRoute
@Composable
-@Suppress("DEPRECATION")
fun NavWithArgsDemo() {
val navController = rememberNavController()
- NavHost(navController, startDestination = Screen.Profile.route) {
- composable(Screen.Profile.route) { ProfileWithArgs(navController) }
- composable(Screen.Dashboard.route + "?userId={userId}") { backStackEntry ->
- Dashboard(navController, backStackEntry.arguments?.get("userId") as? String)
- }
- }
-}
-
-@Composable
-fun ProfileWithArgs(navController: NavController) {
- Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
- Text(text = stringResource(Screen.Profile.resourceId))
- Divider(color = Color.Black)
- val state = rememberSaveable { mutableStateOf("") }
- Box {
- TextField(
- value = state.value,
- onValueChange = { state.value = it },
- placeholder = { Text("Enter userId here") }
- )
- }
- Divider(color = Color.Black)
- NavigateButton("Dashboard with userId") {
- navController.navigate(Screen.Dashboard.route + "?userId=" + state.value)
+ NavHost(navController, startDestination = Profile) {
+ composable<Profile> { ProfileWithArgs(navController) }
+ composable<Dashboard> { backStackEntry ->
+ val dashboard = backStackEntry.toRoute<Dashboard>()
+ Dashboard(navController, dashboard.userId)
}
}
}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/EmptyFragment.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsInNestedGraphDemo.kt
similarity index 71%
copy from privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/EmptyFragment.kt
copy to navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsInNestedGraphDemo.kt
index 3a27fde..fe91ed6 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/EmptyFragment.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavWithArgsInNestedGraphDemo.kt
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package androidx.privacysandbox.ui.integration.testapp
+package androidx.navigation.compose.demos
-class EmptyFragment : BaseFragment() {
- override fun handleDrawerStateChange(isDrawerOpen: Boolean) {
- }
+import androidx.compose.runtime.Composable
+import androidx.navigation.compose.samples.NavWithArgsInNestedGraph
+
+@Composable
+fun NavWithArgsInNestedGraphDemo() {
+ NavWithArgsInNestedGraph()
}
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt
index 113d02d..6e11a87 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/src/main/java/androidx/navigation/compose/demos/NavigationDemos.kt
@@ -27,6 +27,7 @@
ComposableDemo("Nested Nav In Graph Demo") { NestNavInGraphDemo() },
ComposableDemo("Bottom Bar Nav Demo") { BottomBarNavDemo() },
ComposableDemo("Navigation with Args") { NavWithArgsDemo() },
+ ComposableDemo("Navigation with Args in Nested Graph") { NavWithArgsInNestedGraphDemo() },
ComposableDemo("Navigation by DeepLink") { NavByDeepLinkDemo() },
ComposableDemo("Navigation PopUpTo") { NavPopUpToDemo() },
ComposableDemo("Navigation SingleTop") { NavSingleTopDemo() },
diff --git a/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/NavigationSamples.kt b/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/NavigationSamples.kt
index d2b50f3e..02acffa 100644
--- a/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/NavigationSamples.kt
+++ b/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/NavigationSamples.kt
@@ -19,7 +19,6 @@
import android.os.Bundle
import android.os.Parcelable
import androidx.annotation.Sampled
-import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -36,8 +35,11 @@
import androidx.compose.material.Divider
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
+import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color.Companion.LightGray
@@ -49,6 +51,7 @@
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.navigation.NavController
+import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
@@ -56,28 +59,44 @@
import androidx.navigation.compose.dialog
import androidx.navigation.compose.navigation
import androidx.navigation.compose.rememberNavController
-import androidx.navigation.navArgument
+import androidx.navigation.toRoute
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
-sealed class Screen(val route: String, @StringRes val resourceId: Int) {
- object Profile : Screen("profile", R.string.profile)
- object Dashboard : Screen("dashboard", R.string.dashboard)
- object Scrollable : Screen("scrollable", R.string.scrollable)
- object Dialog : Screen("dialog", R.string.dialog)
+@Serializable object Profile {
+ val resourceId: Int = R.string.profile
}
+@Serializable object Scrollable {
+ val resourceId: Int = R.string.scrollable
+}
+
+@Serializable object Dialog {
+ val resourceId: Int = R.string.dialog
+}
+
+@Serializable data class Dashboard(val userId: String? = "no value given") {
+ companion object {
+ val resourceId: Int = R.string.dashboard
+ }
+}
+
+@Serializable
+object Nested
+
+@Serializable
+data class NestedWithArg(val userId: String? = "default nested arg")
+
@Composable
fun BasicNav() {
val navController = rememberNavController()
- NavHost(navController, startDestination = Screen.Profile.route) {
- composable(Screen.Profile.route) { Profile(navController) }
- composable(
- Screen.Dashboard.route,
+ NavHost(navController, startDestination = Profile) {
+ composable<Profile> { Profile(navController) }
+ composable<Dashboard>(
enterTransition = {
- if (initialState.destination.route == Screen.Scrollable.route) {
+ if (initialState.destination.hasRoute<Scrollable>()) {
// Slide in when entering from Scrollable
slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start)
} else {
@@ -85,7 +104,7 @@
}
},
popExitTransition = {
- if (targetState.destination.route == Screen.Scrollable.route) {
+ if (targetState.destination.hasRoute<Scrollable>()) {
// Slide out when popping back to Scrollable
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End)
} else {
@@ -95,10 +114,9 @@
) {
Dashboard(navController)
}
- composable(
- Screen.Scrollable.route,
+ composable<Scrollable>(
exitTransition = {
- if (targetState.destination.route == Screen.Dashboard.route) {
+ if (targetState.destination.hasRoute<Dashboard>()) {
// Slide out when navigating to Dashboard
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start)
} else {
@@ -106,7 +124,7 @@
}
},
popEnterTransition = {
- if (initialState.destination.route == Screen.Dashboard.route) {
+ if (initialState.destination.hasRoute<Dashboard>()) {
// Slide back in when returning from Dashboard
slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End)
} else {
@@ -116,33 +134,33 @@
) {
Scrollable(navController)
}
- dialog(Screen.Dialog.route) { DialogContent(navController) }
+ dialog<Dialog> { DialogContent(navController) }
}
}
@Composable
fun NestedNavStartDestination() {
val navController = rememberNavController()
- NavHost(navController, startDestination = "nested") {
- navigation(startDestination = Screen.Profile.route, route = "nested") {
- composable(Screen.Profile.route) { Profile(navController) }
+ NavHost(navController, startDestination = Nested) {
+ navigation<Nested>(startDestination = Profile) {
+ composable<Profile> { Profile(navController) }
}
- composable(Screen.Dashboard.route) { Dashboard(navController) }
- composable(Screen.Scrollable.route) { Scrollable(navController) }
- dialog(Screen.Dialog.route) { DialogContent(navController) }
+ composable<Dashboard> { Dashboard(navController) }
+ composable<Scrollable> { Scrollable(navController) }
+ dialog<Dialog> { DialogContent(navController) }
}
}
@Composable
fun NestedNavInGraph() {
val navController = rememberNavController()
- NavHost(navController, startDestination = Screen.Profile.route) {
- composable(Screen.Profile.route) { Profile(navController) }
- navigation(startDestination = "nested", route = Screen.Dashboard.route) {
- composable("nested") { Dashboard(navController) }
+ NavHost(navController, startDestination = Profile) {
+ composable<Profile> { Profile(navController) }
+ navigation<Dashboard>(startDestination = Nested) {
+ composable<Nested> { Dashboard(navController) }
}
- composable(Screen.Scrollable.route) { Scrollable(navController) }
- dialog(Screen.Dialog.route) { DialogContent(navController) }
+ composable<Scrollable> { Scrollable(navController) }
+ dialog<Dialog> { DialogContent(navController) }
}
}
@@ -151,49 +169,27 @@
fun NavScaffold() {
val navController = rememberNavController()
Scaffold { innerPadding ->
- NavHost(navController, Screen.Profile.route, Modifier.padding(innerPadding)) {
- composable(Screen.Profile.route) { Profile(navController) }
- composable(Screen.Dashboard.route) { Dashboard(navController) }
- composable(Screen.Scrollable.route) { Scrollable(navController) }
- dialog(Screen.Dialog.route) { DialogContent(navController) }
- }
- }
-}
-
-@Composable
-fun NavWithArgs() {
- val navController = rememberNavController()
- NavHost(navController, startDestination = Screen.Profile.route) {
- composable(Screen.Profile.route) { Profile(navController) }
- composable(
- Screen.Dashboard.route,
- arguments = listOf(navArgument("userId") { defaultValue = "no value given" })
- ) { backStackEntry ->
- Dashboard(navController, backStackEntry.arguments?.getString("userId"))
+ NavHost(navController, Profile, Modifier.padding(innerPadding)) {
+ composable<Profile> { Profile(navController) }
+ composable<Dashboard> { Dashboard(navController) }
+ composable<Scrollable> { Scrollable(navController) }
+ dialog<Dialog> { DialogContent(navController) }
}
}
}
@Sampled
@Composable
-fun NestedNavInGraphWithArgs() {
+fun NavWithArgsInNestedGraph() {
val navController = rememberNavController()
- NavHost(navController, startDestination = Screen.Profile.route) {
- composable(Screen.Profile.route) { Profile(navController) }
- navigation(
- startDestination = "nested",
- route = Screen.Dashboard.route,
- // This value will be sent to the start destination of the graph when you navigate to
- // this graph
- arguments = listOf(navArgument("userId") { defaultValue = "no value given" })
- ) {
- composable(
- "nested",
- // We don't need to set a default value here because the start destination will
- // automatically receive the arguments of its parent graph
- arguments = listOf(navArgument("userId") { })
- ) {
- Dashboard(navController)
+ NavHost(navController, startDestination = Profile) {
+ composable<Profile> { ProfileWithArgs(navController) }
+ navigation<Dashboard>(startDestination = NestedWithArg::class) {
+ composable<NestedWithArg> {
+ // argument from parent graph Destination.Dashboard will automatically be
+ // bundled into the start destination's arguments
+ val userId = it.toRoute<NestedWithArg>().userId
+ Dashboard(navController, userId)
}
}
}
@@ -202,17 +198,17 @@
@Composable
fun Profile(navController: NavHostController) {
Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
- Text(text = stringResource(Screen.Profile.resourceId))
- NavigateButton(stringResource(Screen.Dashboard.resourceId)) {
- navController.navigate(Screen.Dashboard.route)
+ Text(text = stringResource(Profile.resourceId))
+ NavigateButton(stringResource(Dashboard.resourceId)) {
+ navController.navigate(Dashboard())
}
Divider(color = Color.Black)
- NavigateButton(stringResource(Screen.Scrollable.resourceId)) {
- navController.navigate(Screen.Scrollable.route)
+ NavigateButton(stringResource(Scrollable.resourceId)) {
+ navController.navigate(Scrollable)
}
Divider(color = Color.Black)
- NavigateButton(stringResource(Screen.Dialog.resourceId)) {
- navController.navigate(Screen.Dialog.route)
+ NavigateButton(stringResource(Dialog.resourceId)) {
+ navController.navigate(Dialog)
}
Spacer(Modifier.weight(1f))
NavigateBackButton(navController)
@@ -220,9 +216,29 @@
}
@Composable
+fun ProfileWithArgs(navController: NavController) {
+ Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
+ Text(text = stringResource(Profile.resourceId))
+ Divider(color = Color.Black)
+ val state = rememberSaveable { mutableStateOf("") }
+ Box {
+ TextField(
+ value = state.value,
+ onValueChange = { state.value = it },
+ placeholder = { Text("Enter userId here") }
+ )
+ }
+ Divider(color = Color.Black)
+ NavigateButton("Dashboard with userId") {
+ navController.navigate(Dashboard(state.value))
+ }
+ }
+}
+
+@Composable
fun Dashboard(navController: NavController, title: String? = null) {
Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
- Text(text = title ?: stringResource(Screen.Dashboard.resourceId))
+ Text(text = title ?: stringResource(Dashboard.resourceId))
Spacer(Modifier.weight(1f))
NavigateBackButton(navController)
}
@@ -231,8 +247,8 @@
@Composable
fun Scrollable(navController: NavController) {
Column(Modifier.fillMaxSize().then(Modifier.padding(8.dp))) {
- NavigateButton(stringResource(Screen.Dashboard.resourceId)) {
- navController.navigate(Screen.Dashboard.route)
+ NavigateButton(stringResource(Dashboard.resourceId)) {
+ navController.navigate(Dashboard())
}
LazyColumn(modifier = Modifier.weight(1f)) {
items(phrases) { phrase ->
diff --git a/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/SizeTransformSample.kt b/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/SizeTransformSample.kt
index ce55fdb..a8779ce 100644
--- a/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/SizeTransformSample.kt
+++ b/navigation/navigation-compose/samples/src/main/java/androidx/navigation/compose/samples/SizeTransformSample.kt
@@ -33,14 +33,15 @@
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
+import kotlinx.serialization.Serializable
@Sampled
@Composable
fun SizeTransformNav() {
val navController = rememberNavController()
Box {
- NavHost(navController, startDestination = "collapsed") {
- composable("collapsed",
+ NavHost(navController, startDestination = Collapsed) {
+ composable<Collapsed>(
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None },
sizeTransform = {
@@ -52,9 +53,9 @@
}
}
}) {
- CollapsedScreen { navController.navigate("expanded") }
+ CollapsedScreen { navController.navigate(Expanded) }
}
- composable("expanded",
+ composable<Expanded>(
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None },
sizeTransform = {
@@ -71,6 +72,12 @@
}
}
+@Serializable
+object Collapsed
+
+@Serializable
+object Expanded
+
@Composable
fun CollapsedScreen(onNavigate: () -> Unit) {
Box(Modifier.clickable { onNavigate() }.size(40.dp).background(Green))
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt
index 6c75048..fb4e5d5 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavGraphBuilder.kt
@@ -223,7 +223,7 @@
/**
* Construct a nested [NavGraph]
*
- * @sample androidx.navigation.compose.samples.NestedNavInGraphWithArgs
+ * @sample androidx.navigation.compose.samples.NavWithArgsInNestedGraph
*
* @param startDestination the starting destination's route for this NavGraph
* @param route the destination's unique route
diff --git a/navigation/navigation-fragment-compose/build.gradle b/navigation/navigation-fragment-compose/build.gradle
index d1ea6149..24d4568 100644
--- a/navigation/navigation-fragment-compose/build.gradle
+++ b/navigation/navigation-fragment-compose/build.gradle
@@ -51,7 +51,7 @@
androidx {
name = "Navigation with Fragments with Compose"
- type = LibraryType.PUBLISHED_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2024"
description = "Add Compose destinations to Navigation with Fragments"
}
diff --git a/navigation/navigation-fragment-ktx/build.gradle b/navigation/navigation-fragment-ktx/build.gradle
index 84b6d20..f38f638 100644
--- a/navigation/navigation-fragment-ktx/build.gradle
+++ b/navigation/navigation-fragment-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -38,7 +38,7 @@
androidx {
name = "Navigation Fragment Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Android Navigation-Fragment-Ktx"
metalavaK2UastEnabled = true
diff --git a/navigation/navigation-runtime-ktx/build.gradle b/navigation/navigation-runtime-ktx/build.gradle
index 93fa350..36897df 100644
--- a/navigation/navigation-runtime-ktx/build.gradle
+++ b/navigation/navigation-runtime-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -38,7 +38,7 @@
androidx {
name = "Navigation Runtime Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Android Navigation-Runtime-Ktx"
metalavaK2UastEnabled = true
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
index 89b8bed..a308c35 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
@@ -1484,6 +1484,79 @@
@UiThreadTest
@Test
+ fun testGetBackStackEntryWithKClassNested() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ // a sibling graph with nested KClass destination
+ navigation<TestGraph>(startDestination = TestClass::class) {
+ test<TestClass>()
+ }
+ test("second")
+ }
+
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo(
+ TEST_CLASS_ROUTE
+ )
+
+ navController.navigate("second")
+
+ assertThat(navigator.backStack.size).isEqualTo(3)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo("second")
+
+ val entry = navController.getBackStackEntry<TestClass>()
+ assertThat(entry.destination.route).isEqualTo(TEST_CLASS_ROUTE)
+ }
+
+ @UiThreadTest
+ @Test
+ fun testGetBackStackEntryWithKClassNotInGraph() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.getBackStackEntry<TestClassPathArg>()
+ }
+ assertThat(exception.message).isEqualTo(
+ "Destination with route TestClassPathArg cannot be found in " +
+ "navigation graph ${navController.graph}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
+ fun testGetBackStackEntryWithKClassNotInBackstack() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(1)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.getBackStackEntry<TestClass>()
+ }
+ assertThat(exception.message).isEqualTo(
+ "No destination with route TestClass is on the NavController's " +
+ "back stack. The current destination is ${navController.currentDestination}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
fun testGetBackStackEntryWithObject() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
@@ -1561,6 +1634,79 @@
@UiThreadTest
@Test
+ fun testGetBackStackEntryWithObjectNested() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ // a sibling graph with nested KClass destination
+ navigation<TestGraph>(startDestination = TestClassPathArg::class) {
+ test<TestClassPathArg>()
+ }
+ test("second")
+ }
+
+ navController.navigate(TestClassPathArg(1))
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo(
+ TEST_CLASS_PATH_ARG_ROUTE
+ )
+
+ navController.navigate("second")
+
+ assertThat(navigator.backStack.size).isEqualTo(3)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo("second")
+
+ val entry = navController.getBackStackEntry(TestClassPathArg(1))
+ assertThat(entry.destination.route).isEqualTo(TEST_CLASS_PATH_ARG_ROUTE)
+ }
+
+ @UiThreadTest
+ @Test
+ fun testGetBackStackEntryWithObjectNotInGraph() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.getBackStackEntry(TestClassPathArg(1))
+ }
+ assertThat(exception.message).isEqualTo(
+ "Destination with route TestClassPathArg cannot be found in " +
+ "navigation graph ${navController.graph}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
+ fun testGetBackStackEntryWithObjectNotInBackstack() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(1)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.getBackStackEntry(TestClass())
+ }
+ assertThat(exception.message).isEqualTo(
+ "No destination with route $TEST_CLASS_ROUTE is on the NavController's " +
+ "back stack. The current destination is ${navController.currentDestination}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
fun testPopBackStack() {
val navController = createNavController()
navController.graph = nav_singleArg_graph
@@ -1778,19 +1924,14 @@
test("start")
test<TestClass>()
}
-
- // first nav
- navController.navigate("start")
-
- // second nav
- navController.navigate(TEST_CLASS_ROUTE)
+ navController.navigate(TestClass())
val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
- assertThat(navigator.backStack.size).isEqualTo(3)
+ assertThat(navigator.backStack.size).isEqualTo(2)
val popped = navController.popBackStack<TestClass>(true)
assertThat(popped).isTrue()
- assertThat(navigator.backStack.size).isEqualTo(2)
+ assertThat(navigator.backStack.size).isEqualTo(1)
}
@UiThreadTest
@@ -1818,6 +1959,37 @@
@UiThreadTest
@Test
+ fun testPopBackStackWithKClassNested() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ // a sibling graph with nested KClass destination
+ navigation<TestGraph>(startDestination = TestClass::class) {
+ test<TestClass>()
+ }
+ test("second")
+ }
+
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo(
+ TEST_CLASS_ROUTE
+ )
+
+ navController.navigate("second")
+
+ assertThat(navigator.backStack.size).isEqualTo(3)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo("second")
+
+ val popped = navController.popBackStack<TestClass>(true)
+ assertThat(popped).isTrue()
+ assertThat(navigator.backStack.size).isEqualTo(1)
+ }
+
+ @UiThreadTest
+ @Test
fun testPopBackStackWithKClassArg() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
@@ -1841,6 +2013,44 @@
@UiThreadTest
@Test
+ fun testPopBackStackWithKClassNotInGraph() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.popBackStack<TestClassPathArg>(true)
+ }
+ assertThat(exception.message).isEqualTo(
+ "Destination with route TestClassPathArg cannot be found in " +
+ "navigation graph ${navController.graph}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
+ fun testPopBackStackWithKClassNotInBackStack() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(1)
+
+ val popped = navController.popBackStack<TestClass>(true)
+ assertThat(popped).isFalse()
+ }
+
+ @UiThreadTest
+ @Test
fun testPopBackStackWithObject() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
@@ -1933,6 +2143,75 @@
@UiThreadTest
@Test
+ fun testPopBackStackWithObjectNested() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ // a sibling graph with nested KClass destination
+ navigation<TestGraph>(startDestination = TestClass::class) {
+ test<TestClass>()
+ }
+ test("second")
+ }
+
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo(
+ TEST_CLASS_ROUTE
+ )
+
+ navController.navigate("second")
+
+ assertThat(navigator.backStack.size).isEqualTo(3)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo("second")
+
+ val popped = navController.popBackStack(TestClass(), true)
+ assertThat(popped).isTrue()
+ assertThat(navigator.backStack.size).isEqualTo(1)
+ }
+
+ @UiThreadTest
+ @Test
+ fun testPopBackStackWithObjectNotInGraph() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.popBackStack(TestClassPathArg(1), true)
+ }
+ assertThat(exception.message).isEqualTo(
+ "Destination with route TestClassPathArg cannot be found in " +
+ "navigation graph ${navController.graph}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
+ fun testPopBackStackWithObjectNotInBackStack() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(1)
+
+ val popped = navController.popBackStack(TestClass(), true)
+ assertThat(popped).isFalse()
+ }
+
+ @UiThreadTest
+ @Test
fun testFindDestinationWithRoute() {
val navController = createNavController()
navController.graph = nav_singleArg_graph
@@ -3409,6 +3688,27 @@
@UiThreadTest
@Test
+ fun testNavigateWithObjectNotInGraph() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(
+ startDestination = TestClass::class
+ ) {
+ test<TestClass>()
+ }
+ assertThat(navController.currentDestination?.route).isEqualTo(
+ TEST_CLASS_ROUTE
+ )
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.navigate(TestClassPathArg(1))
+ }
+ assertThat(exception.message).isEqualTo(
+ "Destination with route TestClassPathArg cannot be found in navigation " +
+ "graph ${navController.graph}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
fun testDeepLinkFromNavGraph() {
val navController = createNavController()
navController.graph = nav_simple_route_graph
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index 39c0994..d0b7a2e 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -547,7 +547,14 @@
public inline fun <reified T : Any> popBackStack(
inclusive: Boolean,
saveState: Boolean = false
- ): Boolean = popBackStack(serializer<T>().hashCode(), inclusive, saveState)
+ ): Boolean {
+ val id = serializer<T>().hashCode()
+ requireNotNull(findDestinationFromRoot(id)) {
+ "Destination with route ${T::class.simpleName} cannot be found in navigation " +
+ "graph $graph"
+ }
+ return popBackStack(id, inclusive, saveState)
+ }
/**
* Attempts to pop the controller's back stack back to a specific destination.
@@ -640,11 +647,7 @@
): Boolean {
// route contains arguments so we need to generate and pop with the populated route
// rather than popping based on route pattern
- val finalRoute = generateRouteFilled(route, fromBackStack = true)
- requireNotNull(finalRoute) {
- "PopBackStack failed: route $route cannot be found from" +
- "the current backstack. The current destination is $currentDestination"
- }
+ val finalRoute = generateRouteFilled(route)
return popBackStackInternal(finalRoute, inclusive, saveState)
}
@@ -904,7 +907,7 @@
public fun <T : Any> clearBackStack(route: T): Boolean {
// route contains arguments so we need to generate and clear with the populated route
// rather than clearing based on route pattern
- val finalRoute = generateRouteFilled(route) ?: return false
+ val finalRoute = generateRouteFilled(route)
val cleared = clearBackStackInternal(finalRoute)
// Only return true if the clear succeeded and we've dispatched
// the change to a new destination
@@ -1640,6 +1643,17 @@
return currentNode.findDestination(destinationId)
}
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public fun findDestinationFromRoot(@IdRes destinationId: Int): NavDestination? {
+ if (_graph == null) {
+ return null
+ }
+ if (_graph!!.id == destinationId) {
+ return _graph
+ }
+ return _graph!!.findChildNode(destinationId)
+ }
+
private fun NavDestination.findDestination(@IdRes destinationId: Int): NavDestination? {
if (id == destinationId) {
return this
@@ -1662,21 +1676,18 @@
return currentGraph.findNode(route)
}
- // Finds destination and generates a route filled with args based on the serializable object.
- // `fromBackStack` is for efficiency - if left false, the worst case scenario is searching
- // from entire graph when we only care about backstack.
+ // Finds destination within _graph including its children and
+ // generates a route filled with args based on the serializable object.
+ // Throws if destination with `route` is not found
@OptIn(InternalSerializationApi::class)
- private fun <T : Any> generateRouteFilled(route: T, fromBackStack: Boolean = false): String? {
- val destination = if (fromBackStack) {
- // limit search within backstack
- backQueue.lastOrNull {
- it.destination.id == route::class.serializer().hashCode()
- }?.destination
- } else {
- // search from within root graph
- findDestination(route::class.serializer().hashCode())
+ private fun <T : Any> generateRouteFilled(route: T): String {
+ val id = route::class.serializer().hashCode()
+ val destination = findDestinationFromRoot(id)
+ // throw immediately if destination is not found within the graph
+ requireNotNull(destination) {
+ "Destination with route ${route::class.simpleName} cannot be found " +
+ "in navigation graph $_graph"
}
- if (destination == null) return null
return route.generateRouteWithArgs(
// get argument typeMap
destination.arguments.mapValues { it.value.type }
@@ -2688,8 +2699,21 @@
* target NavBackStackEntry's [NavDestination] must have been created with route from [KClass].
* @throws IllegalArgumentException if the destination is not on the back stack
*/
- public inline fun <reified T : Any> getBackStackEntry(): NavBackStackEntry =
- getBackStackEntry(serializer<T>().hashCode())
+ public inline fun <reified T : Any> getBackStackEntry(): NavBackStackEntry {
+ val id = serializer<T>().hashCode()
+ requireNotNull(findDestinationFromRoot(id)) {
+ "Destination with route ${T::class.simpleName} cannot be found in navigation " +
+ "graph $graph"
+ }
+ val lastFromBackStack = currentBackStack.value.lastOrNull { entry ->
+ entry.destination.id == id
+ }
+ requireNotNull(lastFromBackStack) {
+ "No destination with route ${T::class.simpleName} is on the NavController's " +
+ "back stack. The current destination is $currentDestination"
+ }
+ return lastFromBackStack
+ }
/**
* Gets the topmost [NavBackStackEntry] for a route from an Object.
@@ -2705,11 +2729,7 @@
public fun <T : Any> getBackStackEntry(route: T): NavBackStackEntry {
// route contains arguments so we need to generate the populated route
// rather than getting entry based on route pattern
- val finalRoute = generateRouteFilled(route, fromBackStack = true)
- requireNotNull(finalRoute) {
- "No destination with route $finalRoute is on the NavController's back stack. The " +
- "current destination is $currentDestination"
- }
+ val finalRoute = generateRouteFilled(route)
return getBackStackEntry(finalRoute)
}
diff --git a/navigation/navigation-ui-ktx/build.gradle b/navigation/navigation-ui-ktx/build.gradle
index 8b47902..0a1ede1 100644
--- a/navigation/navigation-ui-ktx/build.gradle
+++ b/navigation/navigation-ui-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -38,7 +38,7 @@
androidx {
name = "Navigation UI Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Android Navigation-UI-Ktx"
metalavaK2UastEnabled = true
diff --git a/paging/integration-tests/testapp/build.gradle b/paging/integration-tests/testapp/build.gradle
index f198a5e..147eecb 100644
--- a/paging/integration-tests/testapp/build.gradle
+++ b/paging/integration-tests/testapp/build.gradle
@@ -44,15 +44,16 @@
// Only needed to ensure version of annotation:annotation matches in impl
// and androidTestImpl, for both AOSP and playground builds.
implementation(project(":annotation:annotation"))
+ implementation(project(":annotation:annotation-experimental"))
// Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
+ androidTestImplementation(project(":annotation:annotation"))
androidTestImplementation(project(":annotation:annotation-experimental"))
androidTestImplementation(libs.kotlinTest)
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.truth)
- androidTestImplementation(project(":annotation:annotation"))
}
android {
diff --git a/paging/paging-common-ktx/build.gradle b/paging/paging-common-ktx/build.gradle
index b4dfab7..0f607d3 100644
--- a/paging/paging-common-ktx/build.gradle
+++ b/paging/paging-common-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -34,7 +34,7 @@
androidx {
name = "Paging-Common Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'paging-common' artifact"
metalavaK2UastEnabled = true
diff --git a/paging/paging-compose/build.gradle b/paging/paging-compose/build.gradle
index 7810e16..f52c01c 100644
--- a/paging/paging-compose/build.gradle
+++ b/paging/paging-compose/build.gradle
@@ -21,8 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import androidx.build.RunApiTasks
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -79,10 +78,9 @@
androidx {
name = "Paging-Compose"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Compose integration with Paging"
- runApiTasks = new RunApiTasks.Yes()
legacyDisableKotlinStrictApiMode = true
samples(project(":paging:paging-compose:paging-compose-samples"))
}
diff --git a/paging/paging-runtime-ktx/build.gradle b/paging/paging-runtime-ktx/build.gradle
index 1399fe5..dc1b808 100644
--- a/paging/paging-runtime-ktx/build.gradle
+++ b/paging/paging-runtime-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -37,7 +37,7 @@
androidx {
name = "Paging-Runtime Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'paging-runtime' artifact"
metalavaK2UastEnabled = true
diff --git a/paging/paging-rxjava2-ktx/build.gradle b/paging/paging-rxjava2-ktx/build.gradle
index f41e14e..9bd8dab 100644
--- a/paging/paging-rxjava2-ktx/build.gradle
+++ b/paging/paging-rxjava2-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -44,7 +44,7 @@
androidx {
name = "Paging RxJava2 Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'paging-rxjava2' artifact"
metalavaK2UastEnabled = true
diff --git a/palette/palette-ktx/build.gradle b/palette/palette-ktx/build.gradle
index 98748c6..206b05c 100644
--- a/palette/palette-ktx/build.gradle
+++ b/palette/palette-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -40,7 +40,7 @@
androidx {
name = "Palette Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for 'palette' artifact"
metalavaK2UastEnabled = true
diff --git a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/Dimensions.aidl b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/Dimensions.aidl
similarity index 97%
rename from pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/Dimensions.aidl
rename to pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/Dimensions.aidl
index 1dfe734..b6b6775 100644
--- a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/Dimensions.aidl
+++ b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/Dimensions.aidl
@@ -16,6 +16,6 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaOnlyStableParcelable @JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
parcelable Dimensions;
diff --git a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/LinkRects.aidl b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/GotoLink.aidl
similarity index 95%
copy from pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/LinkRects.aidl
copy to pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/GotoLink.aidl
index 028ecf0..b1a881c 100644
--- a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/LinkRects.aidl
+++ b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/GotoLink.aidl
@@ -16,6 +16,6 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaOnlyStableParcelable @JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
-parcelable LinkRects;
+parcelable GotoLink;
diff --git a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/LinkRects.aidl b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/GotoLinkDestination.aidl
similarity index 94%
copy from pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/LinkRects.aidl
copy to pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/GotoLinkDestination.aidl
index 028ecf0..cbfa741 100644
--- a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/LinkRects.aidl
+++ b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/GotoLinkDestination.aidl
@@ -16,6 +16,6 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaOnlyStableParcelable @JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
-parcelable LinkRects;
+parcelable GotoLinkDestination;
diff --git a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/LinkRects.aidl b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/LinkRects.aidl
similarity index 97%
rename from pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/LinkRects.aidl
rename to pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/LinkRects.aidl
index 028ecf0..59da54a 100644
--- a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/LinkRects.aidl
+++ b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/LinkRects.aidl
@@ -16,6 +16,6 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaOnlyStableParcelable @JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
parcelable LinkRects;
diff --git a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/MatchRects.aidl b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/MatchRects.aidl
similarity index 97%
rename from pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/MatchRects.aidl
rename to pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/MatchRects.aidl
index aef58dd..3e0d4aa 100644
--- a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/MatchRects.aidl
+++ b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/MatchRects.aidl
@@ -16,6 +16,6 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaOnlyStableParcelable @JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
parcelable MatchRects;
diff --git a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/PageSelection.aidl b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PageSelection.aidl
similarity index 97%
rename from pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/PageSelection.aidl
rename to pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PageSelection.aidl
index 7915e96..fe2285f 100644
--- a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/PageSelection.aidl
+++ b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PageSelection.aidl
@@ -16,6 +16,6 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaOnlyStableParcelable @JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
parcelable PageSelection;
diff --git a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/PdfDocumentRemote.aidl b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PdfDocumentRemote.aidl
similarity index 67%
rename from pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/PdfDocumentRemote.aidl
rename to pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PdfDocumentRemote.aidl
index af61188..fac5a69 100644
--- a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/PdfDocumentRemote.aidl
+++ b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PdfDocumentRemote.aidl
@@ -16,21 +16,22 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
interface PdfDocumentRemote {
int create(in ParcelFileDescriptor pfd, String password);
int numPages();
- androidx.pdf.aidl.Dimensions getPageDimensions(int pageNum);
- boolean renderPage(int pageNum, in androidx.pdf.aidl.Dimensions size, boolean hideTextAnnots, in ParcelFileDescriptor output);
- boolean renderTile(int pageNum, int pageWidth, int pageHeight, int left, int top, in androidx.pdf.aidl.Dimensions tileSize, boolean hideTextAnnots, in ParcelFileDescriptor output);
+ androidx.pdf.models.Dimensions getPageDimensions(int pageNum);
+ android.graphics.Bitmap renderPage(int pageNum, int pageWidth, int pageHeight, boolean hideTextAnnots);
+ android.graphics.Bitmap renderTile(int pageNum, int tileWidth, int tileHeight, int scaledPageWidth, int scaledPageHeight, int left, int top, boolean hideTextAnnots);
String getPageText(int pageNum);
List<String> getPageAltText(int pageNum);
- androidx.pdf.aidl.MatchRects searchPageText(int pageNum, String query);
- androidx.pdf.aidl.PageSelection selectPageText(int pageNum, in androidx.pdf.aidl.SelectionBoundary start, in androidx.pdf.aidl.SelectionBoundary stop);
- androidx.pdf.aidl.LinkRects getPageLinks(int pageNum);
- byte[] getPageGotoLinksByteArray(int pageNum);
+ androidx.pdf.models.MatchRects searchPageText(int pageNum, String query);
+ androidx.pdf.models.PageSelection selectPageText(int pageNum, in androidx.pdf.models.SelectionBoundary start, in androidx.pdf.models.SelectionBoundary stop);
+ androidx.pdf.models.LinkRects getPageLinks(int pageNum);
+ List<androidx.pdf.models.GotoLink> getPageGotoLinks(int pageNum);
boolean isPdfLinearized();
+ int getFormType();
boolean cloneWithoutSecurity(in ParcelFileDescriptor destination);
boolean saveAs(in ParcelFileDescriptor destination);
}
diff --git a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/SelectionBoundary.aidl b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/SelectionBoundary.aidl
similarity index 97%
rename from pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/SelectionBoundary.aidl
rename to pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/SelectionBoundary.aidl
index 8348dad..975af72 100644
--- a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/aidl/SelectionBoundary.aidl
+++ b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/SelectionBoundary.aidl
@@ -16,6 +16,6 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaOnlyStableParcelable @JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
parcelable SelectionBoundary;
diff --git a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfLoaderTest.java b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfLoaderTest.java
index 635e63b..eff313b 100644
--- a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfLoaderTest.java
+++ b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfLoaderTest.java
@@ -30,15 +30,15 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import androidx.pdf.aidl.Dimensions;
-import androidx.pdf.aidl.LinkRects;
-import androidx.pdf.aidl.MatchRects;
-import androidx.pdf.aidl.PageSelection;
-import androidx.pdf.aidl.PdfDocumentRemote;
-import androidx.pdf.aidl.SelectionBoundary;
import androidx.pdf.data.DisplayData;
import androidx.pdf.data.Opener;
import androidx.pdf.data.PdfStatus;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.LinkRects;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.PdfDocumentRemote;
+import androidx.pdf.models.SelectionBoundary;
import androidx.pdf.util.RectUtils;
import androidx.pdf.util.TileBoard;
import androidx.pdf.util.TileBoard.CancelTilesCallback;
diff --git a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfTaskExecutorTest.java b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfTaskExecutorTest.java
index e901d9d..1466502 100644
--- a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfTaskExecutorTest.java
+++ b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfTaskExecutorTest.java
@@ -24,7 +24,7 @@
import android.os.RemoteException;
-import androidx.pdf.aidl.PdfDocumentRemote;
+import androidx.pdf.models.PdfDocumentRemote;
import androidx.pdf.pdflib.PdfDocumentRemoteProto;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
diff --git a/pdf/pdf-viewer/src/main/AndroidManifest.xml b/pdf/pdf-viewer/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..149f357
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <application android:label="PdfViewer">
+
+ <service
+ android:name="androidx.pdf.pdflib.PdfDocumentService"
+ android:isolatedProcess="true"
+ tools:ignore="MissingServiceExportedEqualsTrue" />
+
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/data/ContentOpenable.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/data/ContentOpenable.java
index 72c3be7..2d3c15e 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/data/ContentOpenable.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/data/ContentOpenable.java
@@ -24,7 +24,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
+import androidx.pdf.models.Dimensions;
import androidx.pdf.util.Preconditions;
import androidx.pdf.util.Uris;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/data/TextSelection.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/data/TextSelection.java
index cdb2107..a48a9fb 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/data/TextSelection.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/data/TextSelection.java
@@ -20,7 +20,7 @@
import android.os.Parcelable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.SelectionBoundary;
+import androidx.pdf.models.SelectionBoundary;
/** Represents the selection of part of a piece of text - a start and a stop. */
@RestrictTo(RestrictTo.Scope.LIBRARY)
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/fetcher/Fetcher.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/fetcher/Fetcher.java
index ac4f5ee..68735fe 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/fetcher/Fetcher.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/fetcher/Fetcher.java
@@ -20,13 +20,13 @@
import android.net.Uri;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
import androidx.pdf.data.ContentOpenable;
import androidx.pdf.data.FileOpenable;
import androidx.pdf.data.FutureValue;
import androidx.pdf.data.Openable;
import androidx.pdf.data.Opener;
import androidx.pdf.data.UiFutureValues;
+import androidx.pdf.models.Dimensions;
import androidx.pdf.util.Preconditions;
import androidx.pdf.util.StrictModeUtils;
import androidx.pdf.util.Uris;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/Dimensions.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/Dimensions.java
similarity index 91%
rename from pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/Dimensions.java
rename to pdf/pdf-viewer/src/main/java/androidx/pdf/models/Dimensions.java
index 07462ab..d852a22c 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/Dimensions.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/Dimensions.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package androidx.pdf.aidl;
+package androidx.pdf.models;
+import android.annotation.SuppressLint;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,6 +29,7 @@
* Objects of this class are immutable.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("BanParcelableUsage")
public class Dimensions implements Parcelable {
public static final Creator<Dimensions> CREATOR = new Creator<Dimensions>() {
@Override
@@ -49,7 +51,7 @@
this.mHeight = height;
}
- public Dimensions(Rect rect) {
+ public Dimensions(@NonNull Rect rect) {
this.mWidth = rect.width();
this.mHeight = rect.height();
}
@@ -88,7 +90,7 @@
}
@Override
- public void writeToParcel(Parcel parcel, int flags) {
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeInt(mWidth);
parcel.writeInt(mHeight);
}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLink.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLink.java
new file mode 100644
index 0000000..b51abea
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLink.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2024 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.pdf.models;
+
+import android.annotation.SuppressLint;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents the content associated with a goto link on a page in the PDF document. GotoLink is an
+ * internal navigation link which directs the user to a different location within the same pdf
+ * document.
+ */
+// TODO: Use android.graphics.pdf.content.PdfPageGotoLinkContent and remove this class
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressWarnings("deprecation")
+@SuppressLint("BanParcelableUsage")
+public class GotoLink implements Parcelable {
+
+ public static final Creator<GotoLink> CREATOR = new Creator<GotoLink>() {
+ @SuppressWarnings("unchecked")
+ @Override
+ public GotoLink createFromParcel(Parcel parcel) {
+ return new GotoLink((List<Rect>) Objects.requireNonNull(
+ parcel.readArrayList(Rect.class.getClassLoader())),
+ (GotoLinkDestination) Objects.requireNonNull(parcel.readParcelable(
+ GotoLinkDestination.class.getClassLoader())));
+ }
+
+ @Override
+ public GotoLink[] newArray(int size) {
+ return new GotoLink[size];
+ }
+ };
+
+ private final List<Rect> mBounds;
+ private final GotoLinkDestination mDestination;
+
+ /**
+ * Creates a new instance of {@link GotoLink} using the bounds of the goto link
+ * and the destination where it is directing
+ *
+ * @param bounds Bounds which envelop the goto link
+ * @param destination Destination where the goto link is directing
+ * @throws NullPointerException If bounds or destination is null.
+ * @throws IllegalArgumentException If the bounds list is empty.
+ */
+ public GotoLink(@NonNull List<Rect> bounds, @NonNull GotoLinkDestination destination) {
+ Preconditions.checkNotNull(bounds, "Bounds cannot be null");
+ Preconditions.checkArgument(!bounds.isEmpty(), "Bounds cannot be empty");
+ Preconditions.checkNotNull(destination, "Destination cannot be null");
+ this.mBounds = bounds;
+ this.mDestination = destination;
+ }
+
+ /**
+ * Gets the bounds of a {@link GotoLink} represented as a list of {@link Rect}.
+ * Links which are spread across multiple lines will be surrounded by multiple {@link Rect}
+ * in order of viewing.
+ *
+ * @return The bounds of the goto link.
+ */
+ @NonNull
+ public List<Rect> getBounds() {
+ return mBounds;
+ }
+
+ /**
+ * Gets the destination {@link GotoLinkDestination} of the {@link GotoLink}.
+ *
+ * @return Destination where goto link is directing the user.
+ */
+ @NonNull
+ public GotoLinkDestination getDestination() {
+ return mDestination;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "GotoLink{" + "mBounds=" + mBounds + ", mDestination=" + mDestination + '}';
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeList(mBounds);
+ parcel.writeParcelable(mDestination, 0);
+ }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLinkDestination.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLinkDestination.java
new file mode 100644
index 0000000..53c05ec
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLinkDestination.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2024 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.pdf.models;
+
+import android.annotation.SuppressLint;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Represents the content associated with the destination where a goto link is directing.
+ * Should be a nested class of {@link GotoLink}, but AIDL prevents that.
+ */
+// TODO: Use android.graphics.pdf.content.PdfPageGotoLinkContent#Destination and remove this class
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("BanParcelableUsage")
+public class GotoLinkDestination implements Parcelable {
+
+ public static final Creator<GotoLinkDestination> CREATOR =
+ new Creator<GotoLinkDestination>() {
+ @Override
+ public GotoLinkDestination createFromParcel(Parcel parcel) {
+ return new GotoLinkDestination(parcel.readInt(), parcel.readFloat(),
+ parcel.readFloat(), parcel.readFloat());
+ }
+
+ @Override
+ public GotoLinkDestination[] newArray(int size) {
+ return new GotoLinkDestination[size];
+ }
+ };
+
+ private final int mPageNumber;
+ private final float mXCoordinate;
+ private final float mYCoordinate;
+ private final float mZoom;
+
+ /**
+ * Creates a new instance of {@link GotoLinkDestination} using the page number, x coordinate,
+ * and y coordinate of the destination where goto link is directing, and the zoom factor of the
+ * page when goto link takes to the destination.
+ *
+ * @param pageNumber Page number of the goto link Destination
+ * @param xCoordinate X coordinate of the goto link Destination in points (1/72")
+ * @param yCoordinate Y coordinate of the goto link Destination in points (1/72")
+ * @param zoom Zoom factor {@link GotoLinkDestination#getZoom()} of the page when
+ * goto link
+ * takes to the destination
+ * @throws IllegalArgumentException If pageNumber or either of the coordinates or zoom are
+ * less than zero
+ */
+ public GotoLinkDestination(int pageNumber, float xCoordinate, float yCoordinate, float zoom) {
+ Preconditions.checkArgument(pageNumber >= 0,
+ "Page number must be" + " greater than or equal to 0");
+ Preconditions.checkArgument(xCoordinate >= 0,
+ "X coordinate " + "must be greater than or equal to 0");
+ Preconditions.checkArgument(yCoordinate >= 0,
+ "Y coordinate must " + "be greater than or equal to 0");
+ Preconditions.checkArgument(zoom >= 0,
+ "Zoom factor number must be " + "greater than or equal to 0");
+ this.mPageNumber = pageNumber;
+ this.mXCoordinate = xCoordinate;
+ this.mYCoordinate = yCoordinate;
+ this.mZoom = zoom;
+ }
+
+ @Override
+ public String toString() {
+ return "GotoLinkDestination{" + "mPageNumber=" + mPageNumber + ", mXCoordinate="
+ + mXCoordinate + ", mYCoordinate=" + mYCoordinate + ", mZoom=" + mZoom + '}';
+ }
+
+ /**
+ * Gets the page number of the destination where the {@link GotoLink} is directing.
+ *
+ * @return page number of the destination where goto link is directing the user.
+ */
+ public int getPageNumber() {
+ return mPageNumber;
+ }
+
+ /**
+ * Gets the x coordinate of the destination where the {@link GotoLink} is directing.
+ * <p><strong>Note:</strong> If underlying pdfium library can't determine the x coordinate,
+ * it will be set to 0
+ *
+ * @return x coordinate of the Destination where the goto link is directing the user.
+ */
+ public float getXCoordinate() {
+ return mXCoordinate;
+ }
+
+ /**
+ * Gets the y coordinate of the destination where the {@link GotoLink} is directing.
+ * <p><strong>Note:</strong> If underlying pdfium library can't determine the y coordinate,
+ * it will be set to 0
+ *
+ * @return y coordinate of the Destination where the goto link is directing the user.
+ */
+ public float getYCoordinate() {
+ return mYCoordinate;
+ }
+
+ /**
+ * Gets the zoom factor of the page when the goto link takes to the destination
+ * <p><strong>Note:</strong> If there is no zoom value embedded, default value of zoom
+ * will be zero. Otherwise it will be less than 1.0f in case of zoom out and greater
+ * than 1.0f in case of zoom in.
+ *
+ * @return zoom factor of the page when the goto link takes to the destination
+ */
+ public float getZoom() {
+ return mZoom;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mPageNumber);
+ parcel.writeFloat(mXCoordinate);
+ parcel.writeFloat(mYCoordinate);
+ parcel.writeFloat(mZoom);
+ }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/LinkRects.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/LinkRects.java
similarity index 90%
rename from pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/LinkRects.java
rename to pdf/pdf-viewer/src/main/java/androidx/pdf/models/LinkRects.java
index 3739224..499cb82 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/LinkRects.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/LinkRects.java
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-package androidx.pdf.aidl;
+package androidx.pdf.models;
+import android.annotation.SuppressLint;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.pdf.data.ListOfList;
@@ -42,6 +44,7 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
@SuppressWarnings("deprecation")
+@SuppressLint("BanParcelableUsage")
public class LinkRects extends ListOfList<Rect> implements Parcelable {
public static final LinkRects NO_LINKS = new LinkRects(Collections.emptyList(),
Collections.emptyList(), Collections.emptyList());
@@ -65,7 +68,8 @@
private final List<Integer> mLinkToRect;
private final List<String> mUrls;
- public LinkRects(List<Rect> rects, List<Integer> linkToRect, List<String> urls) {
+ public LinkRects(@NonNull List<Rect> rects, @NonNull List<Integer> linkToRect,
+ @NonNull List<String> urls) {
super(rects, linkToRect);
this.mRects = Preconditions.checkNotNull(rects);
this.mLinkToRect = Preconditions.checkNotNull(linkToRect);
@@ -73,6 +77,7 @@
}
/** Return the URL corresponding to the given link. */
+ @NonNull
public String getUrl(int link) {
return mUrls.get(link);
}
@@ -103,7 +108,7 @@
}
@Override
- public void writeToParcel(Parcel parcel, int flags) {
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeList(mRects);
parcel.writeList(mLinkToRect);
parcel.writeList(mUrls);
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/MatchRects.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/MatchRects.java
similarity index 93%
rename from pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/MatchRects.java
rename to pdf/pdf-viewer/src/main/java/androidx/pdf/models/MatchRects.java
index 4ab9bed..98a9695 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/MatchRects.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/MatchRects.java
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-package androidx.pdf.aidl;
+package androidx.pdf.models;
+import android.annotation.SuppressLint;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.pdf.data.ListOfList;
import androidx.pdf.util.Preconditions;
@@ -42,6 +44,7 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
@SuppressWarnings("deprecation")
+@SuppressLint("BanParcelableUsage")
public class MatchRects extends ListOfList<Rect> implements Parcelable {
public static final MatchRects NO_MATCHES = new MatchRects(Collections.emptyList(),
Collections.emptyList(), Collections.emptyList());
@@ -65,7 +68,8 @@
private final List<Integer> mMatchToRect;
private final List<Integer> mCharIndexes;
- public MatchRects(List<Rect> rects, List<Integer> matchToRect, List<Integer> charIndexes) {
+ public MatchRects(@NonNull List<Rect> rects, @NonNull List<Integer> matchToRect,
+ @NonNull List<Integer> charIndexes) {
super(rects, matchToRect);
this.mRects = Preconditions.checkNotNull(rects);
this.mMatchToRect = Preconditions.checkNotNull(matchToRect);
@@ -94,6 +98,7 @@
}
/** Returns the first rect for a given match. */
+ @NonNull
public Rect getFirstRect(int match) {
return mRects.get(mMatchToRect.get(match));
}
@@ -102,6 +107,7 @@
* Returns the flattened, one-dimensional list of all rectangles that surround
* all matches <strong>except</strong> for the given match.
*/
+ @NonNull
public List<Rect> flattenExcludingMatch(int match) {
if (match < 0 || match >= mMatchToRect.size()) {
throw new ArrayIndexOutOfBoundsException(match);
@@ -149,7 +155,7 @@
}
@Override
- public void writeToParcel(Parcel parcel, int flags) {
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeList(mRects);
parcel.writeList(mMatchToRect);
parcel.writeList(mCharIndexes);
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/PageSelection.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/PageSelection.java
similarity index 87%
rename from pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/PageSelection.java
rename to pdf/pdf-viewer/src/main/java/androidx/pdf/models/PageSelection.java
index 794db95..c2460c2 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/PageSelection.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/PageSelection.java
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-package androidx.pdf.aidl;
+package androidx.pdf.models;
import android.graphics.Rect;
import android.os.Parcel;
+import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.pdf.data.TextSelection;
import java.util.List;
-
/** Represents text selection on a particular page of a PDF. Immutable. */
+// TODO: Use android.graphics.pdf.models.selection.PageSelection and remove this class
@RestrictTo(RestrictTo.Scope.LIBRARY)
@SuppressWarnings("deprecation")
public class PageSelection extends TextSelection {
@@ -58,8 +59,8 @@
/** The highlighted text. */
private final String mText;
- public PageSelection(int page, SelectionBoundary start, SelectionBoundary stop,
- List<Rect> rects, String text) {
+ public PageSelection(int page, @NonNull SelectionBoundary start,
+ @NonNull SelectionBoundary stop, @NonNull List<Rect> rects, @NonNull String text) {
super(start, stop);
this.mPage = page;
this.mRects = rects;
@@ -70,10 +71,12 @@
return mPage;
}
+ @NonNull
public List<Rect> getRects() {
return mRects;
}
+ @NonNull
public String getText() {
return mText;
}
@@ -86,7 +89,7 @@
}
@Override
- public void writeToParcel(Parcel parcel, int flags) {
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeInt(mPage);
parcel.writeParcelable(getStart(), 0);
parcel.writeParcelable(getStop(), 0);
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/SelectionBoundary.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/SelectionBoundary.java
similarity index 90%
rename from pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/SelectionBoundary.java
rename to pdf/pdf-viewer/src/main/java/androidx/pdf/models/SelectionBoundary.java
index 14c6157..01294ab 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/aidl/SelectionBoundary.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/SelectionBoundary.java
@@ -14,21 +14,24 @@
* limitations under the License.
*/
-package androidx.pdf.aidl;
+package androidx.pdf.models;
+import android.annotation.SuppressLint;
import android.graphics.Point;
import android.os.Parcel;
import android.os.Parcelable;
+import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
-
/**
* Represents one edge of the selected text. A boundary can be defined by
* either an index into the text, a point on the page, or both.
* Should be a nested class of {@link PageSelection}, but AIDL prevents that.
*/
+// TODO: Use android.graphics.pdf.models.selection.SelectionBoundary and remove this class
@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("BanParcelableUsage")
public class SelectionBoundary implements Parcelable {
public static final SelectionBoundary PAGE_START = SelectionBoundary.atIndex(0);
public static final SelectionBoundary PAGE_END = SelectionBoundary.atIndex(Integer.MAX_VALUE);
@@ -80,17 +83,20 @@
}
/** Create a boundary that has a particular index, but the position is not known. */
+ @NonNull
public static SelectionBoundary atIndex(int index) {
return new SelectionBoundary(index, -1, -1, false);
}
/** Create a boundary at a particular point, but the index is not known. */
+ @NonNull
public static SelectionBoundary atPoint(int x, int y) {
return new SelectionBoundary(-1, x, y, false);
}
/** Create a boundary at a particular point, but the index is not known. */
- public static SelectionBoundary atPoint(Point p) {
+ @NonNull
+ public static SelectionBoundary atPoint(@NonNull Point p) {
return new SelectionBoundary(-1, p.x, p.y, false);
}
@@ -101,7 +107,7 @@
}
@Override
- public void writeToParcel(Parcel parcel, int flags) {
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeIntArray(new int[]{mIndex, mX, mY, mIsRtl ? 1 : 0});
}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/LoadPdfResult.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/LoadPdfResult.java
new file mode 100644
index 0000000..5ce75a3
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/LoadPdfResult.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024 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.pdf.pdflib;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.data.PdfStatus;
+import androidx.pdf.util.Preconditions;
+
+/**
+ * A struct that holds either a successfully loaded PdfDocument, or the reason why it failed.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class LoadPdfResult {
+
+ private final PdfStatus mStatus;
+ @Nullable
+ private final PdfDocument mPdfDocument;
+
+ public LoadPdfResult(int status, @Nullable PdfDocument pdfDocument) {
+ if (status == PdfStatus.LOADED.getNumber()) {
+ Preconditions.checkArgument(pdfDocument != null, "Missing pdfDocument");
+ } else {
+ Preconditions.checkArgument(pdfDocument == null,
+ "Shouldn't construct " + "broken pdfDocument");
+ }
+ this.mStatus = PdfStatus.values()[status];
+ this.mPdfDocument = pdfDocument;
+ }
+
+ @NonNull
+ public PdfStatus getStatus() {
+ return mStatus;
+ }
+
+ @Nullable
+ public PdfDocument getPdfDocument() {
+ return mPdfDocument;
+ }
+
+ public boolean isLoaded() {
+ return mStatus == PdfStatus.LOADED;
+ }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocument.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocument.java
new file mode 100644
index 0000000..66fef2ba
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocument.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2024 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.pdf.pdflib;
+
+import android.graphics.Bitmap;
+import android.os.ParcelFileDescriptor;
+
+import androidx.annotation.RestrictTo;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.GotoLink;
+import androidx.pdf.models.LinkRects;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.SelectionBoundary;
+import androidx.pdf.util.StrictModeUtils;
+
+import java.util.List;
+
+// TODO: Delete this class once framework API calls are added
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class PdfDocument {
+ private static final String LIB_NAME = "pdfclient";
+
+ private final long mPdfDocPtr;
+
+ private final int mNumPages;
+
+ protected PdfDocument(long pdfDocPtr, int numPages) {
+ this.mPdfDocPtr = pdfDocPtr;
+ this.mNumPages = numPages;
+ }
+
+ public static LoadPdfResult createFromFd(int fd, String password) {
+ return null;
+ }
+
+ static void loadLibPdf() {
+ StrictModeUtils.bypass(() -> System.loadLibrary(LIB_NAME));
+ }
+
+ public void destroy() {
+ }
+
+ public boolean saveAs(ParcelFileDescriptor destination) {
+ return false;
+ }
+
+ public int numPages() {
+ return mNumPages;
+ }
+
+ public Dimensions getPageDimensions(int pageNum) {
+ return null;
+ }
+
+ public Bitmap renderPageFd(int pageNum, int pageWidth, int pageHeight, boolean hideTextAnnots) {
+ return null;
+ }
+
+ public Bitmap renderTileFd(int pageNum, int tileWidth, int tileHeight, int scaledPageWidth,
+ int scaledPageHeight, int left, int top, boolean hideTextAnnots) {
+ return null;
+ }
+
+ public boolean cloneWithoutSecurity(ParcelFileDescriptor destination) {
+ return false;
+ }
+
+ public String getPageText(int pageNum) {
+ return null;
+ }
+
+ public List<String> getPageAltText(int pageNum) {
+ return null;
+ }
+
+ public MatchRects searchPageText(int pageNum, String query) {
+ return null;
+ }
+
+ public PageSelection selectPageText(int pageNum, SelectionBoundary start,
+ SelectionBoundary stop) {
+ return null;
+ }
+
+ public LinkRects getPageLinks(int pageNum) {
+ return null;
+ }
+
+ public List<GotoLink> getPageGotoLinks(int pageNum) {
+ return null;
+ }
+
+ public boolean isPdfLinearized() {
+ return false;
+ }
+
+ public int getFormType() {
+ return -1;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PdfDocument(%x, %d pages)", mPdfDocPtr, mNumPages);
+ }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentRemoteProto.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentRemoteProto.java
index 31f31f5..2023539 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentRemoteProto.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentRemoteProto.java
@@ -17,7 +17,7 @@
package androidx.pdf.pdflib;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.PdfDocumentRemote;
+import androidx.pdf.models.PdfDocumentRemote;
/**
*
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentService.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentService.java
new file mode 100644
index 0000000..e7f6c28
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentService.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2024 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.pdf.pdflib;
+
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.data.FutureValues;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.GotoLink;
+import androidx.pdf.models.LinkRects;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.PdfDocumentRemote;
+import androidx.pdf.models.SelectionBoundary;
+
+import java.util.List;
+
+/** Isolated Service wrapper around the PdfClient native lib, for security purposes. */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class PdfDocumentService extends Service {
+
+ private static final String TAG = "PdfDocumentService";
+
+ @NonNull
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new PdfDocumentRemoteImpl();
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ return super.onUnbind(intent);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ private static class PdfDocumentRemoteImpl extends PdfDocumentRemote.Stub {
+
+ private final FutureValues.BlockingCallback<Boolean> mLoaderCallback =
+ new FutureValues.BlockingCallback<>();
+
+ private PdfDocument mPdfDocument;
+
+ PdfDocumentRemoteImpl() {
+ }
+
+ @Override
+ public int create(ParcelFileDescriptor pfd, String password) throws RemoteException {
+ mLoaderCallback.getBlocking();
+ ensurePdfDestroyed();
+ int fd = pfd.detachFd();
+ LoadPdfResult result = PdfDocument.createFromFd(fd, password);
+ if (result.isLoaded()) {
+ mPdfDocument = result.getPdfDocument();
+ }
+ return result.getStatus().getNumber();
+ }
+
+ @Override
+ public int numPages() {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.numPages();
+ }
+
+ @Override
+ public Dimensions getPageDimensions(int pageNum) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.getPageDimensions(pageNum);
+ }
+
+ @Override
+ public String getPageText(int pageNum) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.getPageText(pageNum);
+ }
+
+ @Override
+ public List<String> getPageAltText(int pageNum) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.getPageAltText(pageNum);
+ }
+
+ @Override
+ public Bitmap renderPage(int pageNum, int pageWidth, int pageHeight,
+ boolean hideTextAnnots) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.renderPageFd(pageNum, pageWidth, pageHeight, hideTextAnnots);
+ }
+
+ @Override
+ public Bitmap renderTile(int pageNum, int tileWidth, int tileHeight, int scaledPageWidth,
+ int scaledPageHeight, int left, int top, boolean hideTextAnnots) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.renderTileFd(pageNum, tileWidth, tileHeight, scaledPageWidth,
+ scaledPageHeight, left, top, hideTextAnnots);
+ }
+
+ @Override
+ public MatchRects searchPageText(int pageNum, String query) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.searchPageText(pageNum, query);
+ }
+
+ @Override
+ public PageSelection selectPageText(int pageNum, SelectionBoundary start,
+ SelectionBoundary stop) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.selectPageText(pageNum, start, stop);
+ }
+
+ @Override
+ public LinkRects getPageLinks(int pageNum) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.getPageLinks(pageNum);
+ }
+
+ @Override
+ public List<GotoLink> getPageGotoLinks(int pageNum) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.getPageGotoLinks(pageNum);
+ }
+
+ @Override
+ public boolean isPdfLinearized() {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.isPdfLinearized();
+ }
+
+ @Override
+ public int getFormType() {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.getFormType();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ mLoaderCallback.getBlocking();
+ ensurePdfDestroyed();
+ super.finalize();
+ }
+
+ private void ensurePdfDestroyed() {
+ if (mPdfDocument != null) {
+ try {
+ mPdfDocument.destroy();
+ } catch (Throwable ignored) {
+ }
+ }
+ mPdfDocument = null;
+ }
+
+ @Override
+ public boolean cloneWithoutSecurity(ParcelFileDescriptor destination) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.cloneWithoutSecurity(destination);
+ }
+
+ @Override
+ public boolean saveAs(ParcelFileDescriptor destination) {
+ mLoaderCallback.getBlocking();
+ return mPdfDocument.saveAs(destination);
+ }
+ }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionModel.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionModel.java
index 5f8428b..56a76cc1 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionModel.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionModel.java
@@ -17,7 +17,7 @@
package androidx.pdf.select;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.SelectionBoundary;
+import androidx.pdf.models.SelectionBoundary;
import androidx.pdf.util.ObservableValue;
import androidx.pdf.util.Observables;
import androidx.pdf.util.Observables.ExposedValue;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/BitmapRecycler.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/BitmapRecycler.java
index b4c90d1..e412209 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/BitmapRecycler.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/BitmapRecycler.java
@@ -21,7 +21,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
+import androidx.pdf.models.Dimensions;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/RectUtils.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/RectUtils.java
index 9574758..7544beb 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/RectUtils.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/RectUtils.java
@@ -20,7 +20,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
+import androidx.pdf.models.Dimensions;
/**
* Utilities related to {@link Rect}s.
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/TileBoard.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/TileBoard.java
index e1a0496..455ee35 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/TileBoard.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/TileBoard.java
@@ -24,7 +24,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
-import androidx.pdf.aidl.Dimensions;
+import androidx.pdf.models.Dimensions;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AccessibilityPageWrapper.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AccessibilityPageWrapper.java
index f7cc886..a314022 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AccessibilityPageWrapper.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AccessibilityPageWrapper.java
@@ -22,7 +22,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.LinkRects;
+import androidx.pdf.models.LinkRects;
/**
* Container to hold a {@link PageMosaicView}, a {@link PageLinksView} and sometimes a {@link
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageLinksView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageLinksView.java
index f74d742..7b95410 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageLinksView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageLinksView.java
@@ -32,7 +32,7 @@
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.customview.widget.ExploreByTouchHelper;
-import androidx.pdf.aidl.LinkRects;
+import androidx.pdf.models.LinkRects;
import androidx.pdf.util.ObservableValue;
import androidx.pdf.widget.ZoomView;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java
index fe5a196..5fa1106 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java
@@ -23,8 +23,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
-import androidx.pdf.aidl.LinkRects;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.LinkRects;
import androidx.pdf.util.Accessibility;
import androidx.pdf.util.BitmapRecycler;
import androidx.pdf.widget.MosaicView;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageViewFactory.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageViewFactory.java
index aadf6f4d..801d7a4 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageViewFactory.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageViewFactory.java
@@ -21,8 +21,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
-import androidx.pdf.aidl.LinkRects;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.LinkRects;
import androidx.pdf.util.Accessibility;
import androidx.pdf.util.BitmapRecycler;
import androidx.pdf.util.ObservableValue;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java
index ba67ec2..42e3765 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java
@@ -20,8 +20,8 @@
import android.util.Log;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
import androidx.pdf.data.Range;
+import androidx.pdf.models.Dimensions;
import androidx.pdf.util.ErrorLog;
import androidx.pdf.util.Preconditions;
import androidx.pdf.util.ProjectorContext;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfHighlightOverlay.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfHighlightOverlay.java
index f7fcfc0..4f33f41 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfHighlightOverlay.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfHighlightOverlay.java
@@ -17,8 +17,8 @@
package androidx.pdf.viewer;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.MatchRects;
-import androidx.pdf.aidl.PageSelection;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
import androidx.pdf.util.HighlightOverlay;
import androidx.pdf.util.HighlightPaint;
import androidx.pdf.util.RectDrawSpec;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java
index e6d93f3..a1757b8 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java
@@ -22,8 +22,8 @@
import androidx.annotation.RestrictTo;
import androidx.pdf.R;
-import androidx.pdf.aidl.PageSelection;
-import androidx.pdf.aidl.SelectionBoundary;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.SelectionBoundary;
import androidx.pdf.select.SelectionModel;
import androidx.pdf.util.Preconditions;
import androidx.pdf.widget.ZoomView;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionModel.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionModel.java
index c83cbb8c..7846195 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionModel.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionModel.java
@@ -17,8 +17,8 @@
package androidx.pdf.viewer;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.PageSelection;
-import androidx.pdf.aidl.SelectionBoundary;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.SelectionBoundary;
import androidx.pdf.select.SelectionModel;
import androidx.pdf.viewer.loader.PdfLoader;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
index cb320f0..238fa63 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
@@ -37,11 +37,6 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.pdf.R;
-import androidx.pdf.aidl.Dimensions;
-import androidx.pdf.aidl.LinkRects;
-import androidx.pdf.aidl.MatchRects;
-import androidx.pdf.aidl.PageSelection;
-import androidx.pdf.aidl.SelectionBoundary;
import androidx.pdf.data.DisplayData;
import androidx.pdf.data.FutureValue;
import androidx.pdf.data.FutureValues.SettableFutureValue;
@@ -49,6 +44,11 @@
import androidx.pdf.data.PdfStatus;
import androidx.pdf.data.Range;
import androidx.pdf.fetcher.Fetcher;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.LinkRects;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.SelectionBoundary;
import androidx.pdf.util.ErrorLog;
import androidx.pdf.util.ExternalLinks;
import androidx.pdf.util.GestureTracker;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SearchModel.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SearchModel.java
index 4ac0d36..27b92c7 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SearchModel.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SearchModel.java
@@ -20,8 +20,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.MatchRects;
import androidx.pdf.find.MatchCount;
+import androidx.pdf.models.MatchRects;
import androidx.pdf.util.CycleRange;
import androidx.pdf.util.CycleRange.Direction;
import androidx.pdf.util.ObservableValue;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatch.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatch.java
index 1741885..3639a61 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatch.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatch.java
@@ -20,7 +20,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.MatchRects;
+import androidx.pdf.models.MatchRects;
import androidx.pdf.util.CycleRange.Direction;
import androidx.pdf.util.Preconditions;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractPdfTask.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractPdfTask.java
index 184ff87..1f575e0 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractPdfTask.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractPdfTask.java
@@ -21,7 +21,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.PdfDocumentRemote;
+import androidx.pdf.models.PdfDocumentRemote;
import androidx.pdf.pdflib.PdfDocumentRemoteProto;
import androidx.pdf.util.ErrorLog;
import androidx.pdf.util.ThreadUtils;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java
index bab8271..5735011 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java
@@ -25,7 +25,8 @@
import android.util.Log;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.PdfDocumentRemote;
+import androidx.pdf.models.PdfDocumentRemote;
+import androidx.pdf.pdflib.PdfDocumentService;
import androidx.pdf.util.ErrorLog;
import androidx.pdf.util.Preconditions;
@@ -174,12 +175,8 @@
if (mConnected) {
return;
}
- Intent intent = new Intent();
+ Intent intent = new Intent(mContext, PdfDocumentService.class);
// Data is only required here to make sure we start a new service per document.
- // TODO: Update after porting service
- intent.setComponent(new ComponentName(
- /* pkg = */ "com.androidx.pdf",
- /* cls = */ "com.androidx.pdf.PdfDocumentService"));
intent.setData(uri);
Log.d(TAG, "Connecting to service " + uri);
mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
index a37b159..bcf8f0b 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
@@ -26,12 +26,12 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
-import androidx.pdf.aidl.PdfDocumentRemote;
-import androidx.pdf.aidl.SelectionBoundary;
import androidx.pdf.data.DisplayData;
import androidx.pdf.data.Opener;
import androidx.pdf.data.PdfStatus;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.PdfDocumentRemote;
+import androidx.pdf.models.SelectionBoundary;
import androidx.pdf.pdflib.PdfDocumentRemoteProto;
import androidx.pdf.util.BitmapRecycler;
import androidx.pdf.util.ErrorLog;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacks.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacks.java
index bd2fba6..7296882 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacks.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacks.java
@@ -20,11 +20,11 @@
import android.graphics.Rect;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
-import androidx.pdf.aidl.LinkRects;
-import androidx.pdf.aidl.MatchRects;
-import androidx.pdf.aidl.PageSelection;
import androidx.pdf.data.PdfStatus;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.LinkRects;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
import androidx.pdf.util.TileBoard.TileInfo;
import java.io.FileOutputStream;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java
index 6d712d6..b45874c 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java
@@ -19,17 +19,16 @@
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.Point;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.RestrictTo;
import androidx.pdf.R;
-import androidx.pdf.aidl.Dimensions;
-import androidx.pdf.aidl.LinkRects;
-import androidx.pdf.aidl.MatchRects;
-import androidx.pdf.aidl.PageSelection;
-import androidx.pdf.aidl.SelectionBoundary;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.LinkRects;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.SelectionBoundary;
import androidx.pdf.pdflib.PdfDocumentRemoteProto;
import androidx.pdf.util.BitmapParcel;
import androidx.pdf.util.StrictModeUtils;
@@ -56,16 +55,17 @@
/** Arbitrary dimensions used for pages that are broken. */
private static final Dimensions DEFAULT_PAGE = new Dimensions(400, 400);
+ static {
+ // TODO: StrictMode- disk read 14ms.
+ // NOTE: this line can break when running with --noforge, such as when debugging with
+ // Android Studio. You may need to comment it out locally if you see errors like
+ // `java.lang.UnsatisfiedLinkError: no bitmap_parcel in java.library.path`.
+ StrictModeUtils.bypass(() -> BitmapParcel.loadNdkLib());
+ }
+
private final PdfLoader mParent;
private final int mPageNum;
private final boolean mHideTextAnnotations;
-
- /**
- * This flag is set when this page makes pdfClient crash, and we'd better avoid crashing it
- * again.
- */
- private boolean mIsBroken = false;
-
/** Currently scheduled tasks - null if no task of this type is scheduled. */
GetDimensionsTask mDimensionsTask;
RenderBitmapTask mBitmapTask;
@@ -86,14 +86,11 @@
/** The reference pageWidth for all tile related tasks. */
int mTilePageWidth;
-
- static {
- // TODO: StrictMode- disk read 14ms.
- // NOTE: this line can break when running with --noforge, such as when debugging with
- // Android Studio. You may need to comment it out locally if you see errors like
- // `java.lang.UnsatisfiedLinkError: no bitmap_parcel in java.library.path`.
- StrictModeUtils.bypass(() -> BitmapParcel.loadNdkLib());
- }
+ /**
+ * This flag is set when this page makes pdfClient crash, and we'd better avoid crashing it
+ * again.
+ */
+ private boolean mIsBroken = false;
PdfPageLoader(PdfLoader parent, int pageNum, boolean hideTextAnnotations) {
this.mParent = parent;
@@ -342,23 +339,9 @@
@Override
protected Bitmap doInBackground(PdfDocumentRemoteProto pdfDocument) throws RemoteException {
- Bitmap bitmap = mParent.mBitmapRecycler.obtainBitmap(mDimensions);
- if (bitmap != null) {
- BitmapParcel bitmapParcel = null;
- try {
- bitmapParcel = new BitmapParcel(bitmap);
- ParcelFileDescriptor fd = bitmapParcel.openOutputFd();
- if (fd != null) {
- pdfDocument.getPdfDocumentRemote().renderPage(mPageNum, mDimensions,
- mHideTextAnnotations, fd);
- }
- } finally {
- if (bitmapParcel != null) {
- bitmapParcel.close();
- }
- }
- }
- return bitmap;
+ return pdfDocument.getPdfDocumentRemote().renderPage(mPageNum, mDimensions.getWidth(),
+ mDimensions.getHeight(),
+ mHideTextAnnotations);
}
@Override
@@ -404,33 +387,16 @@
@Override
protected Bitmap doInBackground(PdfDocumentRemoteProto pdfDocument) throws RemoteException {
- Bitmap bitmap = mParent.mBitmapRecycler.obtainBitmap(mTileInfo.getSize());
- if (bitmap != null) {
- Point offset = mTileInfo.getOffset();
-
- BitmapParcel bitmapParcel = null;
- try {
- bitmapParcel = new BitmapParcel(bitmap);
- ParcelFileDescriptor fd = bitmapParcel.openOutputFd();
- if (fd != null) {
- pdfDocument.getPdfDocumentRemote().renderTile(
- mPageNum,
- mPageSize.getWidth(),
- mPageSize.getHeight(),
- offset.x,
- offset.y,
- mTileInfo.getSize(),
- mHideTextAnnotations,
- fd);
- }
- } finally {
- if (bitmapParcel != null) {
- bitmapParcel.close();
- }
- }
-
- }
- return bitmap;
+ Point offset = mTileInfo.getOffset();
+ return pdfDocument.getPdfDocumentRemote().renderTile(
+ mPageNum,
+ mTileInfo.getSize().getWidth(),
+ mTileInfo.getSize().getHeight(),
+ mPageSize.getWidth(),
+ mPageSize.getHeight(),
+ offset.x,
+ offset.y,
+ mHideTextAnnotations);
}
@Override
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/WeakPdfLoaderCallbacks.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/WeakPdfLoaderCallbacks.java
index 89625b7..e271017 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/WeakPdfLoaderCallbacks.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/WeakPdfLoaderCallbacks.java
@@ -22,11 +22,11 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
-import androidx.pdf.aidl.Dimensions;
-import androidx.pdf.aidl.LinkRects;
-import androidx.pdf.aidl.MatchRects;
-import androidx.pdf.aidl.PageSelection;
import androidx.pdf.data.PdfStatus;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.LinkRects;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
import androidx.pdf.util.TileBoard.TileInfo;
import java.lang.ref.WeakReference;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/MosaicView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/MosaicView.java
index 0e4d5e4..1889976 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/MosaicView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/MosaicView.java
@@ -39,7 +39,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.pdf.aidl.Dimensions;
+import androidx.pdf.models.Dimensions;
import androidx.pdf.util.BitmapRecycler;
import androidx.pdf.util.ErrorLog;
import androidx.pdf.util.Preconditions;
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/PdfDocumentRemote.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/PdfDocumentRemote.aidl
deleted file mode 100644
index 381fae7..0000000
--- a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/PdfDocumentRemote.aidl
+++ /dev/null
@@ -1,42 +0,0 @@
-package androidx.pdf.aidl;
-
-import android.graphics.Rect;
-
-import android.os.ParcelFileDescriptor;
-import androidx.pdf.aidl.Dimensions;
-import androidx.pdf.aidl.MatchRects;
-import androidx.pdf.aidl.PageSelection;
-import androidx.pdf.aidl.SelectionBoundary;
-import androidx.pdf.aidl.LinkRects;
-
-/** Remote interface around a PdfDocument. */
-@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
-interface PdfDocumentRemote {
- int create(in ParcelFileDescriptor pfd, String password);
-
- int numPages();
- Dimensions getPageDimensions(int pageNum);
-
- boolean renderPage(int pageNum, in Dimensions size, boolean hideTextAnnots,
- in ParcelFileDescriptor output);
- boolean renderTile(int pageNum, int pageWidth, int pageHeight, int left, int top,
- in Dimensions tileSize, boolean hideTextAnnots, in ParcelFileDescriptor output);
-
- String getPageText(int pageNum);
- List<String> getPageAltText(int pageNum);
-
- MatchRects searchPageText(int pageNum, String query);
- PageSelection selectPageText(int pageNum, in SelectionBoundary start, in SelectionBoundary stop);
-
- LinkRects getPageLinks(int pageNum);
-
- byte[] getPageGotoLinksByteArray(int pageNum);
-
- boolean isPdfLinearized();
-
- boolean cloneWithoutSecurity(in ParcelFileDescriptor destination);
-
- boolean saveAs(in ParcelFileDescriptor destination);
-
- // The PdfDocument is destroyed when this service is destroyed.
-}
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/Dimensions.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/Dimensions.aidl
similarity index 84%
rename from pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/Dimensions.aidl
rename to pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/Dimensions.aidl
index 1ebe1e2..e12ed67 100644
--- a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/Dimensions.aidl
+++ b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/Dimensions.aidl
@@ -1,4 +1,4 @@
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
@JavaOnlyStableParcelable parcelable Dimensions;
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/GotoLink.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/GotoLink.aidl
new file mode 100644
index 0000000..060bd91
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/GotoLink.aidl
@@ -0,0 +1,4 @@
+package androidx.pdf.models;
+
+@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
+@JavaOnlyStableParcelable parcelable GotoLink;
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/GotoLinkDestination.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/GotoLinkDestination.aidl
new file mode 100644
index 0000000..86c00c7
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/GotoLinkDestination.aidl
@@ -0,0 +1,4 @@
+package androidx.pdf.models;
+
+@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
+@JavaOnlyStableParcelable parcelable GotoLinkDestination;
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/LinkRects.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/LinkRects.aidl
similarity index 84%
rename from pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/LinkRects.aidl
rename to pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/LinkRects.aidl
index ebdca9a..a0b6cf1 100644
--- a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/LinkRects.aidl
+++ b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/LinkRects.aidl
@@ -1,4 +1,4 @@
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
@JavaOnlyStableParcelable parcelable LinkRects;
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/MatchRects.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/MatchRects.aidl
similarity index 84%
rename from pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/MatchRects.aidl
rename to pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/MatchRects.aidl
index 243db41..cdc81375 100644
--- a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/MatchRects.aidl
+++ b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/MatchRects.aidl
@@ -1,4 +1,4 @@
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
@JavaOnlyStableParcelable parcelable MatchRects;
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/PageSelection.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PageSelection.aidl
similarity index 84%
rename from pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/PageSelection.aidl
rename to pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PageSelection.aidl
index 053a22b..e0e5a55 100644
--- a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/PageSelection.aidl
+++ b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PageSelection.aidl
@@ -1,4 +1,4 @@
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
@JavaOnlyStableParcelable parcelable PageSelection;
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PdfDocumentRemote.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PdfDocumentRemote.aidl
new file mode 100644
index 0000000..7f09642
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PdfDocumentRemote.aidl
@@ -0,0 +1,45 @@
+package androidx.pdf.models;
+
+import android.graphics.Rect;
+import android.graphics.Bitmap;
+
+import android.os.ParcelFileDescriptor;
+
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.GotoLink;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.SelectionBoundary;
+import androidx.pdf.models.LinkRects;
+
+/** Remote interface around a PdfDocument. */
+@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
+interface PdfDocumentRemote {
+ int create(in ParcelFileDescriptor pfd, String password);
+
+ int numPages();
+ Dimensions getPageDimensions(int pageNum);
+
+ Bitmap renderPage(int pageNum, int pageWidth, int pageHeight, boolean hideTextAnnots);
+ Bitmap renderTile(int pageNum, int tileWidth, int tileHeight, int scaledPageWidth,
+ int scaledPageHeight, int left, int top, boolean hideTextAnnots);
+
+ String getPageText(int pageNum);
+ List<String> getPageAltText(int pageNum);
+
+ MatchRects searchPageText(int pageNum, String query);
+ PageSelection selectPageText(int pageNum, in SelectionBoundary start, in SelectionBoundary stop);
+
+ LinkRects getPageLinks(int pageNum);
+
+ List<GotoLink> getPageGotoLinks(int pageNum);
+
+ boolean isPdfLinearized();
+ int getFormType();
+
+ boolean cloneWithoutSecurity(in ParcelFileDescriptor destination);
+
+ boolean saveAs(in ParcelFileDescriptor destination);
+
+ // The PdfDocument is destroyed when this service is destroyed.
+}
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/SelectionBoundary.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/SelectionBoundary.aidl
similarity index 84%
rename from pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/SelectionBoundary.aidl
rename to pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/SelectionBoundary.aidl
index 2cb0a5b..c8bc815 100644
--- a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/aidl/SelectionBoundary.aidl
+++ b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/SelectionBoundary.aidl
@@ -1,4 +1,4 @@
-package androidx.pdf.aidl;
+package androidx.pdf.models;
@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
@JavaOnlyStableParcelable parcelable SelectionBoundary;
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/LinkRectsTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/LinkRectsTest.java
index 9f93dc8..3c3ebdb 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/LinkRectsTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/LinkRectsTest.java
@@ -16,17 +16,20 @@
package androidx.pdf.aidl;
-
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
+
import android.graphics.Rect;
+import androidx.pdf.models.LinkRects;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -54,6 +57,13 @@
}
@Test
+ public void testGetUrl_returnsUrlCorrespondingToLink() {
+ assertThat(mLinkRects.getUrl(0)).isEqualTo("http://first.com");
+ assertThat(mLinkRects.getUrl(1)).isEqualTo("http://second.org");
+ assertThat(mLinkRects.getUrl(2)).isEqualTo("http://third.net");
+ }
+
+ @Test
public void testGetUrlAtPoint() {
assertThat(mLinkRects.getUrlAtPoint(100, 100)).isEqualTo("http://first.com");
assertThat(mLinkRects.getUrlAtPoint(200, 201)).isEqualTo("http://first.com");
@@ -66,6 +76,23 @@
assertThat(mLinkRects.getUrlAtPoint(510, 500)).isNull();
}
+ @Test
+ public void testClassFields_flagsFieldModification() {
+ List<String> fields = new ArrayList<>();
+ fields.add("NO_LINKS");
+ fields.add("CREATOR");
+ fields.add("mRects");
+ fields.add("mLinkToRect");
+ fields.add("mUrls");
+
+ List<String> declaredFields = new ArrayList<>();
+ for (Field field : LinkRects.class.getDeclaredFields()) {
+ declaredFields.add(field.getName());
+ }
+
+ assertTrue(fields.containsAll(declaredFields));
+ }
+
private static LinkRects createLinkRects(int numRects, Integer[] linkToRect, String[] urls) {
List<Rect> rects = new ArrayList<Rect>();
for (int i = 1; i <= numRects; i++) {
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/MatchRectsTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/MatchRectsTest.java
index fd27016..059e0b2 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/MatchRectsTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/MatchRectsTest.java
@@ -16,17 +16,20 @@
package androidx.pdf.aidl;
-
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
+
import android.graphics.Rect;
+import androidx.pdf.models.MatchRects;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -51,6 +54,27 @@
}
@Test
+ public void testGetCharIndex_returnsIndexCorrespondingToMatch() {
+ assertThat(mMatchRects.getCharIndex(0)).isEqualTo(0);
+ assertThat(mMatchRects.getCharIndex(1)).isEqualTo(10);
+ assertThat(mMatchRects.getCharIndex(2)).isEqualTo(20);
+ }
+
+ @Test
+ public void testGetMatchNearestCharIndex_returnsMatchIndexCorrespondingToCharIndex() {
+ assertThat(mMatchRects.getMatchNearestCharIndex(0)).isEqualTo(0);
+ assertThat(mMatchRects.getMatchNearestCharIndex(10)).isEqualTo(1);
+ assertThat(mMatchRects.getMatchNearestCharIndex(20)).isEqualTo(2);
+ }
+
+ @Test
+ public void testGetFirstRect_returnsFirstRectForMatch() {
+ assertThat(mMatchRects.getFirstRect(0)).isEqualTo(new Rect(0, 0, 0, 0));
+ assertThat(mMatchRects.getFirstRect(1)).isEqualTo(new Rect(200, 200, 202, 202));
+ assertThat(mMatchRects.getFirstRect(2)).isEqualTo(new Rect(300, 300, 303, 303));
+ }
+
+ @Test
public void testFlatten() {
List<Rect> rects = mMatchRects.flatten();
assertThat(rects.size()).isEqualTo(5);
@@ -60,6 +84,31 @@
assertThat(mMatchRects.flattenExcludingMatch(1)).isEqualTo(rectsExcludingMatchOne);
}
+ @Test
+ public void testRectsExcludingMatchOne_returnsFlatListOfRectsForAllMatchesExceptGivenMatch() {
+ List<Rect> rects = mMatchRects.flatten();
+ List<Rect> rectsExcludingMatchOne = Arrays.asList(rects.get(0), rects.get(1), rects.get(3),
+ rects.get(4));
+ assertThat(mMatchRects.flattenExcludingMatch(1)).isEqualTo(rectsExcludingMatchOne);
+ }
+
+ @Test
+ public void testClassFields_flagsFieldModification() {
+ List<String> fields = new ArrayList<>();
+ fields.add("NO_MATCHES");
+ fields.add("CREATOR");
+ fields.add("mRects");
+ fields.add("mMatchToRect");
+ fields.add("mCharIndexes");
+
+ List<String> declaredFields = new ArrayList<>();
+ for (Field field : MatchRects.class.getDeclaredFields()) {
+ declaredFields.add(field.getName());
+ }
+
+ assertTrue(fields.containsAll(declaredFields));
+ }
+
private static MatchRects createMatchRects(int numRects, Integer... matchToRect) {
List<Rect> rects = new ArrayList<>();
List<Integer> charIndexes = new ArrayList<>();
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/SelectionBoundaryTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/SelectionBoundaryTest.java
new file mode 100644
index 0000000..7ac3ddc
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/aidl/SelectionBoundaryTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 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.pdf.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Point;
+
+import androidx.pdf.models.SelectionBoundary;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+public class SelectionBoundaryTest {
+
+ @Test
+ public void testAtIndex_selectionBoundaryCreatedWithCorrectValues() {
+ assertThat(SelectionBoundary.atIndex(4)).isEqualTo(new SelectionBoundary(4, -1, -1, false));
+ }
+ @Test
+ public void testAtPoint_selectionBoundaryCreatedWithCorrectValues() {
+ assertThat(SelectionBoundary.atPoint(new Point(3, 4))).isEqualTo(
+ new SelectionBoundary(-1, 3, 4, false));
+ }
+
+ @Test
+ public void testAtPoint_pointContainsXAndY_selectionBoundaryCreatedWithCorrectValues() {
+ assertThat(SelectionBoundary.atPoint(1, 2)).isEqualTo(
+ new SelectionBoundary(-1, 1, 2, false));
+ }
+ @Test
+ public void testClassFields() {
+ List<String> fields = new ArrayList<>();
+ fields.add("PAGE_START");
+ fields.add("PAGE_END");
+ fields.add("CREATOR");
+ fields.add("mIndex");
+ fields.add("mX");
+ fields.add("mY");
+ fields.add("mIsRtl");
+
+ List<String> declaredFields = new ArrayList<>();
+ for (Field field : SelectionBoundary.class.getDeclaredFields()) {
+ declaredFields.add(field.getName());
+ }
+
+ assertTrue(fields.containsAll(declaredFields));
+ }
+}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/data/ContentOpenableTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/data/ContentOpenableTest.java
index 99a7cfe..bd49042 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/data/ContentOpenableTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/data/ContentOpenableTest.java
@@ -21,7 +21,7 @@
import android.net.Uri;
import android.os.Parcel;
-import androidx.pdf.aidl.Dimensions;
+import androidx.pdf.models.Dimensions;
import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/util/TileBoardTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/util/TileBoardTest.java
index 8ff6447..61e39e0 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/util/TileBoardTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/util/TileBoardTest.java
@@ -27,7 +27,7 @@
import android.graphics.Point;
import android.graphics.Rect;
-import androidx.pdf.aidl.Dimensions;
+import androidx.pdf.models.Dimensions;
import androidx.pdf.util.TileBoard.CancelTilesCallback;
import androidx.pdf.util.TileBoard.TileInfo;
import androidx.pdf.util.TileBoard.ViewAreaUpdateCallback;
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginatedViewTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginatedViewTest.java
index 3f01e68..53f5c10 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginatedViewTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginatedViewTest.java
@@ -20,7 +20,7 @@
import android.content.Context;
-import androidx.pdf.aidl.Dimensions;
+import androidx.pdf.models.Dimensions;
import androidx.pdf.util.BitmapRecycler;
import androidx.pdf.util.ProjectorContext;
import androidx.pdf.viewer.PageViewFactory.PageView;
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java
index 8997889..4303ef7 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java
@@ -22,8 +22,8 @@
import android.content.Context;
import android.graphics.Rect;
-import androidx.pdf.aidl.Dimensions;
import androidx.pdf.data.Range;
+import androidx.pdf.models.Dimensions;
import androidx.pdf.util.ProjectorContext;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SearchModelTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SearchModelTest.java
index 5242d1a..293eeb4 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SearchModelTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SearchModelTest.java
@@ -22,8 +22,8 @@
import android.graphics.Rect;
-import androidx.pdf.aidl.MatchRects;
import androidx.pdf.find.MatchCount;
+import androidx.pdf.models.MatchRects;
import androidx.pdf.viewer.loader.PdfLoader;
import androidx.test.filters.SmallTest;
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SelectedMatchTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SelectedMatchTest.java
index 142c74d..6d37daf 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SelectedMatchTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SelectedMatchTest.java
@@ -23,7 +23,7 @@
import android.graphics.Rect;
-import androidx.pdf.aidl.MatchRects;
+import androidx.pdf.models.MatchRects;
import androidx.test.filters.SmallTest;
import org.junit.Test;
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/MosaicViewTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/MosaicViewTest.java
index e872f88..8fe7eba 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/MosaicViewTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/MosaicViewTest.java
@@ -38,7 +38,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.pdf.aidl.Dimensions;
+import androidx.pdf.models.Dimensions;
import androidx.pdf.util.BitmapRecycler;
import androidx.pdf.util.TileBoard;
import androidx.pdf.util.TileBoard.TileInfo;
diff --git a/playground-common/playground-plugin/build.gradle b/playground-common/playground-plugin/build.gradle
index cc6735b..c5b8bb8 100644
--- a/playground-common/playground-plugin/build.gradle
+++ b/playground-common/playground-plugin/build.gradle
@@ -21,8 +21,8 @@
dependencies {
implementation(project(":shared"))
- implementation("com.gradle:gradle-enterprise-gradle-plugin:3.16")
- implementation("com.gradle:common-custom-user-data-gradle-plugin:1.12")
+ implementation("com.gradle:develocity-gradle-plugin:3.17.2")
+ implementation("com.gradle:common-custom-user-data-gradle-plugin:2.0.1")
implementation("supportBuildSrc:private")
implementation("supportBuildSrc:public")
implementation("supportBuildSrc:plugins")
@@ -37,8 +37,8 @@
implementationClass = "androidx.playground.PlaygroundPlugin"
}
gradleEnterpriseConventions {
- id = "playground-ge-conventions"
- implementationClass = "androidx.playground.GradleEnterpriseConventionsPlugin"
+ id = "playground-develocity-conventions"
+ implementationClass = "androidx.playground.GradleDevelocityConventionsPlugin"
}
}
}
diff --git a/playground-common/playground-plugin/settings.gradle b/playground-common/playground-plugin/settings.gradle
index 1c6802c..9680ada 100644
--- a/playground-common/playground-plugin/settings.gradle
+++ b/playground-common/playground-plugin/settings.gradle
@@ -29,7 +29,7 @@
mavenCentral()
google()
gradlePluginPortal().content {
- it.includeModule("com.gradle", "gradle-enterprise-gradle-plugin")
+ it.includeModule("com.gradle", "develocity-gradle-plugin")
it.includeModule("com.gradle", "common-custom-user-data-gradle-plugin")
it.includeModule("org.spdx", "spdx-gradle-plugin")
it.includeModule("com.github.johnrengelman.shadow",
diff --git a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleDevelocityConventionsPlugin.kt
similarity index 82%
rename from playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt
rename to playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleDevelocityConventionsPlugin.kt
index aa5351f..08b00ca 100644
--- a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt
+++ b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleDevelocityConventionsPlugin.kt
@@ -16,34 +16,32 @@
package androidx.playground
-import com.gradle.enterprise.gradleplugin.internal.extension.BuildScanExtensionWithHiddenFeatures
-import org.gradle.api.Plugin
-import org.gradle.api.initialization.Settings
-import org.gradle.caching.http.HttpBuildCache
-import org.gradle.kotlin.dsl.gradleEnterprise
import java.net.InetAddress
import java.net.URI
import java.util.function.Function
+import org.gradle.api.Plugin
+import org.gradle.api.initialization.Settings
+import org.gradle.caching.http.HttpBuildCache
+import org.gradle.kotlin.dsl.develocity
-class GradleEnterpriseConventionsPlugin : Plugin<Settings> {
+class GradleDevelocityConventionsPlugin : Plugin<Settings> {
override fun apply(settings: Settings) {
- settings.apply(mapOf("plugin" to "com.gradle.enterprise"))
+ settings.apply(mapOf("plugin" to "com.gradle.develocity"))
settings.apply(mapOf("plugin" to "com.gradle.common-custom-user-data-gradle-plugin"))
// Github Actions always sets a "CI" environment variable
val isCI = System.getenv("CI") != null
- settings.gradleEnterprise {
- server = "https://ge.androidx.dev"
-
+ settings.develocity {
+ server.set("https://ge.androidx.dev")
buildScan.apply {
- publishAlways()
- (this as BuildScanExtensionWithHiddenFeatures).publishIfAuthenticated()
- isUploadInBackground = !isCI
- capture.isTaskInputFiles = true
-
+ uploadInBackground.set(!isCI)
+ capture.fileFingerprints.set(true)
obfuscation.hostname(HostnameHider())
obfuscation.ipAddresses(IpAddressHider())
+ publishing.onlyIf {
+ it.isAuthenticated
+ }
}
}
@@ -88,4 +86,4 @@
return listOf("0.0.0.0")
}
}
-}
\ No newline at end of file
+}
diff --git a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundPlugin.kt b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundPlugin.kt
index b6960f9..22c1f44 100644
--- a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundPlugin.kt
+++ b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundPlugin.kt
@@ -21,7 +21,7 @@
class PlaygroundPlugin : Plugin<Settings> {
override fun apply(settings: Settings) {
- settings.apply(mapOf("plugin" to "playground-ge-conventions"))
+ settings.apply(mapOf("plugin" to "playground-develocity-conventions"))
settings.extensions.create("playground", PlaygroundExtension::class.java, settings)
validateJvm(settings)
}
diff --git a/preference/preference-ktx/build.gradle b/preference/preference-ktx/build.gradle
index eaca3dc..b76c3a6 100644
--- a/preference/preference-ktx/build.gradle
+++ b/preference/preference-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -51,7 +51,7 @@
androidx {
name = "Preferences KTX"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for preferences"
metalavaK2UastEnabled = true
diff --git a/preference/preference/lint-baseline.xml b/preference/preference/lint-baseline.xml
index 7cbbdb5..50a07bf 100644
--- a/preference/preference/lint-baseline.xml
+++ b/preference/preference/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -11,6 +11,29 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/preference/PreferenceCategory.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `drawable`.">
+ <location
+ file="res/drawable-v21"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="res/values-v21"/>
+ </issue>
+
+ <issue
id="KotlinPropertyAccess"
message="This getter should be public such that `onBindEditTextListener` can be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
errorLine1=" @Nullable OnBindEditTextListener getOnBindEditTextListener() {"
diff --git a/print/print/lint-baseline.xml b/print/print/lint-baseline.xml
new file mode 100644
index 0000000..8002622
--- /dev/null
+++ b/print/print/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" Build.VERSION.SDK_INT < 20 || Build.VERSION.SDK_INT > 23;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/print/PrintHelper.java"/>
+ </issue>
+
+</issues>
diff --git a/privacysandbox/ads/ads-adservices-java/api/1.1.0-beta07.txt b/privacysandbox/ads/ads-adservices-java/api/1.1.0-beta07.txt
new file mode 100644
index 0000000..232abf5
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/1.1.0-beta07.txt
@@ -0,0 +1,99 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+ public abstract class AdIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AdIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+ public abstract class AdSelectionManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataOutcome> getAdSelectionDataAsync(androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataRequest getAdSelectionDataRequest);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> persistAdSelectionResultAsync(androidx.privacysandbox.ads.adservices.adselection.PersistAdSelectionResultRequest persistAdSelectionResultRequest);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportEventAsync(androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest reportEventRequest);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> updateAdCounterHistogramAsync(androidx.privacysandbox.ads.adservices.adselection.UpdateAdCounterHistogramRequest updateAdCounterHistogramRequest);
+ field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+ }
+
+ public static final class AdSelectionManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+ public abstract class AppSetIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AppSetIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+ public abstract class CustomAudienceManagerFutures {
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> fetchAndJoinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.FetchAndJoinCustomAudienceRequest request);
+ method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+ }
+
+ public static final class CustomAudienceManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+ public abstract class MeasurementManagerFutures {
+ method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+ method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+ }
+
+ public static final class MeasurementManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+ public abstract class TopicsManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+ }
+
+ public static final class TopicsManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ }
+
+}
+
diff --git a/graphics/graphics-shapes/api/res-1.0.0-beta02.txt b/privacysandbox/ads/ads-adservices-java/api/res-1.1.0-beta07.txt
similarity index 100%
copy from graphics/graphics-shapes/api/res-1.0.0-beta02.txt
copy to privacysandbox/ads/ads-adservices-java/api/res-1.1.0-beta07.txt
diff --git a/privacysandbox/ads/ads-adservices-java/api/restricted_1.1.0-beta07.txt b/privacysandbox/ads/ads-adservices-java/api/restricted_1.1.0-beta07.txt
new file mode 100644
index 0000000..232abf5
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/restricted_1.1.0-beta07.txt
@@ -0,0 +1,99 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+ public abstract class AdIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AdIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+ public abstract class AdSelectionManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataOutcome> getAdSelectionDataAsync(androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataRequest getAdSelectionDataRequest);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> persistAdSelectionResultAsync(androidx.privacysandbox.ads.adservices.adselection.PersistAdSelectionResultRequest persistAdSelectionResultRequest);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportEventAsync(androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest reportEventRequest);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> updateAdCounterHistogramAsync(androidx.privacysandbox.ads.adservices.adselection.UpdateAdCounterHistogramRequest updateAdCounterHistogramRequest);
+ field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+ }
+
+ public static final class AdSelectionManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+ public abstract class AppSetIdManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+ field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+ }
+
+ public static final class AppSetIdManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+ public abstract class CustomAudienceManagerFutures {
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> fetchAndJoinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.FetchAndJoinCustomAudienceRequest request);
+ method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+ }
+
+ public static final class CustomAudienceManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+ public abstract class MeasurementManagerFutures {
+ method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+ method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+ }
+
+ public static final class MeasurementManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+ public abstract class TopicsManagerFutures {
+ method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+ field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+ }
+
+ public static final class TopicsManagerFutures.Companion {
+ method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices-java/lint-baseline.xml b/privacysandbox/ads/ads-adservices-java/lint-baseline.xml
index 737aa13..afd6f01 100644
--- a/privacysandbox/ads/ads-adservices-java/lint-baseline.xml
+++ b/privacysandbox/ads/ads-adservices-java/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -16,15 +16,6 @@
errorLine1=" Thread.sleep(100);"
errorLine2=" ~~~~~">
<location
- file="src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/appsetid/AppSetIdManagerTest.java"/>
- </issue>
-
- <issue
- id="BanThreadSleep"
- message="Uses Thread.sleep()"
- errorLine1=" Thread.sleep(100);"
- errorLine2=" ~~~~~">
- <location
file="src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java"/>
</issue>
diff --git a/privacysandbox/ads/ads-adservices/api/1.1.0-beta07.txt b/privacysandbox/ads/ads-adservices/api/1.1.0-beta07.txt
new file mode 100644
index 0000000..7574be5
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/1.1.0-beta07.txt
@@ -0,0 +1,530 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+ public final class AdId {
+ method public String getAdId();
+ method public boolean isLimitAdTrackingEnabled();
+ property public final String adId;
+ property public final boolean isLimitAdTrackingEnabled;
+ }
+
+ public abstract class AdIdManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+ method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+ }
+
+ public static final class AdIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+ public final class AdSelectionConfig {
+ ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+ method public android.net.Uri getDecisionLogicUri();
+ method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+ method public android.net.Uri getTrustedScoringSignalsUri();
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+ property public final android.net.Uri decisionLogicUri;
+ property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+ property public final android.net.Uri trustedScoringSignalsUri;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class AdSelectionFromOutcomesConfig {
+ ctor public AdSelectionFromOutcomesConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, java.util.List<java.lang.Long> adSelectionIds, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, android.net.Uri selectionLogicUri);
+ method public java.util.List<java.lang.Long> getAdSelectionIds();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+ method public android.net.Uri getSelectionLogicUri();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+ method public void setSelectionLogicUri(android.net.Uri);
+ property public final java.util.List<java.lang.Long> adSelectionIds;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+ property public final android.net.Uri selectionLogicUri;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+ }
+
+ public abstract class AdSelectionManager {
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? getAdSelectionData(androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataRequest getAdSelectionDataRequest, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataOutcome>);
+ method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? persistAdSelectionResult(androidx.privacysandbox.ads.adservices.adselection.PersistAdSelectionResultRequest persistAdSelectionResultRequest, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract suspend Object? reportEvent(androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest reportEventRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract suspend Object? updateAdCounterHistogram(androidx.privacysandbox.ads.adservices.adselection.UpdateAdCounterHistogramRequest updateAdCounterHistogramRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+ }
+
+ public static final class AdSelectionManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ }
+
+ public final class AdSelectionOutcome {
+ ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+ method public long getAdSelectionId();
+ method public android.net.Uri getRenderUri();
+ method @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public boolean hasOutcome();
+ property public final long adSelectionId;
+ property public final android.net.Uri renderUri;
+ field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome.Companion Companion;
+ field @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome NO_OUTCOME;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public static final class AdSelectionOutcome.Companion {
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataOutcome {
+ ctor public GetAdSelectionDataOutcome(long adSelectionId, optional byte[]? adSelectionData);
+ method public byte[]? getAdSelectionData();
+ method public long getAdSelectionId();
+ property public final byte[]? adSelectionData;
+ property public final long adSelectionId;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataRequest {
+ ctor public GetAdSelectionDataRequest(optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller);
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? getSeller();
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class PersistAdSelectionResultRequest {
+ ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller, optional byte[]? adSelectionResult);
+ method public long getAdSelectionId();
+ method public byte[]? getAdSelectionResult();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? getSeller();
+ property public final long adSelectionId;
+ property public final byte[]? adSelectionResult;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class ReportEventRequest {
+ ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations, optional android.view.InputEvent? inputEvent);
+ method public long getAdSelectionId();
+ method public String getEventData();
+ method public String getEventKey();
+ method public android.view.InputEvent? getInputEvent();
+ method public int getReportingDestinations();
+ property public final long adSelectionId;
+ property public final String eventData;
+ property public final String eventKey;
+ property public final android.view.InputEvent? inputEvent;
+ property public final int reportingDestinations;
+ field public static final androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest.Companion Companion;
+ field public static final int FLAG_REPORTING_DESTINATION_BUYER = 2; // 0x2
+ field public static final int FLAG_REPORTING_DESTINATION_SELLER = 1; // 0x1
+ }
+
+ public static final class ReportEventRequest.Companion {
+ }
+
+ public final class ReportImpressionRequest {
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public ReportImpressionRequest(long adSelectionId);
+ ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+ method public long getAdSelectionId();
+ property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+ property public final long adSelectionId;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class UpdateAdCounterHistogramRequest {
+ ctor public UpdateAdCounterHistogramRequest(long adSelectionId, int adEventType, androidx.privacysandbox.ads.adservices.common.AdTechIdentifier callerAdTech);
+ method public int getAdEventType();
+ method public long getAdSelectionId();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getCallerAdTech();
+ property public final int adEventType;
+ property public final long adSelectionId;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier callerAdTech;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+ public final class AppSetId {
+ ctor public AppSetId(String id, int scope);
+ method public String getId();
+ method public int getScope();
+ property public final String id;
+ property public final int scope;
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+ field public static final int SCOPE_APP = 1; // 0x1
+ field public static final int SCOPE_DEVELOPER = 2; // 0x2
+ }
+
+ public static final class AppSetId.Companion {
+ }
+
+ public abstract class AppSetIdManager {
+ method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+ method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+ }
+
+ public static final class AppSetIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+ public final class AdData {
+ ctor public AdData(android.net.Uri renderUri, String metadata);
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public AdData(android.net.Uri renderUri, String metadata, optional java.util.Set<java.lang.Integer> adCounterKeys, optional androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters);
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public AdData(android.net.Uri renderUri, String metadata, optional java.util.Set<java.lang.Integer> adCounterKeys, optional androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters, optional String? adRenderId);
+ method public java.util.Set<java.lang.Integer> getAdCounterKeys();
+ method public androidx.privacysandbox.ads.adservices.common.AdFilters? getAdFilters();
+ method public String? getAdRenderId();
+ method public String getMetadata();
+ method public android.net.Uri getRenderUri();
+ property public final java.util.Set<java.lang.Integer> adCounterKeys;
+ property public final androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters;
+ property public final String? adRenderId;
+ property public final String metadata;
+ property public final android.net.Uri renderUri;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class AdFilters {
+ ctor public AdFilters(androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? frequencyCapFilters);
+ method public androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? getFrequencyCapFilters();
+ property public final androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? frequencyCapFilters;
+ }
+
+ public final class AdSelectionSignals {
+ ctor public AdSelectionSignals(String signals);
+ method public String getSignals();
+ property public final String signals;
+ }
+
+ public final class AdTechIdentifier {
+ ctor public AdTechIdentifier(String identifier);
+ method public String getIdentifier();
+ property public final String identifier;
+ }
+
+ public sealed interface ExperimentalFeatures {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext10 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext10OptIn {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext11 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext11OptIn {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext8 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext8OptIn {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class FrequencyCapFilters {
+ ctor public FrequencyCapFilters();
+ ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents);
+ ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents);
+ ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents);
+ ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForClickEvents);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForClickEvents();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForImpressionEvents();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForViewEvents();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForWinEvents();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForClickEvents;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents;
+ field public static final int AD_EVENT_TYPE_CLICK = 3; // 0x3
+ field public static final int AD_EVENT_TYPE_IMPRESSION = 1; // 0x1
+ field public static final int AD_EVENT_TYPE_VIEW = 2; // 0x2
+ field public static final int AD_EVENT_TYPE_WIN = 0; // 0x0
+ field public static final androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters.Companion Companion;
+ }
+
+ public static final class FrequencyCapFilters.Companion {
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class KeyedFrequencyCap {
+ ctor public KeyedFrequencyCap(int adCounterKey, int maxCount, java.time.Duration interval);
+ method public int getAdCounterKey();
+ method public java.time.Duration getInterval();
+ method public int getMaxCount();
+ property public final int adCounterKey;
+ property public final java.time.Duration interval;
+ property public final int maxCount;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+ public final class CustomAudience {
+ ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+ method public java.time.Instant? getActivationTime();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+ method public android.net.Uri getBiddingLogicUri();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public android.net.Uri getDailyUpdateUri();
+ method public java.time.Instant? getExpirationTime();
+ method public String getName();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+ property public final java.time.Instant? activationTime;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+ property public final android.net.Uri biddingLogicUri;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final android.net.Uri dailyUpdateUri;
+ property public final java.time.Instant? expirationTime;
+ property public final String name;
+ property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+ }
+
+ public static final class CustomAudience.Builder {
+ ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+ }
+
+ public abstract class CustomAudienceManager {
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? fetchAndJoinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.FetchAndJoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+ }
+
+ public static final class CustomAudienceManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class FetchAndJoinCustomAudienceRequest {
+ ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals);
+ method public java.time.Instant? getActivationTime();
+ method public java.time.Instant? getExpirationTime();
+ method public android.net.Uri getFetchUri();
+ method public String? getName();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+ property public final java.time.Instant? activationTime;
+ property public final java.time.Instant? expirationTime;
+ property public final android.net.Uri fetchUri;
+ property public final String? name;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+ }
+
+ public final class JoinCustomAudienceRequest {
+ ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+ property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+ }
+
+ public final class LeaveCustomAudienceRequest {
+ ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public String getName();
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final String name;
+ }
+
+ public final class TrustedBiddingData {
+ ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+ method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+ method public android.net.Uri getTrustedBiddingUri();
+ property public final java.util.List<java.lang.String> trustedBiddingKeys;
+ property public final android.net.Uri trustedBiddingUri;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class DeletionRequest {
+ ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+ method public int getDeletionMode();
+ method public java.util.List<android.net.Uri> getDomainUris();
+ method public java.time.Instant getEnd();
+ method public int getMatchBehavior();
+ method public java.util.List<android.net.Uri> getOriginUris();
+ method public java.time.Instant getStart();
+ property public final int deletionMode;
+ property public final java.util.List<android.net.Uri> domainUris;
+ property public final java.time.Instant end;
+ property public final int matchBehavior;
+ property public final java.util.List<android.net.Uri> originUris;
+ property public final java.time.Instant start;
+ field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+ field public static final int DELETION_MODE_ALL = 0; // 0x0
+ field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+ field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+ field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public static final class DeletionRequest.Builder {
+ ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+ }
+
+ public static final class DeletionRequest.Companion {
+ }
+
+ public abstract class MeasurementManager {
+ ctor public MeasurementManager();
+ method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+ method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+ field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+ field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+ }
+
+ public static final class MeasurementManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+ ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+ method public android.view.InputEvent? getInputEvent();
+ method public java.util.List<android.net.Uri> getRegistrationUris();
+ property public final android.view.InputEvent? inputEvent;
+ property public final java.util.List<android.net.Uri> registrationUris;
+ }
+
+ public static final class SourceRegistrationRequest.Builder {
+ ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ }
+
+ public final class WebSourceParams {
+ ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ public final class WebSourceRegistrationRequest {
+ ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+ method public android.net.Uri? getAppDestination();
+ method public android.view.InputEvent? getInputEvent();
+ method public android.net.Uri getTopOriginUri();
+ method public android.net.Uri? getVerifiedDestination();
+ method public android.net.Uri? getWebDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+ property public final android.net.Uri? appDestination;
+ property public final android.view.InputEvent? inputEvent;
+ property public final android.net.Uri topOriginUri;
+ property public final android.net.Uri? verifiedDestination;
+ property public final android.net.Uri? webDestination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+ }
+
+ public static final class WebSourceRegistrationRequest.Builder {
+ ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+ }
+
+ public final class WebTriggerParams {
+ ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ public final class WebTriggerRegistrationRequest {
+ ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+ method public android.net.Uri getDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+ property public final android.net.Uri destination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext11OptIn public final class EncryptedTopic {
+ ctor public EncryptedTopic(byte[] encryptedTopic, String keyIdentifier, byte[] encapsulatedKey);
+ method public byte[] getEncapsulatedKey();
+ method public byte[] getEncryptedTopic();
+ method public String getKeyIdentifier();
+ property public final byte[] encapsulatedKey;
+ property public final byte[] encryptedTopic;
+ property public final String keyIdentifier;
+ }
+
+ public final class GetTopicsRequest {
+ ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+ method public String getAdsSdkName();
+ method public boolean shouldRecordObservation();
+ property public final String adsSdkName;
+ property public final boolean shouldRecordObservation;
+ }
+
+ public static final class GetTopicsRequest.Builder {
+ ctor public GetTopicsRequest.Builder();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+ }
+
+ public final class GetTopicsResponse {
+ ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext11OptIn public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics, java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> encryptedTopics);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> getEncryptedTopics();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> encryptedTopics;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+ }
+
+ public final class Topic {
+ ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+ method public long getModelVersion();
+ method public long getTaxonomyVersion();
+ method public int getTopicId();
+ property public final long modelVersion;
+ property public final long taxonomyVersion;
+ property public final int topicId;
+ }
+
+ public abstract class TopicsManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+ method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+ }
+
+ public static final class TopicsManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/current.txt b/privacysandbox/ads/ads-adservices/api/current.txt
index 32149c9..7574be5 100644
--- a/privacysandbox/ads/ads-adservices/api/current.txt
+++ b/privacysandbox/ads/ads-adservices/api/current.txt
@@ -218,6 +218,9 @@
@SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext10 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext10OptIn {
}
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext11 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext11OptIn {
+ }
+
@SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext8 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext8OptIn {
}
@@ -469,6 +472,16 @@
package androidx.privacysandbox.ads.adservices.topics {
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext11OptIn public final class EncryptedTopic {
+ ctor public EncryptedTopic(byte[] encryptedTopic, String keyIdentifier, byte[] encapsulatedKey);
+ method public byte[] getEncapsulatedKey();
+ method public byte[] getEncryptedTopic();
+ method public String getKeyIdentifier();
+ property public final byte[] encapsulatedKey;
+ property public final byte[] encryptedTopic;
+ property public final String keyIdentifier;
+ }
+
public final class GetTopicsRequest {
ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
method public String getAdsSdkName();
@@ -486,7 +499,10 @@
public final class GetTopicsResponse {
ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext11OptIn public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics, java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> encryptedTopics);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> getEncryptedTopics();
method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> encryptedTopics;
property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
}
diff --git a/graphics/graphics-shapes/api/res-1.0.0-beta02.txt b/privacysandbox/ads/ads-adservices/api/res-1.1.0-beta07.txt
similarity index 100%
copy from graphics/graphics-shapes/api/res-1.0.0-beta02.txt
copy to privacysandbox/ads/ads-adservices/api/res-1.1.0-beta07.txt
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta07.txt b/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta07.txt
new file mode 100644
index 0000000..7574be5
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta07.txt
@@ -0,0 +1,530 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+ public final class AdId {
+ method public String getAdId();
+ method public boolean isLimitAdTrackingEnabled();
+ property public final String adId;
+ property public final boolean isLimitAdTrackingEnabled;
+ }
+
+ public abstract class AdIdManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+ method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+ }
+
+ public static final class AdIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+ public final class AdSelectionConfig {
+ ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+ method public android.net.Uri getDecisionLogicUri();
+ method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+ method public android.net.Uri getTrustedScoringSignalsUri();
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+ property public final android.net.Uri decisionLogicUri;
+ property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+ property public final android.net.Uri trustedScoringSignalsUri;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class AdSelectionFromOutcomesConfig {
+ ctor public AdSelectionFromOutcomesConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, java.util.List<java.lang.Long> adSelectionIds, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, android.net.Uri selectionLogicUri);
+ method public java.util.List<java.lang.Long> getAdSelectionIds();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+ method public android.net.Uri getSelectionLogicUri();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+ method public void setSelectionLogicUri(android.net.Uri);
+ property public final java.util.List<java.lang.Long> adSelectionIds;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+ property public final android.net.Uri selectionLogicUri;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+ }
+
+ public abstract class AdSelectionManager {
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? getAdSelectionData(androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataRequest getAdSelectionDataRequest, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.GetAdSelectionDataOutcome>);
+ method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? persistAdSelectionResult(androidx.privacysandbox.ads.adservices.adselection.PersistAdSelectionResultRequest persistAdSelectionResultRequest, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract suspend Object? reportEvent(androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest reportEventRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public abstract suspend Object? updateAdCounterHistogram(androidx.privacysandbox.ads.adservices.adselection.UpdateAdCounterHistogramRequest updateAdCounterHistogramRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+ }
+
+ public static final class AdSelectionManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+ }
+
+ public final class AdSelectionOutcome {
+ ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+ method public long getAdSelectionId();
+ method public android.net.Uri getRenderUri();
+ method @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public boolean hasOutcome();
+ property public final long adSelectionId;
+ property public final android.net.Uri renderUri;
+ field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome.Companion Companion;
+ field @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome NO_OUTCOME;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public static final class AdSelectionOutcome.Companion {
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataOutcome {
+ ctor public GetAdSelectionDataOutcome(long adSelectionId, optional byte[]? adSelectionData);
+ method public byte[]? getAdSelectionData();
+ method public long getAdSelectionId();
+ property public final byte[]? adSelectionData;
+ property public final long adSelectionId;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataRequest {
+ ctor public GetAdSelectionDataRequest(optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller);
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? getSeller();
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class PersistAdSelectionResultRequest {
+ ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller, optional byte[]? adSelectionResult);
+ method public long getAdSelectionId();
+ method public byte[]? getAdSelectionResult();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? getSeller();
+ property public final long adSelectionId;
+ property public final byte[]? adSelectionResult;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class ReportEventRequest {
+ ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations, optional android.view.InputEvent? inputEvent);
+ method public long getAdSelectionId();
+ method public String getEventData();
+ method public String getEventKey();
+ method public android.view.InputEvent? getInputEvent();
+ method public int getReportingDestinations();
+ property public final long adSelectionId;
+ property public final String eventData;
+ property public final String eventKey;
+ property public final android.view.InputEvent? inputEvent;
+ property public final int reportingDestinations;
+ field public static final androidx.privacysandbox.ads.adservices.adselection.ReportEventRequest.Companion Companion;
+ field public static final int FLAG_REPORTING_DESTINATION_BUYER = 2; // 0x2
+ field public static final int FLAG_REPORTING_DESTINATION_SELLER = 1; // 0x1
+ }
+
+ public static final class ReportEventRequest.Companion {
+ }
+
+ public final class ReportImpressionRequest {
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public ReportImpressionRequest(long adSelectionId);
+ ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+ method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+ method public long getAdSelectionId();
+ property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+ property public final long adSelectionId;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class UpdateAdCounterHistogramRequest {
+ ctor public UpdateAdCounterHistogramRequest(long adSelectionId, int adEventType, androidx.privacysandbox.ads.adservices.common.AdTechIdentifier callerAdTech);
+ method public int getAdEventType();
+ method public long getAdSelectionId();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getCallerAdTech();
+ property public final int adEventType;
+ property public final long adSelectionId;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier callerAdTech;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+ public final class AppSetId {
+ ctor public AppSetId(String id, int scope);
+ method public String getId();
+ method public int getScope();
+ property public final String id;
+ property public final int scope;
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+ field public static final int SCOPE_APP = 1; // 0x1
+ field public static final int SCOPE_DEVELOPER = 2; // 0x2
+ }
+
+ public static final class AppSetId.Companion {
+ }
+
+ public abstract class AppSetIdManager {
+ method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+ method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+ }
+
+ public static final class AppSetIdManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+ public final class AdData {
+ ctor public AdData(android.net.Uri renderUri, String metadata);
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public AdData(android.net.Uri renderUri, String metadata, optional java.util.Set<java.lang.Integer> adCounterKeys, optional androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters);
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public AdData(android.net.Uri renderUri, String metadata, optional java.util.Set<java.lang.Integer> adCounterKeys, optional androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters, optional String? adRenderId);
+ method public java.util.Set<java.lang.Integer> getAdCounterKeys();
+ method public androidx.privacysandbox.ads.adservices.common.AdFilters? getAdFilters();
+ method public String? getAdRenderId();
+ method public String getMetadata();
+ method public android.net.Uri getRenderUri();
+ property public final java.util.Set<java.lang.Integer> adCounterKeys;
+ property public final androidx.privacysandbox.ads.adservices.common.AdFilters? adFilters;
+ property public final String? adRenderId;
+ property public final String metadata;
+ property public final android.net.Uri renderUri;
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class AdFilters {
+ ctor public AdFilters(androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? frequencyCapFilters);
+ method public androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? getFrequencyCapFilters();
+ property public final androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters? frequencyCapFilters;
+ }
+
+ public final class AdSelectionSignals {
+ ctor public AdSelectionSignals(String signals);
+ method public String getSignals();
+ property public final String signals;
+ }
+
+ public final class AdTechIdentifier {
+ ctor public AdTechIdentifier(String identifier);
+ method public String getIdentifier();
+ property public final String identifier;
+ }
+
+ public sealed interface ExperimentalFeatures {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext10 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext10OptIn {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext11 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext11OptIn {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext8 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext8OptIn {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class FrequencyCapFilters {
+ ctor public FrequencyCapFilters();
+ ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents);
+ ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents);
+ ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents);
+ ctor public FrequencyCapFilters(optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents, optional java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForClickEvents);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForClickEvents();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForImpressionEvents();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForViewEvents();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> getKeyedFrequencyCapsForWinEvents();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForClickEvents;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForViewEvents;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.KeyedFrequencyCap> keyedFrequencyCapsForWinEvents;
+ field public static final int AD_EVENT_TYPE_CLICK = 3; // 0x3
+ field public static final int AD_EVENT_TYPE_IMPRESSION = 1; // 0x1
+ field public static final int AD_EVENT_TYPE_VIEW = 2; // 0x2
+ field public static final int AD_EVENT_TYPE_WIN = 0; // 0x0
+ field public static final androidx.privacysandbox.ads.adservices.common.FrequencyCapFilters.Companion Companion;
+ }
+
+ public static final class FrequencyCapFilters.Companion {
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class KeyedFrequencyCap {
+ ctor public KeyedFrequencyCap(int adCounterKey, int maxCount, java.time.Duration interval);
+ method public int getAdCounterKey();
+ method public java.time.Duration getInterval();
+ method public int getMaxCount();
+ property public final int adCounterKey;
+ property public final java.time.Duration interval;
+ property public final int maxCount;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+ public final class CustomAudience {
+ ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+ method public java.time.Instant? getActivationTime();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+ method public android.net.Uri getBiddingLogicUri();
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public android.net.Uri getDailyUpdateUri();
+ method public java.time.Instant? getExpirationTime();
+ method public String getName();
+ method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+ property public final java.time.Instant? activationTime;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+ property public final android.net.Uri biddingLogicUri;
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final android.net.Uri dailyUpdateUri;
+ property public final java.time.Instant? expirationTime;
+ property public final String name;
+ property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+ }
+
+ public static final class CustomAudience.Builder {
+ ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+ }
+
+ public abstract class CustomAudienceManager {
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public abstract suspend Object? fetchAndJoinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.FetchAndJoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+ }
+
+ public static final class CustomAudienceManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class FetchAndJoinCustomAudienceRequest {
+ ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals);
+ method public java.time.Instant? getActivationTime();
+ method public java.time.Instant? getExpirationTime();
+ method public android.net.Uri getFetchUri();
+ method public String? getName();
+ method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+ property public final java.time.Instant? activationTime;
+ property public final java.time.Instant? expirationTime;
+ property public final android.net.Uri fetchUri;
+ property public final String? name;
+ property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+ }
+
+ public final class JoinCustomAudienceRequest {
+ ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+ method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+ property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+ }
+
+ public final class LeaveCustomAudienceRequest {
+ ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+ method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+ method public String getName();
+ property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+ property public final String name;
+ }
+
+ public final class TrustedBiddingData {
+ ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+ method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+ method public android.net.Uri getTrustedBiddingUri();
+ property public final java.util.List<java.lang.String> trustedBiddingKeys;
+ property public final android.net.Uri trustedBiddingUri;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public final class DeletionRequest {
+ ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+ method public int getDeletionMode();
+ method public java.util.List<android.net.Uri> getDomainUris();
+ method public java.time.Instant getEnd();
+ method public int getMatchBehavior();
+ method public java.util.List<android.net.Uri> getOriginUris();
+ method public java.time.Instant getStart();
+ property public final int deletionMode;
+ property public final java.util.List<android.net.Uri> domainUris;
+ property public final java.time.Instant end;
+ property public final int matchBehavior;
+ property public final java.util.List<android.net.Uri> originUris;
+ property public final java.time.Instant start;
+ field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+ field public static final int DELETION_MODE_ALL = 0; // 0x0
+ field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+ field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+ field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.O) public static final class DeletionRequest.Builder {
+ ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+ }
+
+ public static final class DeletionRequest.Companion {
+ }
+
+ public abstract class MeasurementManager {
+ ctor public MeasurementManager();
+ method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+ method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+ field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+ field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+ }
+
+ public static final class MeasurementManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+ }
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+ ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+ method public android.view.InputEvent? getInputEvent();
+ method public java.util.List<android.net.Uri> getRegistrationUris();
+ property public final android.view.InputEvent? inputEvent;
+ property public final java.util.List<android.net.Uri> registrationUris;
+ }
+
+ public static final class SourceRegistrationRequest.Builder {
+ ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+ method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ }
+
+ public final class WebSourceParams {
+ ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ public final class WebSourceRegistrationRequest {
+ ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+ method public android.net.Uri? getAppDestination();
+ method public android.view.InputEvent? getInputEvent();
+ method public android.net.Uri getTopOriginUri();
+ method public android.net.Uri? getVerifiedDestination();
+ method public android.net.Uri? getWebDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+ property public final android.net.Uri? appDestination;
+ property public final android.view.InputEvent? inputEvent;
+ property public final android.net.Uri topOriginUri;
+ property public final android.net.Uri? verifiedDestination;
+ property public final android.net.Uri? webDestination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+ }
+
+ public static final class WebSourceRegistrationRequest.Builder {
+ ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+ method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+ }
+
+ public final class WebTriggerParams {
+ ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+ method public boolean getDebugKeyAllowed();
+ method public android.net.Uri getRegistrationUri();
+ property public final boolean debugKeyAllowed;
+ property public final android.net.Uri registrationUri;
+ }
+
+ public final class WebTriggerRegistrationRequest {
+ ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+ method public android.net.Uri getDestination();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+ property public final android.net.Uri destination;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+ }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext11OptIn public final class EncryptedTopic {
+ ctor public EncryptedTopic(byte[] encryptedTopic, String keyIdentifier, byte[] encapsulatedKey);
+ method public byte[] getEncapsulatedKey();
+ method public byte[] getEncryptedTopic();
+ method public String getKeyIdentifier();
+ property public final byte[] encapsulatedKey;
+ property public final byte[] encryptedTopic;
+ property public final String keyIdentifier;
+ }
+
+ public final class GetTopicsRequest {
+ ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+ method public String getAdsSdkName();
+ method public boolean shouldRecordObservation();
+ property public final String adsSdkName;
+ property public final boolean shouldRecordObservation;
+ }
+
+ public static final class GetTopicsRequest.Builder {
+ ctor public GetTopicsRequest.Builder();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+ method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+ }
+
+ public final class GetTopicsResponse {
+ ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext11OptIn public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics, java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> encryptedTopics);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> getEncryptedTopics();
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> encryptedTopics;
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+ }
+
+ public final class Topic {
+ ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+ method public long getModelVersion();
+ method public long getTaxonomyVersion();
+ method public int getTopicId();
+ property public final long modelVersion;
+ property public final long taxonomyVersion;
+ property public final int topicId;
+ }
+
+ public abstract class TopicsManager {
+ method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+ method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+ }
+
+ public static final class TopicsManager.Companion {
+ method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+ }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_current.txt b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
index 32149c9..7574be5 100644
--- a/privacysandbox/ads/ads-adservices/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
@@ -218,6 +218,9 @@
@SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext10 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext10OptIn {
}
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext11 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext11OptIn {
+ }
+
@SuppressCompatibility @kotlin.RequiresOptIn(message="The Ext8 API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.Ext8OptIn {
}
@@ -469,6 +472,16 @@
package androidx.privacysandbox.ads.adservices.topics {
+ @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext11OptIn public final class EncryptedTopic {
+ ctor public EncryptedTopic(byte[] encryptedTopic, String keyIdentifier, byte[] encapsulatedKey);
+ method public byte[] getEncapsulatedKey();
+ method public byte[] getEncryptedTopic();
+ method public String getKeyIdentifier();
+ property public final byte[] encapsulatedKey;
+ property public final byte[] encryptedTopic;
+ property public final String keyIdentifier;
+ }
+
public final class GetTopicsRequest {
ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
method public String getAdsSdkName();
@@ -486,7 +499,10 @@
public final class GetTopicsResponse {
ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+ ctor @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext11OptIn public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics, java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> encryptedTopics);
+ method public java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> getEncryptedTopics();
method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+ property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.EncryptedTopic> encryptedTopics;
property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
}
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/EncryptedTopicTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/EncryptedTopicTest.kt
new file mode 100644
index 0000000..b374d2e
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/EncryptedTopicTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 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.privacysandbox.ads.adservices.topics
+
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+class EncryptedTopicTest {
+ @Test
+ fun testToString() {
+ val result =
+ "EncryptedTopic { EncryptedTopic=encryptedTopic1, KeyIdentifier=publicKey1," +
+ " EncapsulatedKey=encapsulatedKey1 }"
+ var encryptedTopic =
+ EncryptedTopic(
+ "encryptedTopic1".toByteArray(),
+ "publicKey1",
+ "encapsulatedKey1".toByteArray(),
+ )
+ Truth.assertThat(encryptedTopic.toString()).isEqualTo(result)
+ }
+
+ @Test
+ fun testEquals() {
+ var encryptedTopic1 =
+ EncryptedTopic(
+ "encryptedTopic".toByteArray(),
+ "publicKey",
+ "encapsulatedKey".toByteArray(),
+ )
+ var encryptedTopic2 =
+ EncryptedTopic(
+ "encryptedTopic".toByteArray(),
+ "publicKey",
+ "encapsulatedKey".toByteArray(),
+ )
+
+ Truth.assertThat(encryptedTopic1 == encryptedTopic2).isTrue()
+ }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseHelperTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseHelperTest.kt
index 27d3385..5f95b62 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseHelperTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseHelperTest.kt
@@ -16,9 +16,11 @@
package androidx.privacysandbox.ads.adservices.topics
+import android.adservices.topics.EncryptedTopic
import android.adservices.topics.GetTopicsResponse
import android.adservices.topics.Topic
import android.annotation.SuppressLint
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -31,16 +33,21 @@
@SuppressLint("NewApi")
@SmallTest
@RunWith(AndroidJUnit4::class)
[email protected]
class GetTopicsResponseHelperTest {
private val mValidAdServicesSdkExt4Version = AdServicesInfo.adServicesVersion() >= 4
- private val mValidAdExtServicesSdkExtVersion = AdServicesInfo.extServicesVersionS() >= 9
+ private val mValidAdServicesSdkExt11Version = AdServicesInfo.adServicesVersion() >= 11
+ private val mValidAdExtServicesSdkExt9Version = AdServicesInfo.extServicesVersionS() >= 9
+ private val mValidAdExtServicesSdkExt11Version = AdServicesInfo.extServicesVersionS() >= 11
// Verify legacy tests with just plaintext topics
@Suppress("DEPRECATION")
@Test
fun testResponse() {
- Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
- mValidAdServicesSdkExt4Version || mValidAdExtServicesSdkExtVersion)
+ Assume.assumeTrue(
+ "minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+ mValidAdServicesSdkExt4Version || mValidAdExtServicesSdkExt9Version,
+ )
var topic1 = Topic(3, 7, 10023)
var topic2 = Topic(3, 7, 10024)
@@ -54,4 +61,41 @@
androidx.privacysandbox.ads.adservices.topics.Topic(3, 7, 10023),
)
}
+
+ @Test
+ fun testResponseWithEncryptedTopics() {
+ Assume.assumeTrue(
+ "minSdkVersion = API 33 ext 11 or API 31/32 ext 11",
+ mValidAdServicesSdkExt11Version || mValidAdExtServicesSdkExt11Version,
+ )
+
+ var topic1 = Topic(3, 7, 10023)
+ var topic2 = Topic(3, 7, 10024)
+ var encryptedTopic1 =
+ EncryptedTopic(
+ "encryptedTopic".toByteArray(),
+ "publicKey",
+ "encapsulatedKey".toByteArray(),
+ )
+
+ var response = GetTopicsResponse.Builder(listOf(topic1, topic2),
+ listOf(encryptedTopic1)).build()
+ var convertedResponse =
+ GetTopicsResponseHelper.convertResponseWithEncryptedTopics(response)
+
+ assertEquals(2, convertedResponse.topics.size)
+ assertEquals(1, convertedResponse.encryptedTopics.size)
+ assertContains(
+ convertedResponse.topics,
+ androidx.privacysandbox.ads.adservices.topics.Topic(3, 7, 10023),
+ )
+ assertContains(
+ convertedResponse.encryptedTopics,
+ androidx.privacysandbox.ads.adservices.topics.EncryptedTopic(
+ "encryptedTopic".toByteArray(),
+ "publicKey",
+ "encapsulatedKey".toByteArray(),
+ ),
+ )
+ }
}
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseTest.kt
index 25c0699..cab8b28 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseTest.kt
@@ -16,6 +16,7 @@
package androidx.privacysandbox.ads.adservices.topics
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth
@@ -24,12 +25,14 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
[email protected]
class GetTopicsResponseTest {
@Test
- fun testToString() {
- val topicsString = "Topic { TaxonomyVersion=1, ModelVersion=10, TopicCode=100 }, " +
- "Topic { TaxonomyVersion=2, ModelVersion=20, TopicCode=200 }"
- val result = "Topics=[$topicsString]"
+ fun testToString_onlyPlaintextTopics() {
+ val topicsString =
+ "Topic { TaxonomyVersion=1, ModelVersion=10, TopicCode=100 }, " +
+ "Topic { TaxonomyVersion=2, ModelVersion=20, TopicCode=200 }"
+ val result = "GetTopicsResponse: Topics=[$topicsString], EncryptedTopics=[]"
val topic1 = Topic(1, 10, 100)
var topic2 = Topic(2, 20, 200)
@@ -38,11 +41,69 @@
}
@Test
- fun testEquals() {
+ fun testToString_plaintextAndEncryptedTopics() {
+ val topicsString =
+ "Topic { TaxonomyVersion=1, ModelVersion=10, TopicCode=100 }, " +
+ "Topic { TaxonomyVersion=2, ModelVersion=20, TopicCode=200 }"
+ val encryptedTopicsString =
+ "EncryptedTopic { EncryptedTopic=encryptedTopic1, KeyIdentifier=publicKey1," +
+ " EncapsulatedKey=encapsulatedKey1 }, EncryptedTopic " +
+ "{ EncryptedTopic=encryptedTopic2, KeyIdentifier=publicKey2," +
+ " EncapsulatedKey=encapsulatedKey2 }"
+
+ val result =
+ "GetTopicsResponse: Topics=[$topicsString], EncryptedTopics=[$encryptedTopicsString]"
+
+ val topic1 = Topic(1, 10, 100)
+ var topic2 = Topic(2, 20, 200)
+ var encryptedTopic1 =
+ EncryptedTopic(
+ "encryptedTopic1".toByteArray(),
+ "publicKey1",
+ "encapsulatedKey1".toByteArray(),
+ )
+ var encryptedTopic2 =
+ EncryptedTopic(
+ "encryptedTopic2".toByteArray(),
+ "publicKey2",
+ "encapsulatedKey2".toByteArray(),
+ )
+
+ val response1 =
+ GetTopicsResponse(listOf(topic1, topic2), listOf(encryptedTopic1, encryptedTopic2))
+ Truth.assertThat(response1.toString()).isEqualTo(result)
+ }
+
+ @Test
+ fun testEquals_onlyPlaintextTopics() {
val topic1 = Topic(1, 10, 100)
var topic2 = Topic(2, 20, 200)
val response1 = GetTopicsResponse(listOf(topic1, topic2))
val response2 = GetTopicsResponse(listOf(topic2, topic1))
Truth.assertThat(response1 == response2).isTrue()
}
+
+ @Test
+ fun testEquals_plaintextAndEncryptedTopics() {
+ val topic1 = Topic(1, 10, 100)
+ var topic2 = Topic(2, 20, 200)
+ var encryptedTopic1 =
+ EncryptedTopic(
+ "encryptedTopic1".toByteArray(),
+ "publicKey1",
+ "encapsulatedKey1".toByteArray(),
+ )
+ var encryptedTopic2 =
+ EncryptedTopic(
+ "encryptedTopic2".toByteArray(),
+ "publicKey2",
+ "encapsulatedKey2".toByteArray(),
+ )
+
+ val response1 =
+ GetTopicsResponse(listOf(topic1, topic2), listOf(encryptedTopic1, encryptedTopic2))
+ val response2 =
+ GetTopicsResponse(listOf(topic2, topic1), listOf(encryptedTopic2, encryptedTopic1))
+ Truth.assertThat(response1 == response2).isTrue()
+ }
}
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerTest.kt
index b7c0324..615fab2 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerTest.kt
@@ -16,10 +16,12 @@
package androidx.privacysandbox.ads.adservices.topics
+import android.adservices.topics.EncryptedTopic
import android.adservices.topics.Topic
import android.adservices.topics.TopicsManager
import android.content.Context
import android.os.OutcomeReceiver
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
import androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion.obtain
import androidx.test.core.app.ApplicationProvider
@@ -50,18 +52,21 @@
@SuppressWarnings("NewApi")
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = 30)
[email protected]
class TopicsManagerTest {
private var mSession: StaticMockitoSession? = null
private val mValidAdServicesSdkExt4Version = AdServicesInfo.adServicesVersion() >= 4
private val mValidAdServicesSdkExt5Version = AdServicesInfo.adServicesVersion() >= 5
- private val mValidAdExtServicesSdkExtVersion = AdServicesInfo.extServicesVersionS() >= 9
+ private val mValidAdServicesSdkExt11Version = AdServicesInfo.adServicesVersion() >= 11
+ private val mValidAdExtServicesSdkExt9Version = AdServicesInfo.extServicesVersionS() >= 9
+ private val mValidAdExtServicesSdkExt11Version = AdServicesInfo.extServicesVersionS() >= 11
@Before
fun setUp() {
mContext = spy(ApplicationProvider.getApplicationContext<Context>())
- if (mValidAdExtServicesSdkExtVersion) {
+ if (mValidAdExtServicesSdkExt9Version) {
// setup a mockitoSession to return the mocked manager
// when the static method .get() is called
mSession = ExtendedMockito.mockitoSession()
@@ -80,13 +85,13 @@
@SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
fun testTopicsOlderVersions() {
Assume.assumeTrue("maxSdkVersion = API 33 ext 3", !mValidAdServicesSdkExt4Version)
- Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExtVersion)
+ Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExt9Version)
assertThat(obtain(mContext)).isNull()
}
@Test
fun testTopicsManagerNoClassDefFoundError() {
- Assume.assumeTrue("minSdkVersion = API 31/32 ext 9", mValidAdExtServicesSdkExtVersion);
+ Assume.assumeTrue("minSdkVersion = API 31/32 ext 9", mValidAdExtServicesSdkExt9Version);
`when`(TopicsManager.get(any())).thenThrow(NoClassDefFoundError())
assertThat(obtain(mContext)).isNull()
@@ -94,10 +99,12 @@
@Test
fun testTopicsAsync() {
- Assume.assumeTrue("minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
- mValidAdServicesSdkExt4Version || mValidAdExtServicesSdkExtVersion)
+ Assume.assumeTrue(
+ "minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+ mValidAdServicesSdkExt4Version || mValidAdExtServicesSdkExt9Version
+ )
- val topicsManager = mockTopicsManager(mContext, mValidAdExtServicesSdkExtVersion)
+ val topicsManager = mockTopicsManager(mContext, mValidAdExtServicesSdkExt9Version)
setupTopicsResponse(topicsManager)
val managerCompat = obtain(mContext)
@@ -124,11 +131,46 @@
}
@Test
- fun testTopicsAsyncPreviewSupported() {
- Assume.assumeTrue("minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
- mValidAdServicesSdkExt5Version || mValidAdExtServicesSdkExtVersion)
+ fun testEncryptedTopicsAsync() {
+ Assume.assumeTrue(
+ "minSdkVersion = API 33 ext 11 or API 31/32 ext 11",
+ mValidAdServicesSdkExt11Version || mValidAdExtServicesSdkExt11Version,
+ )
- val topicsManager = mockTopicsManager(mContext, mValidAdExtServicesSdkExtVersion)
+ val topicsManager = mockTopicsManager(mContext, mValidAdExtServicesSdkExt11Version)
+ setupEncryptedTopicsResponse(topicsManager)
+ val managerCompat = obtain(mContext)
+
+ // Actually invoke the compat code.
+ val result = runBlocking {
+ val request = GetTopicsRequest.Builder()
+ .setAdsSdkName(mSdkName)
+ .setShouldRecordObservation(true)
+ .build()
+
+ managerCompat!!.getTopics(request)
+ }
+
+ // Verify that the compat code was invoked correctly.
+ val captor = ArgumentCaptor
+ .forClass(android.adservices.topics.GetTopicsRequest::class.java)
+ verify(topicsManager).getTopics(captor.capture(), any(), any())
+
+ // Verify that the request that the compat code makes to the platform is correct.
+ verifyRequest(captor.value)
+
+ // Verify that the result of the compat call is correct.
+ verifyEncryptedResponse(result)
+ }
+
+ @Test
+ fun testTopicsAsyncPreviewSupported() {
+ Assume.assumeTrue(
+ "minSdkVersion = API 33 ext 5 or API 31/32 ext 9",
+ mValidAdServicesSdkExt5Version || mValidAdExtServicesSdkExt9Version
+ )
+
+ val topicsManager = mockTopicsManager(mContext, mValidAdExtServicesSdkExt9Version)
setupTopicsResponse(topicsManager)
val managerCompat = obtain(mContext)
@@ -188,6 +230,37 @@
)
}
+ @Suppress("deprecation")
+ private fun setupEncryptedTopicsResponse(topicsManager: TopicsManager) {
+ // Set up the response that TopicsManager will return when the compat code calls it.
+ val topic1 = Topic(1, 1, 1)
+ val topic2 = Topic(2, 2, 2)
+ var encryptedTopic1 = EncryptedTopic(
+ "encryptedTopic1".toByteArray(),
+ "publicKey1",
+ "encapsulatedKey1".toByteArray()
+ )
+ var encryptedTopic2 = EncryptedTopic(
+ "encryptedTopic2".toByteArray(),
+ "publicKey2",
+ "encapsulatedKey2".toByteArray()
+ )
+
+ val topics = listOf(topic1, topic2)
+ val encryptedTopics = listOf(encryptedTopic1, encryptedTopic2)
+ val response =
+ android.adservices.topics.GetTopicsResponse.Builder(topics, encryptedTopics).build()
+ val answer = { args: InvocationOnMock ->
+ val receiver = args.getArgument<
+ OutcomeReceiver<android.adservices.topics.GetTopicsResponse, Exception>>(2)
+ receiver.onResult(response)
+ null
+ }
+ doAnswer(answer).`when`(topicsManager).getTopics(
+ any(), any(), any()
+ )
+ }
+
private fun verifyRequest(topicsRequest: android.adservices.topics.GetTopicsRequest) {
// Set up the request that we expect the compat code to invoke.
val expectedRequest =
@@ -209,6 +282,33 @@
Assert.assertEquals(expectedRequest.adsSdkName, topicsRequest.adsSdkName)
}
+ private fun verifyEncryptedResponse(getTopicsResponse: GetTopicsResponse) {
+ Assert.assertEquals(2, getTopicsResponse.encryptedTopics.size)
+ val encryptedTopic1 = getTopicsResponse.encryptedTopics[0]
+ Assert.assertArrayEquals(
+ "encryptedTopic1".toByteArray(),
+ encryptedTopic1.encryptedTopic
+ )
+ Assert.assertEquals("publicKey1", encryptedTopic1.keyIdentifier)
+ Assert.assertArrayEquals(
+ "encapsulatedKey1".toByteArray(),
+ encryptedTopic1.encapsulatedKey
+ )
+ val encryptedTopic2 = getTopicsResponse.encryptedTopics[1]
+ Assert.assertArrayEquals(
+ "encryptedTopic2".toByteArray(),
+ encryptedTopic2.encryptedTopic
+ )
+ Assert.assertEquals("publicKey2", encryptedTopic2.keyIdentifier)
+ Assert.assertArrayEquals(
+ "encapsulatedKey2".toByteArray(),
+ encryptedTopic2.encapsulatedKey
+ )
+
+ // Verify plaintext topic fields
+ verifyResponse(getTopicsResponse)
+ }
+
private fun verifyResponse(getTopicsResponse: GetTopicsResponse) {
Assert.assertEquals(2, getTopicsResponse.topics.size)
val topic1 = getTopicsResponse.topics[0]
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/ReportEventRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/ReportEventRequest.kt
index 43320f6..fbad84b 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/ReportEventRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/ReportEventRequest.kt
@@ -21,6 +21,7 @@
import android.os.ext.SdkExtensions
import android.util.Log
import android.view.InputEvent
+import androidx.annotation.IntDef
import androidx.annotation.RequiresExtension
import androidx.annotation.RestrictTo
import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
@@ -43,7 +44,7 @@
val adSelectionId: Long,
val eventKey: String,
val eventData: String,
- val reportingDestinations: Int,
+ @ReportingDestination val reportingDestinations: Int,
@property:ExperimentalFeatures.Ext10OptIn val inputEvent: InputEvent? = null
) {
init {
@@ -82,6 +83,15 @@
"inputEvent=$inputEvent"
}
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(
+ flag = true,
+ value = [
+ Companion.FLAG_REPORTING_DESTINATION_SELLER,
+ Companion.FLAG_REPORTING_DESTINATION_BUYER])
+ annotation class ReportingDestination
+
companion object {
const val FLAG_REPORTING_DESTINATION_SELLER: Int =
android.adservices.adselection.ReportEventRequest.FLAG_REPORTING_DESTINATION_SELLER
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/UpdateAdCounterHistogramRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/UpdateAdCounterHistogramRequest.kt
index 5528a9f..38d8db8 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/UpdateAdCounterHistogramRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/UpdateAdCounterHistogramRequest.kt
@@ -38,7 +38,7 @@
@ExperimentalFeatures.Ext8OptIn
class UpdateAdCounterHistogramRequest public constructor(
val adSelectionId: Long,
- val adEventType: Int,
+ @FrequencyCapFilters.AdEventType val adEventType: Int,
val callerAdTech: AdTechIdentifier
) {
init {
@@ -77,7 +77,9 @@
val adEventTypeStr = when (adEventType) {
FrequencyCapFilters.AD_EVENT_TYPE_IMPRESSION -> "AD_EVENT_TYPE_IMPRESSION"
FrequencyCapFilters.AD_EVENT_TYPE_VIEW -> "AD_EVENT_TYPE_VIEW"
- else -> "AD_EVENT_TYPE_CLICK"
+ FrequencyCapFilters.AD_EVENT_TYPE_WIN -> "AD_EVENT_TYPE_WIN"
+ FrequencyCapFilters.AD_EVENT_TYPE_CLICK -> "AD_EVENT_TYPE_CLICK"
+ else -> "Invalid ad event type"
}
return "UpdateAdCounterHistogramRequest: adSelectionId=$adSelectionId, " +
"adEventType=$adEventTypeStr, callerAdTech=$callerAdTech"
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/AdData.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/AdData.kt
index 8c89de6..10cf1ee 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/AdData.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/AdData.kt
@@ -31,7 +31,7 @@
* @param renderUri a URI pointing to the ad's rendering assets
* @param metadata buyer ad metadata represented as a JSON string
* @param adCounterKeys the set of keys used in counting events
- * @param adFilters all [AdFilters] associated with the ad
+ * @param adFilters all [AdFilters] associated with the ad, it's optional and can be null as well
* @param adRenderId ad render id for server auctions
*/
@OptIn(ExperimentalFeatures.Ext8OptIn::class, ExperimentalFeatures.Ext10OptIn::class)
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt
index c7ac0e5..cdec101 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt
@@ -31,4 +31,7 @@
@RequiresOptIn("The Ext10 API is experimental.", RequiresOptIn.Level.WARNING)
annotation class Ext10OptIn
+
+ @RequiresOptIn("The Ext11 API is experimental.", RequiresOptIn.Level.WARNING)
+ annotation class Ext11OptIn
}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/FrequencyCapFilters.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/FrequencyCapFilters.kt
index 502262d..9f1df84 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/FrequencyCapFilters.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/FrequencyCapFilters.kt
@@ -18,6 +18,7 @@
import android.os.Build
import android.os.ext.SdkExtensions
+import androidx.annotation.IntDef
import androidx.annotation.RequiresExtension
import androidx.annotation.RestrictTo
@@ -73,8 +74,19 @@
"keyedFrequencyCapsForClickEvents=$keyedFrequencyCapsForClickEvents"
}
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(
+ Companion.AD_EVENT_TYPE_WIN,
+ Companion.AD_EVENT_TYPE_VIEW,
+ Companion.AD_EVENT_TYPE_CLICK,
+ Companion.AD_EVENT_TYPE_IMPRESSION)
+ annotation class AdEventType
+
companion object {
/**
+ * Represents the Win event for ads that were selected as winners in ad selection.
+ *
* The WIN ad event type is automatically populated within the Protected Audience service
* for any winning ad which is returned from Protected Audience ad selection.
*
@@ -82,10 +94,23 @@
*/
public const val AD_EVENT_TYPE_WIN: Int =
android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_WIN
+
+ /**
+ * Represents the Impression event type which correlate to an impression as interpreted by
+ * an adtech.
+ */
public const val AD_EVENT_TYPE_IMPRESSION: Int =
android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_IMPRESSION
+
+ /**
+ * Represents the View event type which correlate to a view as interpreted by an adtech.
+ */
public const val AD_EVENT_TYPE_VIEW: Int =
android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_VIEW
+
+ /**
+ * Represents the Click event type which correlate to a click as interpreted by an adtech.
+ */
public const val AD_EVENT_TYPE_CLICK: Int =
android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_CLICK
}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/KeyedFrequencyCap.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/KeyedFrequencyCap.kt
index 7e95473..37f606b 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/KeyedFrequencyCap.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/KeyedFrequencyCap.kt
@@ -28,9 +28,9 @@
* Frequency caps define the maximum rate an event can occur within a given time interval. If the
* frequency cap is exceeded, the associated ad will be filtered out of ad selection.
*
- * @param adCounterKey The ad counter key that the frequency cap is applied to.
- * @param maxCount A render URL for the winning ad.
- * @param interval The caller adtech entity's [AdTechIdentifier].
+ * @param adCounterKey The ad counter key that the frequency cap is applied to
+ * @param maxCount The maximum count of event occurrences allowed within a given time interval
+ * @param interval The interval, as a [Duration] over which the frequency cap is calculated
*/
@ExperimentalFeatures.Ext8OptIn
class KeyedFrequencyCap public constructor(
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/EncryptedTopic.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/EncryptedTopic.kt
new file mode 100644
index 0000000..716c6ed
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/EncryptedTopic.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 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.privacysandbox.ads.adservices.topics
+
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+import java.util.Objects
+
+/**
+ * This class will be used to return encrypted topic cipher text along with necessary fields required to decrypt it.
+ *
+ * <p>Decryption of {@link EncryptedTopic#getEncryptedTopic()} should give json string for {@link
+ * Topic}. Example of decrypted json string: {@code { "taxonomy_version": 5, "model_version": 2,
+ * "topic_id": 10010 }}
+ *
+ * <p>Decryption of cipher text is expected to happen on the server with the corresponding algorithm
+ * and private key for the public key {@link EncryptedTopic#getKeyIdentifier()}}.
+ *
+ * <p>Detailed steps on decryption can be found on <a
+ * href="https://developer.android.com/design-for-safety/privacy-sandbox/guides/topics">Developer
+ * Guide</a>.
+ */
[email protected]
+class EncryptedTopic public constructor(
+ val encryptedTopic: ByteArray,
+ val keyIdentifier: String,
+ val encapsulatedKey: ByteArray
+) {
+ override fun toString(): String {
+ val encryptedTopicString = "EncryptedTopic=${encryptedTopic.decodeToString()}" +
+ ", KeyIdentifier=$keyIdentifier" +
+ ", EncapsulatedKey=${encapsulatedKey.decodeToString()} }"
+ return "EncryptedTopic { $encryptedTopicString"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is EncryptedTopic) return false
+ return this.encryptedTopic.contentEquals(other.encryptedTopic) &&
+ this.keyIdentifier.contentEquals(other.keyIdentifier) &&
+ this.encapsulatedKey.contentEquals(other.encapsulatedKey)
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(
+ encryptedTopic.contentHashCode(),
+ keyIdentifier,
+ encapsulatedKey.contentHashCode()
+ )
+ }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponse.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponse.kt
index f6f828a..dbfcf53 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponse.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponse.kt
@@ -16,22 +16,31 @@
package androidx.privacysandbox.ads.adservices.topics
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
import java.util.Objects
/** Represent the result from the getTopics API. */
-class GetTopicsResponse(val topics: List<Topic>) {
+@OptIn(ExperimentalFeatures.Ext11OptIn::class)
+class GetTopicsResponse @ExperimentalFeatures.Ext11OptIn constructor(
+ val topics: List<Topic>,
+ val encryptedTopics: List<EncryptedTopic>,
+) {
+ constructor(topics: List<Topic>) : this(topics, listOf())
+
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is GetTopicsResponse) return false
- if (topics.size != other.topics.size) return false
- return HashSet(this.topics) == HashSet(other.topics)
+ if (topics.size != other.topics.size ||
+ encryptedTopics.size != other.encryptedTopics.size) return false
+ return HashSet(this.topics) == HashSet(other.topics) &&
+ HashSet(this.encryptedTopics) == HashSet(other.encryptedTopics)
}
override fun hashCode(): Int {
- return Objects.hash(topics)
+ return Objects.hash(topics, encryptedTopics)
}
override fun toString(): String {
- return "Topics=$topics"
+ return "GetTopicsResponse: Topics=$topics, EncryptedTopics=$encryptedTopics"
}
}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseHelper.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseHelper.kt
index 5636137..d22fa00 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseHelper.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsResponseHelper.kt
@@ -21,6 +21,7 @@
import android.os.ext.SdkExtensions
import androidx.annotation.RequiresExtension
import androidx.annotation.RestrictTo
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
/**
* Helper class to consolidate conversion logic for GetTopicsResponse.
@@ -30,12 +31,36 @@
object GetTopicsResponseHelper {
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
- internal fun convertResponse(response: android.adservices.topics.GetTopicsResponse):
- GetTopicsResponse {
+ internal fun convertResponse(
+ response: android.adservices.topics.GetTopicsResponse,
+ ): GetTopicsResponse {
val topics = mutableListOf<Topic>()
for (topic in response.topics) {
topics.add(Topic(topic.taxonomyVersion, topic.modelVersion, topic.topicId))
}
return GetTopicsResponse(topics)
}
+
+ @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 11)
+ @RequiresExtension(extension = Build.VERSION_CODES.S, version = 11)
+ @ExperimentalFeatures.Ext11OptIn
+ internal fun convertResponseWithEncryptedTopics(
+ response: android.adservices.topics.GetTopicsResponse,
+ ): GetTopicsResponse {
+ val topics = mutableListOf<Topic>()
+ for (topic in response.topics) {
+ topics.add(Topic(topic.taxonomyVersion, topic.modelVersion, topic.topicId))
+ }
+ val encryptedTopics = mutableListOf<EncryptedTopic>()
+ for (encryptedTopic in response.encryptedTopics) {
+ encryptedTopics.add(
+ EncryptedTopic(
+ encryptedTopic.encryptedTopic,
+ encryptedTopic.keyIdentifier,
+ encryptedTopic.encapsulatedKey,
+ ),
+ )
+ }
+ return GetTopicsResponse(topics, encryptedTopics)
+ }
}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManager.kt
index 97ec4ba..a114dfe 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManager.kt
@@ -51,10 +51,16 @@
@JvmStatic
@SuppressLint("NewApi", "ClassVerificationFailure")
fun obtain(context: Context): TopicsManager? {
- return if (AdServicesInfo.adServicesVersion() >= 5) {
+ return if (AdServicesInfo.adServicesVersion() >= 11) {
+ TopicsManagerApi33Ext11Impl(context)
+ } else if (AdServicesInfo.adServicesVersion() >= 5) {
TopicsManagerApi33Ext5Impl(context)
} else if (AdServicesInfo.adServicesVersion() == 4) {
TopicsManagerApi33Ext4Impl(context)
+ } else if (AdServicesInfo.extServicesVersionS() >= 11) {
+ BackCompatManager.getManager(context, "TopicsManager") {
+ TopicsManagerApi31Ext11Impl(context)
+ }
} else if (AdServicesInfo.extServicesVersionS() >= 9) {
BackCompatManager.getManager(context, "TopicsManager") {
TopicsManagerApi31Ext9Impl(context)
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerApi31Ext11Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerApi31Ext11Impl.kt
new file mode 100644
index 0000000..b6473f2
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerApi31Ext11Impl.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 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.privacysandbox.ads.adservices.topics
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = Build.VERSION_CODES.S, version = 11)
+class TopicsManagerApi31Ext11Impl(context: Context) : TopicsManagerImplCommon(
+ android.adservices.topics.TopicsManager.get(context),
+) {
+ override fun convertRequest(
+ request: GetTopicsRequest
+ ): android.adservices.topics.GetTopicsRequest {
+ return GetTopicsRequestHelper.convertRequestWithRecordObservation(request)
+ }
+
+ @ExperimentalFeatures.Ext11OptIn
+ override fun convertResponse(
+ response: android.adservices.topics.GetTopicsResponse
+ ): GetTopicsResponse {
+ return GetTopicsResponseHelper.convertResponseWithEncryptedTopics(response)
+ }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerApi33Ext11Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerApi33Ext11Impl.kt
new file mode 100644
index 0000000..c5f2524
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerApi33Ext11Impl.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 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.privacysandbox.ads.adservices.topics
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressLint("NewApi", "ClassVerificationFailure")
+@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 11)
+class TopicsManagerApi33Ext11Impl(context: Context) : TopicsManagerImplCommon(
+ context.getSystemService(android.adservices.topics.TopicsManager::class.java)
+) {
+
+ override fun convertRequest(
+ request: GetTopicsRequest
+ ): android.adservices.topics.GetTopicsRequest {
+ return GetTopicsRequestHelper.convertRequestWithRecordObservation(request)
+ }
+
+ @ExperimentalFeatures.Ext11OptIn
+ override fun convertResponse(
+ response: android.adservices.topics.GetTopicsResponse
+ ): GetTopicsResponse {
+ return GetTopicsResponseHelper.convertResponseWithEncryptedTopics(response)
+ }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt
index 3fe0c44..1c46ec7 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt
@@ -58,7 +58,7 @@
return GetTopicsRequestHelper.convertRequestWithoutRecordObservation(request)
}
- internal fun convertResponse(
+ internal open fun convertResponse(
response: android.adservices.topics.GetTopicsResponse
): GetTopicsResponse {
return GetTopicsResponseHelper.convertResponse(response)
diff --git a/privacysandbox/plugins/plugins-privacysandbox-library/src/main/java/androidx/privacysandboxlibraryplugin/PrivacySandboxLibraryPlugin.kt b/privacysandbox/plugins/plugins-privacysandbox-library/src/main/java/androidx/privacysandboxlibraryplugin/PrivacySandboxLibraryPlugin.kt
index 2dcc31b..7d62c28 100644
--- a/privacysandbox/plugins/plugins-privacysandbox-library/src/main/java/androidx/privacysandboxlibraryplugin/PrivacySandboxLibraryPlugin.kt
+++ b/privacysandbox/plugins/plugins-privacysandbox-library/src/main/java/androidx/privacysandboxlibraryplugin/PrivacySandboxLibraryPlugin.kt
@@ -77,7 +77,8 @@
// Add additional dependencies required for KSP outputs
- val toolsVersion = "1.0.0-alpha04"
+ val toolsVersion = "1.0.0-alpha08"
+ val sdkRuntimeVersion = "1.0.0-alpha13"
project.dependencies {
add(
"ksp",
@@ -85,7 +86,7 @@
)
add(
"implementation",
- "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
+ "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1"
)
add(
"implementation",
@@ -93,11 +94,15 @@
)
add(
"implementation",
- "androidx.privacysandbox.sdkruntime:sdkruntime-core:1.0.0-alpha06"
+ "androidx.privacysandbox.sdkruntime:sdkruntime-core:$sdkRuntimeVersion"
)
add(
"implementation",
- "androidx.privacysandbox.sdkruntime:sdkruntime-client:1.0.0-alpha06"
+ "androidx.privacysandbox.sdkruntime:sdkruntime-client:$sdkRuntimeVersion"
+ )
+ add(
+ "implementation",
+ "androidx.privacysandbox.sdkruntime:sdkruntime-provider:$sdkRuntimeVersion"
)
}
project.afterEvaluate {
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml b/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
index 1d7dbea..c0e9f08 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
+++ b/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha10" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha10)" variant="all" version="8.3.0-alpha10">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -19,4 +19,58 @@
file="src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorageTest.kt"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProvider.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProvider.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtils.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt"/>
+ </issue>
+
</issues>
diff --git a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
index 7a8b34a..777c33f 100644
--- a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
+++ b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
@@ -50,8 +50,6 @@
classpath = extraClasspath,
symbolProcessorProviders = symbolProcessorProviders,
processorOptions = processorOptions,
- // b/328813158 to remove targeting of Java 17
- javacArguments = listOf("-source", "17", "-target", "17")
)
)
}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/BaseFragment.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/BaseFragment.kt
index 2513b5d..f253f94 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/BaseFragment.kt
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/BaseFragment.kt
@@ -16,11 +16,20 @@
package androidx.privacysandbox.ui.integration.testapp
+import android.app.Activity
import android.os.Bundle
+import android.util.Log
+import android.view.ViewGroup
+import android.widget.TextView
+import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat
+import androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat
+import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionState
+import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionStateChangedListener
import androidx.privacysandbox.ui.client.view.SandboxedSdkView
import androidx.privacysandbox.ui.integration.testaidl.ISdkApi
+import kotlinx.coroutines.runBlocking
/**
* Base fragment to be used for testing different manual flows.
@@ -31,16 +40,22 @@
*/
abstract class BaseFragment : Fragment() {
private lateinit var sdkApi: ISdkApi
+ private lateinit var sdkSandboxManager: SdkSandboxManagerCompat
+ private lateinit var activity: Activity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val sdkSandboxManager = SdkSandboxManagerCompat.from(requireContext())
- val loadedSdks = sdkSandboxManager.getSandboxedSdks()
- val loadedSdk = loadedSdks.firstOrNull { it.getSdkInfo()?.name == SDK_NAME }
- if (loadedSdk == null) {
- throw IllegalStateException("SDK not loaded")
+ activity = requireActivity()
+ sdkSandboxManager = SdkSandboxManagerCompat.from(requireContext())
+ runBlocking {
+ val loadedSdks = sdkSandboxManager.getSandboxedSdks()
+ var loadedSdk = loadedSdks.firstOrNull { it.getSdkInfo()?.name == SDK_NAME }
+ if (loadedSdk == null) {
+ loadedSdk = sdkSandboxManager.loadSdk(SDK_NAME, Bundle())
+ sdkSandboxManager.loadSdk(MEDIATEE_SDK_NAME, Bundle())
+ }
+ sdkApi = ISdkApi.Stub.asInterface(loadedSdk.getInterface())
}
- sdkApi = ISdkApi.Stub.asInterface(loadedSdk.getInterface())
}
/**
@@ -50,6 +65,20 @@
return sdkApi
}
+ fun SandboxedSdkView.addStateChangedListener() {
+ addStateChangedListener(StateChangeListener(this))
+ }
+
+ /**
+ * Unloads all SDKs, resulting in sandbox death. This method registers a death callback to
+ * ensure that the app is not also killed.
+ */
+ fun unloadAllSdks() {
+ sdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, DeathCallbackImpl())
+ sdkSandboxManager.unloadSdk(SDK_NAME)
+ sdkSandboxManager.unloadSdk(MEDIATEE_SDK_NAME)
+ }
+
/**
* Called when the app's drawer layout state changes. When called, change the Z-order of
* any [SandboxedSdkView] owned by the fragment to ensure that the remote UI is not drawn over
@@ -58,8 +87,37 @@
*/
abstract fun handleDrawerStateChange(isDrawerOpen: Boolean)
+ private inner class StateChangeListener(val view: SandboxedSdkView) :
+ SandboxedSdkUiSessionStateChangedListener {
+ override fun onStateChanged(state: SandboxedSdkUiSessionState) {
+ Log.i(TAG, "UI session state changed to: $state")
+ if (state is SandboxedSdkUiSessionState.Error) {
+ // If the session fails to open, display the error.
+ val parent = view.parent as ViewGroup
+ val index = parent.indexOfChild(view)
+ val textView = TextView(requireActivity())
+ textView.text = state.throwable.message
+
+ requireActivity().runOnUiThread {
+ parent.removeView(view)
+ parent.addView(textView, index)
+ }
+ }
+ }
+ }
+
+ private inner class DeathCallbackImpl : SdkSandboxProcessDeathCallbackCompat {
+ override fun onSdkSandboxDied() {
+ activity.runOnUiThread {
+ Toast.makeText(activity, "Sandbox died", Toast.LENGTH_LONG).show()
+ }
+ }
+ }
+
companion object {
private const val SDK_NAME = "androidx.privacysandbox.ui.integration.testsdkprovider"
+ private const val MEDIATEE_SDK_NAME =
+ "androidx.privacysandbox.ui.integration.mediateesdkprovider"
const val TAG = "TestSandboxClient"
}
}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
index ae13cb1..8d7af17 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
@@ -109,7 +109,7 @@
val itemId = it.itemId
when (itemId) {
R.id.item_main -> switchContentFragment(MainFragment(), it.title)
- R.id.item_empty -> switchContentFragment(EmptyFragment(), it.title)
+ R.id.item_sandbox_death -> switchContentFragment(SandboxDeathFragment(), it.title)
else -> {
Log.e(TAG, "Invalid fragment option")
true
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainFragment.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainFragment.kt
index 7944323..e9b2ccc 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainFragment.kt
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainFragment.kt
@@ -17,16 +17,12 @@
package androidx.privacysandbox.ui.integration.testapp
import android.os.Bundle
-import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
-import android.widget.TextView
import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
-import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionState
-import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionStateChangedListener
import androidx.privacysandbox.ui.client.view.SandboxedSdkView
import androidx.privacysandbox.ui.integration.testaidl.ISdkApi
import com.google.android.material.switchmaterial.SwitchMaterial
@@ -79,7 +75,7 @@
}
private fun loadWebViewBannerAd() {
- webViewBannerView.addStateChangedListener(StateChangeListener(webViewBannerView))
+ webViewBannerView.addStateChangedListener()
webViewBannerView.setAdapter(
SandboxedUiAdapterFactory.createFromCoreLibInfo(
sdkApi.loadLocalWebViewAd()
@@ -101,7 +97,7 @@
}
private fun loadBottomBannerAd() {
- bottomBannerView.addStateChangedListener(StateChangeListener(bottomBannerView))
+ bottomBannerView.addStateChangedListener()
bottomBannerView.layoutParams = inflatedView.findViewById<LinearLayout>(
R.id.bottom_banner_container).layoutParams
requireActivity().runOnUiThread {
@@ -115,8 +111,7 @@
}
private fun loadResizableBannerAd() {
- resizableBannerView.addStateChangedListener(
- StateChangeListener(resizableBannerView))
+ resizableBannerView.addStateChangedListener()
resizableBannerView.setAdapter(
SandboxedUiAdapterFactory.createFromCoreLibInfo(
sdkApi.loadTestAdWithWaitInsideOnDraw(/*text=*/ "Resizable View")
@@ -164,23 +159,4 @@
sdkApi.requestResize(newWidth, newHeight)
}
}
-
- private inner class StateChangeListener(val view: SandboxedSdkView) :
- SandboxedSdkUiSessionStateChangedListener {
- override fun onStateChanged(state: SandboxedSdkUiSessionState) {
- Log.i(TAG, "UI session state changed to: $state")
- if (state is SandboxedSdkUiSessionState.Error) {
- // If the session fails to open, display the error.
- val parent = view.parent as ViewGroup
- val index = parent.indexOfChild(view)
- val textView = TextView(requireActivity())
- textView.text = state.throwable.message
-
- requireActivity().runOnUiThread {
- parent.removeView(view)
- parent.addView(textView, index)
- }
- }
- }
- }
}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/SandboxDeathFragment.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/SandboxDeathFragment.kt
new file mode 100644
index 0000000..2c85ccd
--- /dev/null
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/SandboxDeathFragment.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024 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.privacysandbox.ui.integration.testapp
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
+import androidx.privacysandbox.ui.client.view.SandboxedSdkView
+import androidx.privacysandbox.ui.integration.testaidl.ISdkApi
+
+class SandboxDeathFragment : BaseFragment() {
+ private lateinit var sdkApi: ISdkApi
+ private lateinit var inflatedView: View
+ private lateinit var sandboxedSdkView: SandboxedSdkView
+
+ override fun handleDrawerStateChange(isDrawerOpen: Boolean) {
+ sandboxedSdkView.orderProviderUiAboveClientUi(!isDrawerOpen)
+ }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ inflatedView = inflater.inflate(R.layout.fragment_sandbox_death, container, false)
+ sdkApi = getSdkApi()
+ onLoaded()
+ return inflatedView
+ }
+
+ private fun onLoaded() {
+ sandboxedSdkView = inflatedView.findViewById(R.id.remote_view)
+ sandboxedSdkView.addStateChangedListener()
+ sandboxedSdkView.setAdapter(
+ SandboxedUiAdapterFactory.createFromCoreLibInfo(sdkApi.loadTestAd("Test Ad")))
+ val unloadSdksButton: Button = inflatedView.findViewById(R.id.unload_all_sdks_button)
+ unloadSdksButton.setOnClickListener {
+ unloadAllSdks()
+ }
+ }
+}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/action_menu.xml b/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/action_menu.xml
index 836375e..1cde836 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/action_menu.xml
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/action_menu.xml
@@ -19,6 +19,6 @@
android:id="@+id/item_main"
android:title="Main CUJ" />
<item
- android:id="@+id/item_empty"
- android:title="Empty CUJ" />
+ android:id="@+id/item_sandbox_death"
+ android:title="Sandbox Death CUJ" />
</menu>
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/fragment_sandbox_death.xml b/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/fragment_sandbox_death.xml
new file mode 100644
index 0000000..02015bb
--- /dev/null
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/fragment_sandbox_death.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <androidx.privacysandbox.ui.client.view.SandboxedSdkView
+ android:layout_width="500dp"
+ android:layout_height="500dp"
+ android:id="@+id/remote_view"/>
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="100dp"
+ android:id="@+id/unload_all_sdks_button"
+ android:text="@string/unload_sdks_button"/>
+</androidx.appcompat.widget.LinearLayoutCompat>
\ No newline at end of file
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/res/values/strings.xml b/privacysandbox/ui/integration-tests/testapp/src/main/res/values/strings.xml
index 5ecbc59..33f5242 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/res/values/strings.xml
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/res/values/strings.xml
@@ -21,4 +21,5 @@
<string name="local_to_internet_switch">local webview</string>
<string name="mediation_switch">Mediation</string>
<string name="app_owned_mediatee_switch">AppOwnedMediatee</string>
+ <string name="unload_sdks_button">Unload SDKs</string>
</resources>
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
index a94fd8f..d25dc59 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
@@ -23,6 +23,7 @@
import android.os.Build
import android.os.Bundle
import android.os.IBinder
+import android.os.RemoteException
import android.util.Log
import android.view.Display
import android.view.SurfaceControlViewHost
@@ -233,14 +234,16 @@
context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val displayId = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).displayId
- adapterInterface.openRemoteSession(
- windowInputToken,
- displayId,
- initialWidth,
- initialHeight,
- isZOrderOnTop,
- RemoteSessionClient(context, client, clientExecutor)
- )
+ tryToCallRemoteObject {
+ adapterInterface.openRemoteSession(
+ windowInputToken,
+ displayId,
+ initialWidth,
+ initialHeight,
+ isZOrderOnTop,
+ RemoteSessionClient(context, client, clientExecutor)
+ )
+ }
}
class RemoteSessionClient(
@@ -263,6 +266,11 @@
.onSessionOpened(SessionImpl(surfaceView,
remoteSessionController, surfacePackage))
}
+ tryToCallRemoteObject {
+ remoteSessionController.asBinder().linkToDeath({
+ onRemoteSessionError("Remote process died")
+ }, 0)
+ }
}
override fun onRemoteSessionError(errorString: String) {
@@ -287,7 +295,9 @@
override val view: View = surfaceView
override fun notifyConfigurationChanged(configuration: Configuration) {
- remoteSessionController.notifyConfigurationChanged(configuration)
+ tryToCallRemoteObject {
+ remoteSessionController.notifyConfigurationChanged(configuration)
+ }
}
@SuppressLint("ClassVerificationFailure")
@@ -302,7 +312,9 @@
}
val providerResizeRunnable = Runnable {
- remoteSessionController.notifyResized(width, height)
+ tryToCallRemoteObject {
+ remoteSessionController.notifyResized(width, height)
+ }
}
val syncGroup = SurfaceSyncGroup("AppAndSdkViewsSurfaceSync")
@@ -314,11 +326,27 @@
override fun notifyZOrderChanged(isZOrderOnTop: Boolean) {
surfaceView.setZOrderOnTop(isZOrderOnTop)
- remoteSessionController.notifyZOrderChanged(isZOrderOnTop)
+ tryToCallRemoteObject {
+ remoteSessionController.notifyZOrderChanged(isZOrderOnTop)
+ }
}
override fun close() {
- remoteSessionController.close()
+ tryToCallRemoteObject { remoteSessionController.close() }
+ }
+ }
+
+ private companion object {
+
+ /**
+ * Tries to call the remote object and handles exceptions if the remote object has died.
+ */
+ private inline fun tryToCallRemoteObject(function: () -> Unit) {
+ try {
+ function()
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Calling remote object failed: $e")
+ }
}
}
}
diff --git a/profileinstaller/profileinstaller/lint-baseline.xml b/profileinstaller/profileinstaller/lint-baseline.xml
new file mode 100644
index 0000000..f6e8bf9
--- /dev/null
+++ b/profileinstaller/profileinstaller/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/profileinstaller/BenchmarkOperation.java"/>
+ </issue>
+
+</issues>
diff --git a/recyclerview/recyclerview/lint-baseline.xml b/recyclerview/recyclerview/lint-baseline.xml
index c0f60da..65ec4fd 100644
--- a/recyclerview/recyclerview/lint-baseline.xml
+++ b/recyclerview/recyclerview/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -263,6 +263,87 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/recyclerview/widget/GridLayoutManager.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/recyclerview/widget/GridLayoutManager.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/recyclerview/widget/ItemTouchHelper.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/recyclerview/widget/ItemTouchUIUtilImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/recyclerview/widget/ItemTouchUIUtilImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 19"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/recyclerview/widget/RecyclerView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" || Build.VERSION.SDK_INT == 20;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/recyclerview/widget/RecyclerView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/recyclerview/widget/RecyclerView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/recyclerview/widget/RecyclerView.java"/>
+ </issue>
+
+ <issue
id="KotlinPropertyAccess"
message="This method should be called `getHasFixedSize` such that `hasFixedSize` can be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
errorLine1=" public boolean hasFixedSize() {"
diff --git a/room/integration-tests/kotlintestapp/lint-baseline.xml b/room/integration-tests/kotlintestapp/lint-baseline.xml
index ccd47d1..714f298 100644
--- a/room/integration-tests/kotlintestapp/lint-baseline.xml
+++ b/room/integration-tests/kotlintestapp/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.2.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.2.0-beta01)" variant="all" version="8.2.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -64,4 +64,13 @@
file="src/androidTest/java/androidx/room/integration/kotlintestapp/test/Rx3PagingSourceTest.kt"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/room/integration/kotlintestapp/dao/DependencyDao.kt"/>
+ </issue>
+
</issues>
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
index 1eb28cf..0166c7d 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
@@ -416,4 +416,57 @@
assertContentEquals(arrayOf(1, 2), resultArrayWithLong)
assertContentEquals(longArrayOf(1, 2), resultLongArray)
}
+
+ @Test
+ fun relation1to1() = runTest {
+ val sampleEntity1 = SampleEntity(1, 1)
+ val sampleEntity2 = SampleEntity2(1, 2)
+ db.dao().insert(sampleEntity1)
+ db.dao().insert(sampleEntity2)
+ assertThat(
+ db.dao().getSample1To2()
+ ).isEqualTo(
+ SampleDao.Sample1And2(sample1 = sampleEntity1, sample2 = sampleEntity2)
+ )
+ }
+
+ @Test
+ fun relation1toMany() = runTest {
+ val sampleEntity1 = SampleEntity(1, 1)
+ val sampleEntity2 = SampleEntity2(1, 2)
+ val sampleEntity2s = listOf(sampleEntity2, SampleEntity2(2, 3))
+
+ db.dao().insert(sampleEntity1)
+ db.dao().insertSampleEntity2List(sampleEntity2s)
+
+ assertThat(
+ db.dao().getSample1ToMany()
+ ).isEqualTo(
+ SampleDao.Sample1AndMany(
+ sample1 = sampleEntity1,
+ sample2s = listOf(sampleEntity2)
+ )
+ )
+ }
+
+ @Test
+ fun relationManytoMany() = runTest {
+ val sampleEntity1 = SampleEntity(1, 1)
+ val sampleEntity1s = listOf(sampleEntity1, SampleEntity(2, 2))
+
+ val sampleEntity2 = SampleEntity2(1, 1)
+ val sampleEntity2s = listOf(sampleEntity2, SampleEntity2(2, 2))
+
+ db.dao().insertSampleEntityList(sampleEntity1s)
+ db.dao().insertSampleEntity2List(sampleEntity2s)
+
+ assertThat(
+ db.dao().getSampleManyToMany()
+ ).isEqualTo(
+ SampleDao.SampleManyAndMany(
+ sample1 = sampleEntity1,
+ sample2s = listOf()
+ )
+ )
+ }
}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
index 8787aed..f41e88e 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
@@ -20,12 +20,16 @@
import androidx.room.Dao
import androidx.room.Database
import androidx.room.Delete
+import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.ForeignKey
+import androidx.room.Index
import androidx.room.Insert
+import androidx.room.Junction
import androidx.room.MapColumn
import androidx.room.PrimaryKey
import androidx.room.Query
+import androidx.room.Relation
import androidx.room.RewriteQueriesToDropUnusedColumns
import androidx.room.RoomDatabase
import androidx.room.Transaction
@@ -71,6 +75,15 @@
val dataCopy: Long
)
+@Entity(
+ primaryKeys = ["sample1Key", "sample2Key"],
+ indices = [Index("sample1Key"), Index("sample2Key")]
+)
+data class Sample1Sample2XRef(
+ val sample1Key: Long,
+ val sample2Key: Long,
+)
+
@Dao
interface SampleDao {
@@ -142,6 +155,12 @@
suspend fun insertArray(entities: Array<SampleEntity>)
@Insert
+ suspend fun insertSampleEntityList(entities: List<SampleEntity>)
+
+ @Insert
+ suspend fun insertSampleEntity2List(entities: List<SampleEntity2>)
+
+ @Insert
suspend fun insert(entity: SampleEntity2)
@Insert
@@ -167,6 +186,47 @@
@Query("SELECT pk FROM SampleEntity")
suspend fun queryOfLongArray(): LongArray
+
+ @Transaction
+ @Query("SELECT * FROM SampleEntity")
+ suspend fun getSample1To2(): Sample1And2
+
+ @Transaction
+ @Query("SELECT * FROM SampleEntity")
+ suspend fun getSample1ToMany(): Sample1AndMany
+
+ @Transaction
+ @Query("SELECT * FROM SampleEntity")
+ suspend fun getSampleManyToMany(): SampleManyAndMany
+
+ data class Sample1And2(
+ @Embedded
+ val sample1: SampleEntity,
+ @Relation(parentColumn = "pk", entityColumn = "pk2")
+ val sample2: SampleEntity2
+ )
+
+ data class Sample1AndMany(
+ @Embedded
+ val sample1: SampleEntity,
+ @Relation(parentColumn = "pk", entityColumn = "pk2")
+ val sample2s: List<SampleEntity2>
+ )
+
+ data class SampleManyAndMany(
+ @Embedded
+ val sample1: SampleEntity,
+ @Relation(
+ parentColumn = "pk",
+ entityColumn = "pk2",
+ associateBy = Junction(
+ value = Sample1Sample2XRef::class,
+ parentColumn = "sample1Key",
+ entityColumn = "sample2Key"
+ )
+ )
+ val sample2s: List<SampleEntity2>
+ )
}
@Database(
@@ -174,7 +234,8 @@
SampleEntity::class,
SampleEntity2::class,
SampleEntity3::class,
- SampleEntityCopy::class],
+ SampleEntityCopy::class,
+ Sample1Sample2XRef::class],
version = 1,
exportSchema = false
)
diff --git a/room/integration-tests/testapp/lint-baseline.xml b/room/integration-tests/testapp/lint-baseline.xml
index eaa9ec8..171a19c 100644
--- a/room/integration-tests/testapp/lint-baseline.xml
+++ b/room/integration-tests/testapp/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -94,15 +94,6 @@
<issue
id="BanThreadSleep"
message="Uses Thread.sleep()"
- errorLine1=" Thread.sleep(100); // Let db auto close"
- errorLine2=" ~~~~~">
- <location
- file="src/androidTest/java/androidx/room/integration/testapp/test/AutoClosingRoomOpenHelperTest.java"/>
- </issue>
-
- <issue
- id="BanThreadSleep"
- message="Uses Thread.sleep()"
errorLine1=" Thread.sleep(delayMillis, delayNanos);"
errorLine2=" ~~~~~">
<location
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/AutoClosingRoomOpenHelperTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/AutoClosingRoomOpenHelperTest.java
index 0419088..79fe546 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/AutoClosingRoomOpenHelperTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/AutoClosingRoomOpenHelperTest.java
@@ -43,6 +43,7 @@
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
@@ -253,6 +254,8 @@
assertFalse(testDatabase.isOpen());
}
+ // TODO(336671494): broken test
+ @Ignore
@Test
@MediumTest
public void invalidationObserver_isCalledOnEachInvalidation()
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultiInstanceInvalidationTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultiInstanceInvalidationTest.java
index bf9a575..43e65e1 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultiInstanceInvalidationTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultiInstanceInvalidationTest.java
@@ -208,6 +208,8 @@
assertThat(db1.getCustomerDao().countCustomers(), is(1));
}
+ // TODO(335890993): broken test
+ @Ignore
@Test
public void invalidationInAnotherInstance_closed() throws Exception {
final SampleDatabase db1 = openDatabase(true);
diff --git a/room/room-compiler-processing/build.gradle b/room/room-compiler-processing/build.gradle
index 42a9e97..2bc4be2 100644
--- a/room/room-compiler-processing/build.gradle
+++ b/room/room-compiler-processing/build.gradle
@@ -81,7 +81,6 @@
shadowed(libs.kotlinMetadataJvm) {
exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib"
}
- implementation(libs.intellijAnnotations)
implementation(libs.kspApi)
implementation(libs.kotlinStdlibJdk8) // KSP defines older version as dependency, force update.
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt
index 0171412..5296e39 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt
@@ -148,6 +148,15 @@
kotlin = com.squareup.kotlinpoet.ANY
)
+ /**
+ * A convenience [XTypeName] that represents [kotlin.Enum] in Kotlin and
+ * [java.lang.Enum] in Java.
+ */
+ val ENUM = XTypeName(
+ java = JClassName.get(java.lang.Enum::class.java),
+ kotlin = com.squareup.kotlinpoet.ENUM
+ )
+
val PRIMITIVE_BOOLEAN = Boolean::class.asPrimitiveTypeName()
val PRIMITIVE_BYTE = Byte::class.asPrimitiveTypeName()
val PRIMITIVE_SHORT = Short::class.asPrimitiveTypeName()
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeKotlinPoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeKotlinPoetExt.kt
index bf9c0d6..7a80d08 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeKotlinPoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeKotlinPoetExt.kt
@@ -26,6 +26,7 @@
import com.google.devtools.ksp.symbol.KSTypeArgument
import com.google.devtools.ksp.symbol.KSTypeParameter
import com.google.devtools.ksp.symbol.KSTypeReference
+import com.google.devtools.ksp.symbol.Nullability
import com.google.devtools.ksp.symbol.Variance
import com.squareup.kotlinpoet.ANY
import com.squareup.kotlinpoet.KModifier
@@ -92,8 +93,16 @@
val mutableBounds = mutableListOf(ANY.copy(nullable = true))
val typeName = createModifiableTypeVariableName(name = name.asString(), bounds = mutableBounds)
typeArgumentTypeLookup[name] = typeName
- val resolvedBounds = bounds.map {
- it.asKTypeName(resolver, typeArgumentTypeLookup)
+ val resolvedBounds = bounds.map { typeReference ->
+ typeReference.asKTypeName(resolver, typeArgumentTypeLookup).let { kTypeName ->
+ typeReference.resolve().let {
+ if (it.nullability == Nullability.PLATFORM) {
+ kTypeName.copy(nullable = true)
+ } else {
+ kTypeName
+ }
+ }
+ }
}.toList()
if (resolvedBounds.isNotEmpty()) {
mutableBounds.addAll(resolvedBounds)
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodTypeVariableType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodTypeVariableType.kt
index 5a9d520..748503b 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodTypeVariableType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodTypeVariableType.kt
@@ -25,6 +25,7 @@
import androidx.room.compiler.processing.XTypeVariableType
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSTypeParameter
+import com.google.devtools.ksp.symbol.Nullability
import com.squareup.javapoet.TypeName
import kotlin.reflect.KClass
@@ -53,7 +54,16 @@
)
}
- override val upperBounds: List<XType> = ksTypeVariable.bounds.map(env::wrap).toList()
+ override val upperBounds: List<XType> = ksTypeVariable.bounds.map {
+ val type = it.resolve().let {
+ if (it.nullability == Nullability.PLATFORM) {
+ it.withNullability(XNullability.NULLABLE)
+ } else {
+ it
+ }
+ }
+ env.wrap(it, type)
+ }.toList()
override fun annotations(): Sequence<KSAnnotation> {
return ksTypeVariable.annotations
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeParameterElement.kt
index 64e518b..26fa62a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeParameterElement.kt
@@ -18,10 +18,12 @@
import androidx.room.compiler.processing.XAnnotated
import androidx.room.compiler.processing.XMemberContainer
+import androidx.room.compiler.processing.XNullability
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.XTypeParameterElement
import androidx.room.compiler.processing.ksp.KspAnnotated.UseSiteFilter.Companion.NO_USE_SITE_OR_FIELD
import com.google.devtools.ksp.symbol.KSTypeParameter
+import com.google.devtools.ksp.symbol.Nullability
import com.squareup.javapoet.TypeVariableName
internal class KspTypeParameterElement(
@@ -43,7 +45,16 @@
}
override val bounds: List<XType> by lazy {
- declaration.bounds.map { env.wrap(it, it.resolve()) }.toList().ifEmpty {
+ declaration.bounds.map {
+ val type = it.resolve().let {
+ if (it.nullability == Nullability.PLATFORM) {
+ it.withNullability(XNullability.NULLABLE)
+ } else {
+ it
+ }
+ }
+ env.wrap(it, type)
+ }.toList().ifEmpty {
listOf(env.requireType(Any::class).makeNullable())
}
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
index da629f7..46e753d 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
@@ -18,6 +18,7 @@
import androidx.kruth.assertThat
import androidx.room.compiler.codegen.JArrayTypeName
+import androidx.room.compiler.processing.compat.XConverters.toKS
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.asJClassName
@@ -1284,7 +1285,10 @@
val kClassKTypeName = kotlin.reflect.KClass::class.asKClassName().parameterizedBy(STAR)
fun checkSingleValue(annotationValue: XAnnotationValue, expectedValue: String) {
// TODO(bcorso): Consider making the value types match in this case.
- if (!invocation.isKsp || (sourceKind == SourceKind.JAVA && !isPreCompiled)) {
+ if (!invocation.isKsp ||
+ (invocation.processingEnv.toKS().kspVersion < KotlinVersion(2, 0) &&
+ sourceKind == SourceKind.JAVA &&
+ !isPreCompiled)) {
assertThat(annotationValue.valueType.asTypeName().java)
.isEqualTo(classJTypeName)
} else {
@@ -1299,7 +1303,10 @@
fun checkListValues(annotationValue: XAnnotationValue, vararg expectedValues: String) {
// TODO(bcorso): Consider making the value types match in this case.
- if (!invocation.isKsp || (sourceKind == SourceKind.JAVA && !isPreCompiled)) {
+ if (!invocation.isKsp ||
+ (invocation.processingEnv.toKS().kspVersion < KotlinVersion(2, 0) &&
+ sourceKind == SourceKind.JAVA &&
+ !isPreCompiled)) {
assertThat(annotationValue.valueType.asTypeName().java)
.isEqualTo(JArrayTypeName.of(classJTypeName))
} else {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
index e83bbbe..9a83501 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
@@ -244,24 +244,31 @@
validateMethodElement(
element = it.processingEnv.requireTypeElement("foo.bar.Base"),
- tTypeName = XTypeName.getTypeVariableName("T", listOf(XTypeName.ANY_OBJECT)),
- rTypeName = XTypeName.getTypeVariableName("R", listOf(XTypeName.ANY_OBJECT))
+ tTypeName = XTypeName.getTypeVariableName("T", listOf(
+ XTypeName.ANY_OBJECT.copy(nullable = true))),
+ rTypeName = XTypeName.getTypeVariableName("R", listOf(
+ XTypeName.ANY_OBJECT.copy(nullable = true)))
)
validateMethodElement(
element = it.processingEnv.requireTypeElement("foo.bar.Child"),
- tTypeName = XTypeName.getTypeVariableName("T", listOf(XTypeName.ANY_OBJECT)),
- rTypeName = XTypeName.getTypeVariableName("R", listOf(XTypeName.ANY_OBJECT))
+ tTypeName = XTypeName.getTypeVariableName("T", listOf(
+ XTypeName.ANY_OBJECT.copy(nullable = true))),
+ rTypeName = XTypeName.getTypeVariableName("R", listOf(
+ XTypeName.ANY_OBJECT.copy(nullable = true)))
)
validateMethodTypeAsMemberOf(
element = it.processingEnv.requireTypeElement("foo.bar.Base"),
- tTypeName = XTypeName.getTypeVariableName("T", listOf(XTypeName.ANY_OBJECT)),
- rTypeName = XTypeName.getTypeVariableName("R", listOf(XTypeName.ANY_OBJECT))
+ tTypeName = XTypeName.getTypeVariableName("T", listOf(
+ XTypeName.ANY_OBJECT.copy(nullable = true))),
+ rTypeName = XTypeName.getTypeVariableName("R", listOf(
+ XTypeName.ANY_OBJECT.copy(nullable = true)))
)
validateMethodTypeAsMemberOf(
element = it.processingEnv.requireTypeElement("foo.bar.Child"),
tTypeName = String::class.asClassName(),
- rTypeName = XTypeName.getTypeVariableName("R", listOf(XTypeName.ANY_OBJECT))
+ rTypeName = XTypeName.getTypeVariableName("R", listOf(
+ XTypeName.ANY_OBJECT.copy(nullable = true)))
)
}
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
index 4d9dcea..8320561 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
@@ -22,8 +22,9 @@
import androidx.room.compiler.processing.util.CONTINUATION_JCLASS_NAME
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.UNIT_JCLASS_NAME
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.getMethodByJvmName
-import androidx.room.compiler.processing.util.runKspTest
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.typeName
import com.google.common.truth.Truth
@@ -581,7 +582,8 @@
"""
class KotlinSubject {
fun <T> oneTypeVar(): Unit = TODO()
- fun <T : MutableList<*>> oneBoundedTypeVar(): Unit = TODO()
+ fun <T : MutableList<*>?> oneBoundedTypeVar(): Unit = TODO()
+ fun <T : MutableList<*>> oneBoundedTypeVarNotNull(): Unit = TODO()
fun <A, B> twoTypeVar(param: B): A = TODO()
}
""".trimIndent()
@@ -590,15 +592,18 @@
"JavaSubject",
"""
import java.util.List;
+ import org.jetbrains.annotations.NotNull;
class JavaSubject {
<T> void oneTypeVar() {}
<T extends List<?>> void oneBoundedTypeVar() { }
+ <T extends @NotNull List<?>> void oneBoundedTypeVarNotNull() { }
<A, B> A twoTypeVar(B param) { return null; }
}
""".trimIndent()
)
- runKspTest(sources = listOf(kotlinSrc, javaSrc)) { invocation ->
- listOf("KotlinSubject", "JavaSubject",).forEach { subjectFqn ->
+
+ fun handler(invocation: XTestInvocation) {
+ listOf("KotlinSubject", "JavaSubject").forEach { subjectFqn ->
val subject = invocation.processingEnv.requireTypeElement(subjectFqn)
subject.getMethodByJvmName("oneTypeVar").let {
val typeVar = it.executableType.typeVariables.single()
@@ -618,6 +623,29 @@
bounds = listOf(
List::class.asMutableClassName()
.parametrizedBy(XTypeName.ANY_WILDCARD)
+ .copy(nullable = true)
+ )
+ )
+ )
+ assertThat(typeVar.superTypes.map { it.asTypeName() })
+ .containsExactly(
+ XTypeName.ANY_OBJECT.copy(nullable = true),
+ List::class.asMutableClassName()
+ .parametrizedBy(XTypeName.ANY_WILDCARD)
+ .copy(nullable = true)
+ )
+ assertThat(typeVar.typeArguments).isEmpty()
+ assertThat(typeVar.typeElement).isNull()
+ }
+ subject.getMethodByJvmName("oneBoundedTypeVarNotNull").let {
+ val typeVar = it.executableType.typeVariables.single()
+ assertThat(typeVar.asTypeName())
+ .isEqualTo(
+ XTypeName.getTypeVariableName(
+ name = "T",
+ bounds = listOf(
+ List::class.asMutableClassName()
+ .parametrizedBy(XTypeName.ANY_WILDCARD)
)
)
)
@@ -646,5 +674,7 @@
}
}
}
+ runProcessorTest(sources = listOf(kotlinSrc, javaSrc), handler = ::handler)
+ runProcessorTest(classpath = compileFiles(listOf(kotlinSrc, javaSrc)), handler = ::handler)
}
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index 4b190b5..f1dfb5f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -21,6 +21,7 @@
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.asClassName
+import androidx.room.compiler.processing.compat.XConverters.toKS
import androidx.room.compiler.processing.javac.JavacType
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
@@ -1888,7 +1889,13 @@
// TODO(kuanyingchou): https://github.com/google/ksp/issues/1761
val parent = typeElement.superClass!!.typeElement!!
if (qName == "test.KotlinEnum" && !isPreCompiled && invocation.isKsp) {
- assertThat(parent.asClassName()).isEqualTo(Any::class.asClassName())
+ if (invocation.isKsp &&
+ invocation.processingEnv.toKS().kspVersion >=
+ KotlinVersion(2, 0)) {
+ assertThat(parent.asClassName()).isEqualTo(XTypeName.ENUM)
+ } else {
+ assertThat(parent.asClassName()).isEqualTo(Any::class.asClassName())
+ }
} else {
assertThat(parent.asClassName()).isEqualTo(Enum::class.asClassName())
}
@@ -1896,17 +1903,17 @@
val methodNames = typeElement.getDeclaredMethods().map { it.jvmName }
if (qName == "test.KotlinEnum") {
if (invocation.isKsp) {
- if (isPreCompiled) {
+ if (!isPreCompiled && invocation.processingEnv.toKS().kspVersion <
+ KotlinVersion(2, 0)) {
+ assertThat(methodNames).containsExactly(
+ "enumMethod",
+ )
+ } else {
assertThat(methodNames).containsExactly(
"enumMethod",
"values",
"valueOf",
)
- } else {
- // `values` and `valueOf` will be added in KSP2.
- assertThat(methodNames).containsExactly(
- "enumMethod",
- )
}
} else {
assertThat(methodNames).containsExactly(
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
index d14fea3..f3e289d 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
@@ -182,20 +182,28 @@
val bound0 = t.bounds[0]
assertThat(bound0.asTypeName().java.toString()).isEqualTo("Bar")
if (invocation.isKsp) {
- assertThat(bound0.asTypeName().kotlin.toString()).isEqualTo("Bar")
+ assertThat(bound0.asTypeName().kotlin.toString()).isEqualTo("Bar?")
}
- val bar = invocation.processingEnv.requireType("Bar")
+ val bar = invocation.processingEnv.requireType("Bar").makeNullable()
assertThat(bound0.isSameType(bar)).isTrue()
- assertThat(bound0.nullability).isEqualTo(XNullability.UNKNOWN)
+ if (invocation.isKsp) {
+ assertThat(bound0.nullability).isEqualTo(XNullability.NULLABLE)
+ } else {
+ assertThat(bound0.nullability).isEqualTo(XNullability.UNKNOWN)
+ }
val bound1 = t.bounds[1]
assertThat(bound1.asTypeName().java.toString()).isEqualTo("Baz")
if (invocation.isKsp) {
- assertThat(bound1.asTypeName().kotlin.toString()).isEqualTo("Baz")
+ assertThat(bound1.asTypeName().kotlin.toString()).isEqualTo("Baz?")
}
- val baz = invocation.processingEnv.requireType("Baz")
+ val baz = invocation.processingEnv.requireType("Baz").makeNullable()
assertThat(bound1.isSameType(baz)).isTrue()
- assertThat(bound1.nullability).isEqualTo(XNullability.UNKNOWN)
+ if (invocation.isKsp) {
+ assertThat(bound1.nullability).isEqualTo(XNullability.NULLABLE)
+ } else {
+ assertThat(bound1.nullability).isEqualTo(XNullability.UNKNOWN)
+ }
}
}
@@ -333,20 +341,28 @@
val bound0 = t.bounds[0]
assertThat(bound0.asTypeName().java.toString()).isEqualTo("Bar")
if (invocation.isKsp) {
- assertThat(bound0.asTypeName().kotlin.toString()).isEqualTo("Bar")
+ assertThat(bound0.asTypeName().kotlin.toString()).isEqualTo("Bar?")
}
- val bar = invocation.processingEnv.requireType("Bar")
+ val bar = invocation.processingEnv.requireType("Bar").makeNullable()
assertThat(bound0.isSameType(bar)).isTrue()
- assertThat(bound0.nullability).isEqualTo(XNullability.UNKNOWN)
+ if (invocation.isKsp) {
+ assertThat(bound0.nullability).isEqualTo(XNullability.NULLABLE)
+ } else {
+ assertThat(bound0.nullability).isEqualTo(XNullability.UNKNOWN)
+ }
val bound1 = t.bounds[1]
assertThat(bound1.asTypeName().java.toString()).isEqualTo("Baz")
if (invocation.isKsp) {
- assertThat(bound1.asTypeName().kotlin.toString()).isEqualTo("Baz")
+ assertThat(bound1.asTypeName().kotlin.toString()).isEqualTo("Baz?")
}
- val baz = invocation.processingEnv.requireType("Baz")
+ val baz = invocation.processingEnv.requireType("Baz").makeNullable()
assertThat(bound1.isSameType(baz)).isTrue()
- assertThat(bound1.nullability).isEqualTo(XNullability.UNKNOWN)
+ if (invocation.isKsp) {
+ assertThat(bound1.nullability).isEqualTo(XNullability.NULLABLE)
+ } else {
+ assertThat(bound1.nullability).isEqualTo(XNullability.UNKNOWN)
+ }
}
}
@@ -381,20 +397,28 @@
val bound0 = t.bounds[0]
assertThat(bound0.asTypeName().java.toString()).isEqualTo("Bar")
if (invocation.isKsp) {
- assertThat(bound0.asTypeName().kotlin.toString()).isEqualTo("Bar")
+ assertThat(bound0.asTypeName().kotlin.toString()).isEqualTo("Bar?")
}
- val bar = invocation.processingEnv.requireType("Bar")
+ val bar = invocation.processingEnv.requireType("Bar").makeNullable()
assertThat(bound0.isSameType(bar)).isTrue()
- assertThat(bound0.nullability).isEqualTo(XNullability.UNKNOWN)
+ if (invocation.isKsp) {
+ assertThat(bound0.nullability).isEqualTo(XNullability.NULLABLE)
+ } else {
+ assertThat(bound0.nullability).isEqualTo(XNullability.UNKNOWN)
+ }
val bound1 = t.bounds[1]
assertThat(bound1.asTypeName().java.toString()).isEqualTo("Baz")
if (invocation.isKsp) {
- assertThat(bound1.asTypeName().kotlin.toString()).isEqualTo("Baz")
+ assertThat(bound1.asTypeName().kotlin.toString()).isEqualTo("Baz?")
}
- val baz = invocation.processingEnv.requireType("Baz")
+ val baz = invocation.processingEnv.requireType("Baz").makeNullable()
assertThat(bound1.isSameType(baz)).isTrue()
- assertThat(bound1.nullability).isEqualTo(XNullability.UNKNOWN)
+ if (invocation.isKsp) {
+ assertThat(bound1.nullability).isEqualTo(XNullability.NULLABLE)
+ } else {
+ assertThat(bound1.nullability).isEqualTo(XNullability.UNKNOWN)
+ }
assertThat(constructor.parameters).hasSize(1)
val parameter = constructor.parameters[0]
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index 7b32220..30422ec 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -94,6 +94,7 @@
KTypeVariableName(
"InputStreamType",
KClassName("java.io", "InputStream")
+ .copy(nullable = true)
)
)
)
@@ -120,12 +121,14 @@
val expected = KTypeVariableName(
"InputStreamType",
KClassName("java.io", "InputStream")
+ .copy(nullable = true)
)
assertThat(firstType.asTypeName().kotlin).isEqualTo(expected)
assertThat(
(firstType.asTypeName().kotlin as KTypeVariableName).bounds
).containsExactly(
KClassName("java.io", "InputStream")
+ .copy(nullable = true)
)
}
}
@@ -622,8 +625,16 @@
assertThat(typeElement.type.asTypeName().java.dumpToString(5))
.isEqualTo(expectedTypeStringDump)
if (invocation.isKsp) {
+ val expectedTypeStringDumpKotlin = """
+ SelfReferencing<T>
+ | T
+ | > SelfReferencing<T>?
+ | > | T
+ | > | > SelfReferencing<T>?
+ | > | > | T
+ """.trimIndent()
assertThat(typeElement.type.asTypeName().kotlin.dumpToString(5))
- .isEqualTo(expectedTypeStringDump)
+ .isEqualTo(expectedTypeStringDumpKotlin)
}
val expectedParamStringDump = """
SelfReferencing
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt b/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
index 2e69a12..5a051fd 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
@@ -94,15 +94,20 @@
prepareDaosForWriting(databases, it.keys.toList())
it.forEach { (daoMethod, db) ->
DaoWriter(
- daoMethod.dao,
- db.element,
- context.codeLanguage
+ dao = daoMethod.dao,
+ dbElement = db.element,
+ codeLanguage = context.codeLanguage,
+ javaLambdaSyntaxAvailable = context.javaLambdaSyntaxAvailable
).write(context.processingEnv)
}
}
databases?.forEach { db ->
- DatabaseWriter(db, context.codeLanguage).write(context.processingEnv)
+ DatabaseWriter(
+ database = db,
+ codeLanguage = context.codeLanguage,
+ javaLambdaSyntaxAvailable = context.javaLambdaSyntaxAvailable
+ ).write(context.processingEnv)
if (db.exportSchema) {
val qName = db.element.qualifiedName
val filename = "${db.version}.json"
@@ -131,8 +136,12 @@
}
}
db.autoMigrations.forEach { autoMigration ->
- AutoMigrationWriter(db.element, autoMigration, context.codeLanguage)
- .write(context.processingEnv)
+ AutoMigrationWriter(
+ autoMigration = autoMigration,
+ dbElement = db.element,
+ codeLanguage = context.codeLanguage,
+ javaLambdaSyntaxAvailable = context.javaLambdaSyntaxAvailable
+ ).write(context.processingEnv)
}
if (context.codeLanguage == CodeLanguage.KOTLIN) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
index 7b40165..f14ebe9 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
@@ -22,12 +22,14 @@
import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XFunSpec
import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.apply
+import androidx.room.compiler.codegen.XMemberName
import androidx.room.compiler.codegen.XMemberName.Companion.companionMember
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.XTypeSpec
import androidx.room.compiler.codegen.asClassName
import androidx.room.compiler.codegen.asMutableClassName
+import androidx.room.solver.CodeGenScope
import com.squareup.kotlinpoet.javapoet.JTypeName
import java.util.concurrent.Callable
@@ -405,6 +407,126 @@
}.build()
/**
+ * Generates a code block that invokes a function with a functional type as last parameter.
+ *
+ * For Java (jvmTarget >= 8) it will generate:
+ * ```
+ * <functionName>(<args>, (<lambdaSpec.paramName>) -> <lambdaSpec.body>);
+ * ```
+ * For Java (jvmTarget < 8) it will generate:
+ * ```
+ * <functionName>(<args>, new Function1<>() { <lambdaSpec.body> });
+ * ```
+ * For Kotlin it will generate:
+ * ```
+ * <functionName>(<args>) { <lambdaSpec.body> }
+ * ```
+ *
+ * The ideal usage of this utility function is to generate code that invokes the various
+ * `DBUtil.perform*()` APIs for interacting with the database connection in DAOs.
+ */
+fun InvokeWithLambdaParameter(
+ scope: CodeGenScope,
+ functionName: XMemberName,
+ argFormat: List<String>,
+ args: List<Any>,
+ continuationParamName: String? = null,
+ lambdaSpec: LambdaSpec
+): XCodeBlock = XCodeBlock.builder(scope.language).apply {
+ check(argFormat.size == args.size)
+ when (language) {
+ CodeLanguage.JAVA -> {
+ if (lambdaSpec.javaLambdaSyntaxAvailable) {
+ val argsFormatString = argFormat.joinToString(separator = ", ")
+ add(
+ "%M($argsFormatString, (%L) -> {\n",
+ functionName,
+ *args.toTypedArray(),
+ lambdaSpec.parameterName
+ )
+ indent()
+ val bodyScope = scope.fork()
+ with(lambdaSpec) { bodyScope.builder.body(bodyScope) }
+ add(bodyScope.generate())
+ unindent()
+ add("}")
+ if (continuationParamName != null) {
+ add(", %L", continuationParamName)
+ }
+ add(");\n")
+ } else {
+ val adjustedArgsFormatString = buildList {
+ addAll(argFormat)
+ add("%L") // the anonymous function
+ if (continuationParamName != null) {
+ add("%L")
+ }
+ }.joinToString(separator = ", ")
+ val adjustedArgs = buildList {
+ addAll(args)
+ add(
+ Function1TypeSpec(
+ language = language,
+ parameterTypeName = lambdaSpec.parameterTypeName,
+ parameterName = lambdaSpec.parameterName,
+ returnTypeName = lambdaSpec.returnTypeName,
+ callBody = {
+ val bodyScope = scope.fork()
+ with(lambdaSpec) { bodyScope.builder.body(bodyScope) }
+ addCode(bodyScope.generate())
+ }
+ )
+ )
+ if (continuationParamName != null) {
+ add(continuationParamName)
+ }
+ }
+ add(
+ "%M($adjustedArgsFormatString);\n",
+ functionName,
+ *adjustedArgs.toTypedArray(),
+ )
+ }
+ }
+ CodeLanguage.KOTLIN -> {
+ val argsFormatString = argFormat.joinToString(separator = ", ")
+ if (lambdaSpec.parameterTypeName.rawTypeName != KotlinTypeNames.CONTINUATION) {
+ add(
+ "%M($argsFormatString) { %L ->\n",
+ functionName,
+ *args.toTypedArray(),
+ lambdaSpec.parameterName
+ )
+ } else {
+ add(
+ "%M($argsFormatString) {\n",
+ functionName,
+ *args.toTypedArray(),
+ )
+ }
+ indent()
+ val bodyScope = scope.fork()
+ with(lambdaSpec) { bodyScope.builder.body(bodyScope) }
+ add(bodyScope.generate())
+ unindent()
+ add("}\n")
+ }
+ }
+}.build()
+
+/**
+ * Describes the lambda to be generated with [InvokeWithLambdaParameter].
+ */
+abstract class LambdaSpec(
+ val parameterTypeName: XTypeName,
+ val parameterName: String,
+ val returnTypeName: XTypeName,
+ val javaLambdaSyntaxAvailable: Boolean
+) {
+ abstract fun XCodeBlock.Builder.body(scope: CodeGenScope)
+}
+
+/**
* Generates an array literal with the given [values]
*
* Example: `ArrayLiteral(XTypeName.PRIMITIVE_INT, 1, 2, 3)`
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
index 1e6377a..add440f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
@@ -99,6 +99,11 @@
}
}
+ // Whether Java 8's lambda syntax is available to be emitted or not.
+ val javaLambdaSyntaxAvailable by lazy {
+ processingEnv.jvmVersion >= 8
+ }
+
companion object {
val ARG_OPTIONS by lazy {
ProcessorOptions.values().map { it.argName } +
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt
index da3bba1..31ea22f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt
@@ -187,7 +187,6 @@
jvmMethodName = executableElement.jvmName,
callType = callType
),
- javaLambdaSyntaxAvailable = context.processingEnv.jvmVersion >= 8
)
}
@@ -270,8 +269,7 @@
jvmMethodName = executableElement.jvmName,
callType = callType
),
- continuationParamName = continuationParam.name,
- javaLambdaSyntaxAvailable = context.processingEnv.jvmVersion >= 8
+ continuationParamName = continuationParam.name
)
private fun XCodeBlock.Builder.addCoroutineExecuteStatement(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index 99041b3..5c711a60 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -89,7 +89,7 @@
}
fun primaryKeyColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
- return "$columnName referenced in the primary key does not exists in the Entity." +
+ return "$columnName referenced in the primary key does not exist in the Entity." +
" Available column names:${allColumns.joinToString(", ")}"
}
@@ -389,7 +389,7 @@
val INDEX_COLUMNS_CANNOT_BE_EMPTY = "List of columns in an index cannot be empty"
fun indexColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
- return "$columnName referenced in the index does not exists in the Entity." +
+ return "$columnName referenced in the index does not exist in the Entity." +
" Available column names:${allColumns.joinToString(", ")}"
}
@@ -554,7 +554,7 @@
val FOREIGN_KEY_CANNOT_FIND_PARENT = "Cannot find parent entity class."
fun foreignKeyChildColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
- return "($columnName) referenced in the foreign key does not exists in the Entity." +
+ return "($columnName) referenced in the foreign key does not exist in the Entity." +
" Available column names:${allColumns.joinToString(", ")}"
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt
index e552cd7..a11d2dc 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt
@@ -16,7 +16,6 @@
package androidx.room.solver
-import androidx.annotation.VisibleForTesting
import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.writer.TypeWriter
@@ -29,6 +28,7 @@
val useDriverApi: Boolean = false
) {
val language = writer.codeLanguage
+ val javaLambdaSyntaxAvailable = writer.javaLambdaSyntaxAvailable
val builder by lazy { XCodeBlock.builder(language) }
private val tmpVarIndices = mutableMapOf<String, Int>()
@@ -36,8 +36,7 @@
const val TMP_VAR_DEFAULT_PREFIX = "_tmp"
const val CLASS_PROPERTY_PREFIX = "__"
- @VisibleForTesting
- fun getTmpVarString(index: Int) =
+ internal fun getTmpVarString(index: Int) =
getTmpVarString(TMP_VAR_DEFAULT_PREFIX, index)
private fun getTmpVarString(prefix: String, index: Int) =
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/CoroutinePreparedQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/CoroutinePreparedQueryResultBinder.kt
index 5172cacb..71d890b 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/CoroutinePreparedQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/CoroutinePreparedQueryResultBinder.kt
@@ -16,13 +16,14 @@
package androidx.room.solver.prepared.binder
-import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.box
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.solver.CodeGenScope
@@ -54,40 +55,21 @@
returnTypeName: XTypeName,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> executeAndReturnJava(
- sqlQueryVar, dbProperty, bindStatement, returnTypeName, scope
- )
- CodeLanguage.KOTLIN -> executeAndReturnKotlin(
- sqlQueryVar, dbProperty, bindStatement, scope
- )
- }
- }
-
- private fun executeAndReturnJava(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- returnTypeName: XTypeName,
- scope: CodeGenScope
- ) {
val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- scope.builder.addStatement(
- "return %M(%N, %L, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- // TODO(b/322387497): Generate lambda syntax if possible
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ false, /* inTransaction = */ true),
+ continuationParamName = continuationParamName,
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = returnTypeName.box()
+ returnTypeName = returnTypeName.box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+ val statementVar = scope.getTmpVar("_stmt")
addLocalVal(
statementVar,
SQLiteDriverTypeNames.STATEMENT,
@@ -96,49 +78,14 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(functionScope, statementVar)
- adapter?.executeAndReturn(connectionVar, statementVar, functionScope)
+ bindStatement(scope, statementVar)
+ adapter?.executeAndReturn(connectionVar, statementVar, scope)
nextControlFlow("finally")
addStatement("%L.close()", statementVar)
endControlFlow()
- }.build()
- this.addCode(functionCode)
- },
- continuationParamName
+ }
+ }
)
- }
-
- private fun executeAndReturnKotlin(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- scope: CodeGenScope
- ) {
- val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- connectionVar
- )
- addLocalVal(
- statementVar,
- SQLiteDriverTypeNames.STATEMENT,
- "%L.prepare(%L)",
- connectionVar,
- sqlQueryVar
- )
- beginControlFlow("try")
- bindStatement(scope, statementVar)
- adapter?.executeAndReturn(connectionVar, statementVar, scope)
- nextControlFlow("finally")
- addStatement("%L.close()", statementVar)
- endControlFlow()
- endControlFlow()
- }
+ scope.builder.add("return %L", performBlock)
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/InstantPreparedQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/InstantPreparedQueryResultBinder.kt
index a7a3591..2b40e63 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/InstantPreparedQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/InstantPreparedQueryResultBinder.kt
@@ -17,12 +17,14 @@
package androidx.room.solver.prepared.binder
import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.box
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.solver.CodeGenScope
@@ -61,41 +63,20 @@
returnTypeName: XTypeName,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> executeAndReturnJava(
- sqlQueryVar, dbProperty, bindStatement, returnTypeName, scope
- )
- CodeLanguage.KOTLIN -> executeAndReturnKotlin(
- sqlQueryVar, dbProperty, bindStatement, scope
- )
- }
- }
-
- private fun executeAndReturnJava(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- returnTypeName: XTypeName,
- scope: CodeGenScope
- ) {
val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- val returnPrefix = if (returnTypeName == XTypeName.UNIT_VOID) "" else "return "
- scope.builder.addStatement(
- "$returnPrefix%M(%N, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- // TODO(b/322387497): Generate lambda syntax if possible
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ false, /* inTransaction = */ true),
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = returnTypeName.box()
+ returnTypeName = returnTypeName.box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+ val statementVar = scope.getTmpVar("_stmt")
addLocalVal(
statementVar,
SQLiteDriverTypeNames.STATEMENT,
@@ -104,48 +85,18 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(functionScope, statementVar)
- adapter?.executeAndReturn(connectionVar, statementVar, functionScope)
+ bindStatement(scope, statementVar)
+ adapter?.executeAndReturn(connectionVar, statementVar, scope)
nextControlFlow("finally")
addStatement("%L.close()", statementVar)
endControlFlow()
- }.build()
- this.addCode(functionCode)
+ }
}
)
- }
-
- private fun executeAndReturnKotlin(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- scope: CodeGenScope
- ) {
- val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- connectionVar
- )
- addLocalVal(
- statementVar,
- SQLiteDriverTypeNames.STATEMENT,
- "%L.prepare(%L)",
- connectionVar,
- sqlQueryVar
- )
- beginControlFlow("try")
- bindStatement(scope, statementVar)
- adapter?.executeAndReturn(connectionVar, statementVar, scope)
- nextControlFlow("finally")
- addStatement("%L.close()", statementVar)
- endControlFlow()
- endControlFlow()
+ val returnPrefix = when (scope.language) {
+ CodeLanguage.JAVA -> if (returnTypeName == XTypeName.UNIT_VOID) "" else "return "
+ CodeLanguage.KOTLIN -> "return "
}
+ scope.builder.add("$returnPrefix%L", performBlock)
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
index 2a7ff10..89f6080 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
@@ -22,11 +22,13 @@
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.box
import androidx.room.compiler.processing.XType
import androidx.room.ext.ArrayLiteral
import androidx.room.ext.CallableTypeSpecBuilder
import androidx.room.ext.CommonTypeNames
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomCoroutinesTypeNames.COROUTINES_ROOM
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
@@ -99,53 +101,24 @@
CommonTypeNames.STRING,
*tableNames.toTypedArray()
)
- when (scope.language) {
- CodeLanguage.JAVA -> convertAndReturnJava(
- sqlQueryVar,
- dbProperty,
- bindStatement,
- inTransaction,
- arrayOfTableNamesLiteral,
- scope
- )
-
- CodeLanguage.KOTLIN -> convertAndReturnKotlin(
- sqlQueryVar,
- dbProperty,
- bindStatement,
- inTransaction,
- arrayOfTableNamesLiteral,
- scope
- )
- }
- }
-
- private fun convertAndReturnJava(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- inTransaction: Boolean,
- arrayOfTableNamesLiteral: XCodeBlock,
- scope: CodeGenScope
- ) {
val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- scope.builder.addStatement(
- "return %M(%N, %L, %L, %L)",
- RoomTypeNames.FLOW_UTIL.packageMember("createFlow"),
- dbProperty,
- inTransaction,
- arrayOfTableNamesLiteral,
- // TODO(b/322387497): Generate lambda syntax if possible
- Function1TypeSpec(
- language = scope.language,
+ val createBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.FLOW_UTIL.packageMember("createFlow"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, inTransaction, arrayOfTableNamesLiteral),
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = typeArg.asTypeName()
+ returnTypeName = returnTypeName.box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val outVar = functionScope.getTmpVar("_result")
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+ val returnPrefix = when (language) {
+ CodeLanguage.JAVA -> "return "
+ CodeLanguage.KOTLIN -> ""
+ }
+ val statementVar = scope.getTmpVar("_stmt")
addLocalVal(
statementVar,
SQLiteDriverTypeNames.STATEMENT,
@@ -154,53 +127,16 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(functionScope, statementVar)
- adapter?.convert(outVar, statementVar, functionScope)
- addStatement("return %L", outVar)
+ bindStatement(scope, statementVar)
+ val outVar = scope.getTmpVar("_result")
+ adapter?.convert(outVar, statementVar, scope)
+ addStatement("$returnPrefix%L", outVar)
nextControlFlow("finally")
addStatement("%L.close()", statementVar)
endControlFlow()
- }.build()
- this.addCode(functionCode)
+ }
}
)
- }
-
- private fun convertAndReturnKotlin(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- inTransaction: Boolean,
- arrayOfTableNamesLiteral: XCodeBlock,
- scope: CodeGenScope
- ) {
- val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.FLOW_UTIL.packageMember("createFlow"),
- dbProperty,
- inTransaction,
- arrayOfTableNamesLiteral,
- connectionVar
- )
- addLocalVal(
- statementVar,
- SQLiteDriverTypeNames.STATEMENT,
- "%L.prepare(%L)",
- connectionVar,
- sqlQueryVar
- )
- beginControlFlow("try")
- bindStatement(scope, statementVar)
- val outVar = scope.getTmpVar("_result")
- adapter?.convert(outVar, statementVar, scope)
- addStatement("%L", outVar)
- nextControlFlow("finally")
- addStatement("%L.close()", statementVar)
- endControlFlow()
- endControlFlow()
- }
+ scope.builder.add("return %L", createBlock)
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
index e376b18..8d955de 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
@@ -27,7 +27,8 @@
import androidx.room.compiler.processing.XType
import androidx.room.ext.AndroidTypeNames
import androidx.room.ext.CallableTypeSpecBuilder
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomCoroutinesTypeNames
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
@@ -152,42 +153,25 @@
inTransaction: Boolean,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> convertAndReturnJava(
- sqlQueryVar, dbProperty, bindStatement, returnTypeName, inTransaction, scope
- )
- CodeLanguage.KOTLIN -> convertAndReturnKotlin(
- sqlQueryVar, dbProperty, bindStatement, inTransaction, scope
- )
- }
- }
-
- private fun convertAndReturnJava(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- returnTypeName: XTypeName,
- inTransaction: Boolean,
- scope: CodeGenScope
- ) {
val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- scope.builder.addStatement(
- "return %M(%N, %L, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- true, // isReadOnly
- inTransaction,
- // TODO(b/322387497): Generate lambda syntax if possible
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ true, inTransaction),
+ continuationParamName = continuationParamName,
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = returnTypeName.box()
+ returnTypeName = returnTypeName.box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val outVar = functionScope.getTmpVar("_result")
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+ val returnPrefix = when (language) {
+ CodeLanguage.JAVA -> "return "
+ CodeLanguage.KOTLIN -> ""
+ }
+ val statementVar = scope.getTmpVar("_stmt")
addLocalVal(
statementVar,
SQLiteDriverTypeNames.STATEMENT,
@@ -196,53 +180,16 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(functionScope, statementVar)
- adapter?.convert(outVar, statementVar, functionScope)
- addStatement("return %L", outVar)
+ bindStatement(scope, statementVar)
+ val outVar = scope.getTmpVar("_result")
+ adapter?.convert(outVar, statementVar, scope)
+ addStatement("$returnPrefix%L", outVar)
nextControlFlow("finally")
addStatement("%L.close()", statementVar)
endControlFlow()
- }.build()
- this.addCode(functionCode)
- },
- continuationParamName
+ }
+ }
)
- }
-
- private fun convertAndReturnKotlin(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- inTransaction: Boolean,
- scope: CodeGenScope
- ) {
- val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- true, // isReadOnly
- inTransaction,
- connectionVar
- )
- scope.builder.addLocalVal(
- statementVar,
- SQLiteDriverTypeNames.STATEMENT,
- "%L.prepare(%L)",
- connectionVar,
- sqlQueryVar
- )
- beginControlFlow("try")
- bindStatement(scope, statementVar)
- val outVar = scope.getTmpVar("_result")
- adapter?.convert(outVar, statementVar, scope)
- addStatement("%L", outVar)
- nextControlFlow("finally")
- addStatement("%L.close()", statementVar)
- endControlFlow()
- endControlFlow()
- }
+ scope.builder.add("return %L", performBlock)
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
index d947e00..dc58000 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
@@ -23,7 +23,8 @@
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.codegen.box
import androidx.room.ext.AndroidTypeNames
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.solver.CodeGenScope
@@ -91,42 +92,24 @@
inTransaction: Boolean,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> convertAndReturnJava(
- sqlQueryVar, dbProperty, bindStatement, returnTypeName, inTransaction, scope
- )
- CodeLanguage.KOTLIN -> convertAndReturnKotlin(
- sqlQueryVar, dbProperty, bindStatement, inTransaction, scope
- )
- }
- }
-
- private fun convertAndReturnJava(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- returnTypeName: XTypeName,
- inTransaction: Boolean,
- scope: CodeGenScope
- ) {
val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- scope.builder.addStatement(
- "return %M(%N, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- true, // isReadOnly
- inTransaction,
- // TODO(b/322387497): Generate lambda syntax if possible
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ true, inTransaction),
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = returnTypeName.box()
+ returnTypeName = returnTypeName.box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val outVar = functionScope.getTmpVar("_result")
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+ val returnPrefix = when (language) {
+ CodeLanguage.JAVA -> "return "
+ CodeLanguage.KOTLIN -> ""
+ }
+ val statementVar = scope.getTmpVar("_stmt")
addLocalVal(
statementVar,
SQLiteDriverTypeNames.STATEMENT,
@@ -135,52 +118,16 @@
sqlQueryVar
)
beginControlFlow("try")
- bindStatement(functionScope, statementVar)
- adapter?.convert(outVar, statementVar, functionScope)
- addStatement("return %L", outVar)
+ bindStatement(scope, statementVar)
+ val outVar = scope.getTmpVar("_result")
+ adapter?.convert(outVar, statementVar, scope)
+ addStatement("$returnPrefix%L", outVar)
nextControlFlow("finally")
addStatement("%L.close()", statementVar)
endControlFlow()
- }.build()
- this.addCode(functionCode)
+ }
}
)
- }
-
- private fun convertAndReturnKotlin(
- sqlQueryVar: String,
- dbProperty: XPropertySpec,
- bindStatement: CodeGenScope.(String) -> Unit,
- inTransaction: Boolean,
- scope: CodeGenScope
- ) {
- val connectionVar = scope.getTmpVar("_connection")
- val statementVar = scope.getTmpVar("_stmt")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- true, // isReadOnly
- inTransaction,
- connectionVar
- )
- addLocalVal(
- statementVar,
- SQLiteDriverTypeNames.STATEMENT,
- "%L.prepare(%L)",
- connectionVar,
- sqlQueryVar
- )
- beginControlFlow("try")
- bindStatement(scope, statementVar)
- val outVar = scope.getTmpVar("_result")
- adapter?.convert(outVar, statementVar, scope)
- addStatement("%L", outVar)
- nextControlFlow("finally")
- addStatement("%L.close()", statementVar)
- endControlFlow()
- endControlFlow()
- }
+ scope.builder.add("return %L", performBlock)
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
index e7ff05d..e245f35 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
@@ -151,9 +151,7 @@
override fun getDefaultIndexAdapter() = indexAdapter
- override fun isMigratedToDriver(): Boolean {
- return relationCollectors.isEmpty()
- }
+ override fun isMigratedToDriver(): Boolean = relationCollectors.all { it.isMigratedToDriver() }
data class PojoMapping(
val pojo: Pojo,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineDeleteOrUpdateMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineDeleteOrUpdateMethodBinder.kt
index 6fd03bf..57a3081 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineDeleteOrUpdateMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineDeleteOrUpdateMethodBinder.kt
@@ -16,13 +16,14 @@
package androidx.room.solver.shortcut.binder
-import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeSpec
import androidx.room.compiler.codegen.box
import androidx.room.compiler.processing.XType
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.solver.CodeGenScope
@@ -44,80 +45,33 @@
dbProperty: XPropertySpec,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> convertAndReturnJava(
- parameters, adapters, dbProperty, scope
- )
- CodeLanguage.KOTLIN -> convertAndReturnKotlin(
- parameters, adapters, dbProperty, scope
- )
- }
- }
-
- private fun convertAndReturnJava(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
if (adapter == null) {
return
}
val connectionVar = scope.getTmpVar("_connection")
- scope.builder.addStatement(
- "return %M(%N, %L, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ false, /* inTransaction = */ true),
+ continuationParamName = continuationParamName,
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = adapter.returnType.asTypeName().box()
+ returnTypeName = adapter.returnType.asTypeName().box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
adapter.generateMethodBody(
- scope = functionScope,
+ scope = scope,
parameters = parameters,
adapters = adapters,
connectionVar = connectionVar
)
- }.build()
- this.addCode(functionCode)
- },
- continuationParamName
+ }
+ }
)
- }
-
- private fun convertAndReturnKotlin(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
- if (adapter == null) {
- return
- }
- val connectionVar = scope.getTmpVar("_connection")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- connectionVar
- ).apply {
- adapter.generateMethodBody(
- scope = scope,
- parameters = parameters,
- adapters = adapters,
- connectionVar = connectionVar
- )
- }.endControlFlow()
- }
+ scope.builder.add("return %L", performBlock)
}
override fun convertAndReturnCompat(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineInsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineInsertMethodBinder.kt
index f1c92ca..aa711af2 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineInsertMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineInsertMethodBinder.kt
@@ -16,12 +16,13 @@
package androidx.room.solver.shortcut.binder
-import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.box
import androidx.room.compiler.processing.XType
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.solver.CodeGenScope
@@ -43,80 +44,33 @@
dbProperty: XPropertySpec,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> convertAndReturnJava(
- parameters, adapters, dbProperty, scope
- )
- CodeLanguage.KOTLIN -> convertAndReturnKotlin(
- parameters, adapters, dbProperty, scope
- )
- }
- }
-
- private fun convertAndReturnJava(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
if (adapter == null) {
return
}
val connectionVar = scope.getTmpVar("_connection")
- scope.builder.addStatement(
- "return %M(%N, %L, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ false, /* inTransaction = */ true),
+ continuationParamName = continuationParamName,
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = adapter.returnType.asTypeName().box()
+ returnTypeName = adapter.returnType.asTypeName().box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
adapter.generateMethodBody(
- scope = functionScope,
- connectionVar = connectionVar,
+ scope = scope,
parameters = parameters,
- adapters = adapters
+ adapters = adapters,
+ connectionVar = connectionVar
)
- }.build()
- this.addCode(functionCode)
- },
- continuationParamName
+ }
+ }
)
- }
-
- private fun convertAndReturnKotlin(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
- if (adapter == null) {
- return
- }
- val connectionVar = scope.getTmpVar("_connection")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- connectionVar
- ).apply {
- adapter.generateMethodBody(
- scope = scope,
- connectionVar = connectionVar,
- parameters = parameters,
- adapters = adapters
- )
- }.endControlFlow()
- }
+ scope.builder.add("return %L", performBlock)
}
override fun convertAndReturnCompat(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineUpsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineUpsertMethodBinder.kt
index d820e4d..e572b89e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineUpsertMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CoroutineUpsertMethodBinder.kt
@@ -16,12 +16,13 @@
package androidx.room.solver.shortcut.binder
-import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.box
import androidx.room.compiler.processing.XType
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.solver.CodeGenScope
@@ -43,81 +44,33 @@
dbProperty: XPropertySpec,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> convertAndReturnJava(
- parameters, adapters, dbProperty, scope
- )
- CodeLanguage.KOTLIN -> convertAndReturnKotlin(
- parameters, adapters, dbProperty, scope
- )
- }
- }
-
- private fun convertAndReturnJava(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
if (adapter == null) {
return
}
val connectionVar = scope.getTmpVar("_connection")
- scope.builder.addStatement(
- "return %M(%N, %L, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ false, /* inTransaction = */ true),
+ continuationParamName = continuationParamName,
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = adapter.returnType.asTypeName().box()
+ returnTypeName = adapter.returnType.asTypeName().box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
adapter.generateMethodBody(
- scope = functionScope,
- connectionVar = connectionVar,
+ scope = scope,
parameters = parameters,
- adapters = adapters
+ adapters = adapters,
+ connectionVar = connectionVar
)
- }.build()
- this.addCode(functionCode)
- },
- continuationParamName
+ }
+ }
)
- }
-
- private fun convertAndReturnKotlin(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
- if (adapter == null) {
- return
- }
- val connectionVar = scope.getTmpVar("_connection")
-
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performSuspending"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- connectionVar
- ).apply {
- adapter.generateMethodBody(
- scope = scope,
- connectionVar = connectionVar,
- parameters = parameters,
- adapters = adapters
- )
- }.endControlFlow()
- }
+ scope.builder.add("return %L", performBlock)
}
override fun convertAndReturnCompat(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantDeleteOrUpdateMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantDeleteOrUpdateMethodBinder.kt
index 7ce2c3d..19b8ede 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantDeleteOrUpdateMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantDeleteOrUpdateMethodBinder.kt
@@ -17,11 +17,13 @@
package androidx.room.solver.shortcut.binder
import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeSpec
import androidx.room.compiler.codegen.box
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.ext.isNotVoid
@@ -42,81 +44,36 @@
dbProperty: XPropertySpec,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> convertAndReturnJava(
- parameters, adapters, dbProperty, scope
- )
- CodeLanguage.KOTLIN -> convertAndReturnKotlin(
- parameters, adapters, dbProperty, scope
- )
- }
- }
-
- private fun convertAndReturnJava(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
if (adapter == null) {
return
}
-
- val returnPrefix = if (adapter.returnType.isNotVoid()) { "return " } else { "" }
val connectionVar = scope.getTmpVar("_connection")
- scope.builder.addStatement(
- "$returnPrefix%M(%N, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ false, /* inTransaction = */ true),
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = adapter.returnType.asTypeName().box()
+ returnTypeName = adapter.returnType.asTypeName().box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
adapter.generateMethodBody(
- scope = functionScope,
+ scope = scope,
parameters = parameters,
adapters = adapters,
connectionVar = connectionVar
)
- }.build()
- this.addCode(functionCode)
+ }
}
)
- }
-
- private fun convertAndReturnKotlin(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
- if (adapter == null) {
- return
+ val returnPrefix = when (scope.language) {
+ CodeLanguage.JAVA -> if (adapter.returnType.isNotVoid()) { "return " } else { "" }
+ CodeLanguage.KOTLIN -> "return "
}
- val connectionVar = scope.getTmpVar("_connection")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- connectionVar
- ).apply {
- adapter.generateMethodBody(
- scope = scope,
- parameters = parameters,
- adapters = adapters,
- connectionVar = connectionVar
- )
- }.endControlFlow()
- }
+ scope.builder.add("$returnPrefix%L", performBlock)
}
override fun convertAndReturnCompat(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantInsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantInsertMethodBinder.kt
index ca27b5f..e1a4c9a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantInsertMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantInsertMethodBinder.kt
@@ -17,10 +17,12 @@
package androidx.room.solver.shortcut.binder
import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.box
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.ext.isNotVoid
@@ -40,81 +42,36 @@
dbProperty: XPropertySpec,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> convertAndReturnJava(
- parameters, adapters, dbProperty, scope
- )
- CodeLanguage.KOTLIN -> convertAndReturnKotlin(
- parameters, adapters, dbProperty, scope
- )
- }
- }
-
- private fun convertAndReturnJava(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
if (adapter == null) {
return
}
- val returnPrefix = if (adapter.returnType.isNotVoid()) { "return " } else { "" }
-
val connectionVar = scope.getTmpVar("_connection")
- scope.builder.addStatement(
- "$returnPrefix%M(%N, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ false, /* inTransaction = */ true),
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = adapter.returnType.asTypeName().box()
+ returnTypeName = adapter.returnType.asTypeName().box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
adapter.generateMethodBody(
- scope = functionScope,
- connectionVar = connectionVar,
+ scope = scope,
parameters = parameters,
- adapters = adapters
+ adapters = adapters,
+ connectionVar = connectionVar
)
- }.build()
- this.addCode(functionCode)
+ }
}
)
- }
-
- private fun convertAndReturnKotlin(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
- if (adapter == null) {
- return
+ val returnPrefix = when (scope.language) {
+ CodeLanguage.JAVA -> if (adapter.returnType.isNotVoid()) { "return " } else { "" }
+ CodeLanguage.KOTLIN -> "return "
}
- val connectionVar = scope.getTmpVar("_connection")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- connectionVar
- ).apply {
- adapter.generateMethodBody(
- scope = scope,
- connectionVar = connectionVar,
- parameters = parameters,
- adapters = adapters
- )
- }.endControlFlow()
- }
+ scope.builder.add("$returnPrefix%L", performBlock)
}
override fun convertAndReturnCompat(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantUpsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantUpsertMethodBinder.kt
index 244e6c7..4d8be411 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantUpsertMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantUpsertMethodBinder.kt
@@ -17,10 +17,12 @@
package androidx.room.solver.shortcut.binder
import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.box
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.ext.isNotVoid
@@ -40,80 +42,36 @@
dbProperty: XPropertySpec,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> convertAndReturnJava(
- parameters, adapters, dbProperty, scope
- )
- CodeLanguage.KOTLIN -> convertAndReturnKotlin(
- parameters, adapters, dbProperty, scope
- )
- }
- }
-
- private fun convertAndReturnJava(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
if (adapter == null) {
return
}
val connectionVar = scope.getTmpVar("_connection")
- val returnPrefix = if (adapter.returnType.isNotVoid()) { "return " } else { "" }
- scope.builder.addStatement(
- "$returnPrefix%M(%N, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ false, /* inTransaction = */ true),
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
parameterName = connectionVar,
- returnTypeName = adapter.returnType.asTypeName().box()
+ returnTypeName = adapter.returnType.asTypeName().box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- val functionScope = scope.fork()
- val functionCode = functionScope.builder.apply {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
adapter.generateMethodBody(
- scope = functionScope,
- connectionVar = connectionVar,
+ scope = scope,
parameters = parameters,
- adapters = adapters
+ adapters = adapters,
+ connectionVar = connectionVar
)
- }.build()
- this.addCode(functionCode)
+ }
}
)
- }
-
- private fun convertAndReturnKotlin(
- parameters: List<ShortcutQueryParameter>,
- adapters: Map<String, Pair<XPropertySpec, Any>>,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
- if (adapter == null) {
- return
+ val returnPrefix = when (scope.language) {
+ CodeLanguage.JAVA -> if (adapter.returnType.isNotVoid()) { "return " } else { "" }
+ CodeLanguage.KOTLIN -> "return "
}
- val connectionVar = scope.getTmpVar("_connection")
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) { %L ->",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- connectionVar
- ).apply {
- adapter.generateMethodBody(
- scope = scope,
- connectionVar = connectionVar,
- parameters = parameters,
- adapters = adapters
- )
- }.endControlFlow()
- }
+ scope.builder.add("$returnPrefix%L", performBlock)
}
override fun convertAndReturnCompat(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/transaction/binder/CoroutineTransactionMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/transaction/binder/CoroutineTransactionMethodBinder.kt
index bcaf3e3..62ce592 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/transaction/binder/CoroutineTransactionMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/transaction/binder/CoroutineTransactionMethodBinder.kt
@@ -19,13 +19,13 @@
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XCodeBlock
-import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.addStatement
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.processing.XType
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
import androidx.room.ext.KotlinTypeNames
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.solver.CodeGenScope
import androidx.room.solver.transaction.result.TransactionMethodAdapter
@@ -36,8 +36,7 @@
class CoroutineTransactionMethodBinder(
private val returnType: XType,
adapter: TransactionMethodAdapter,
- private val continuationParamName: String,
- private val javaLambdaSyntaxAvailable: Boolean
+ private val continuationParamName: String
) : TransactionMethodBinder(adapter) {
override fun executeAndReturn(
parameterNames: List<String>,
@@ -46,81 +45,40 @@
dbProperty: XPropertySpec,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> executeAndReturnJava(
- parameterNames, daoName, daoImplName, dbProperty, scope
- )
- CodeLanguage.KOTLIN -> executeAndReturnKotlin(
- parameterNames, daoName, daoImplName, dbProperty, scope
- )
- }
- }
-
- private fun executeAndReturnJava(
- parameterNames: List<String>,
- daoName: XClassName,
- daoImplName: XClassName,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
val innerContinuationParamName = scope.getTmpVar("_cont")
- val adapterScope = scope.fork()
- adapter.createDelegateToSuperCode(
- parameterNames = parameterNames + innerContinuationParamName,
- daoName = daoName,
- daoImplName = daoImplName,
- scope = adapterScope
- )
- val functionImpl: Any = if (javaLambdaSyntaxAvailable) {
- XCodeBlock.of(
- scope.language,
- "(%L) -> %L",
- innerContinuationParamName, adapterScope.generate()
- )
- } else {
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performInTransactionSuspending"),
+ argFormat = listOf("%N"),
+ args = listOf(dbProperty),
+ continuationParamName = continuationParamName,
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = KotlinTypeNames.CONTINUATION.parametrizedBy(
XTypeName.getConsumerSuperName(returnType.asTypeName())
),
parameterName = innerContinuationParamName,
- returnTypeName = KotlinTypeNames.ANY
+ returnTypeName = KotlinTypeNames.ANY,
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- addStatement("return %L", adapterScope.generate())
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+ val adapterScope = scope.fork()
+ adapter.createDelegateToSuperCode(
+ parameterNames = when (scope.language) {
+ CodeLanguage.JAVA -> parameterNames + innerContinuationParamName
+ CodeLanguage.KOTLIN -> parameterNames
+ },
+ daoName = daoName,
+ daoImplName = daoImplName,
+ scope = adapterScope
+ )
+ val returnPrefix = when (scope.language) {
+ CodeLanguage.JAVA -> "return "
+ CodeLanguage.KOTLIN -> ""
+ }
+ addStatement("$returnPrefix%L", adapterScope.generate())
+ }
}
- }
-
- scope.builder.addStatement(
- "return %M(%N, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performInTransactionSuspending"),
- dbProperty,
- functionImpl,
- continuationParamName
)
- }
-
- private fun executeAndReturnKotlin(
- parameterNames: List<String>,
- daoName: XClassName,
- daoImplName: XClassName,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N) {",
- RoomTypeNames.DB_UTIL.packageMember("performInTransactionSuspending"),
- dbProperty,
- )
- val adapterScope = scope.fork()
- adapter.createDelegateToSuperCode(
- parameterNames = parameterNames,
- daoName = daoName,
- daoImplName = daoImplName,
- scope = adapterScope
- )
- addStatement("%L", adapterScope.generate())
- endControlFlow()
- }
+ scope.builder.add("return %L", performBlock)
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/transaction/binder/InstantTransactionMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/transaction/binder/InstantTransactionMethodBinder.kt
index ad1e9b3..c458972 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/transaction/binder/InstantTransactionMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/transaction/binder/InstantTransactionMethodBinder.kt
@@ -19,15 +19,15 @@
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XCodeBlock
-import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.addStatement
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XPropertySpec
import androidx.room.compiler.codegen.box
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.isKotlinUnit
import androidx.room.compiler.processing.isVoid
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
import androidx.room.ext.KotlinTypeNames
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.RoomTypeNames
import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.solver.CodeGenScope
@@ -39,7 +39,6 @@
class InstantTransactionMethodBinder(
private val returnType: XType,
adapter: TransactionMethodAdapter,
- private val javaLambdaSyntaxAvailable: Boolean
) : TransactionMethodBinder(adapter) {
override fun executeAndReturn(
parameterNames: List<String>,
@@ -48,88 +47,47 @@
dbProperty: XPropertySpec,
scope: CodeGenScope
) {
- when (scope.language) {
- CodeLanguage.JAVA -> executeAndReturnJava(
- parameterNames, daoName, daoImplName, dbProperty, scope
- )
- CodeLanguage.KOTLIN -> executeAndReturnKotlin(
- parameterNames, daoName, daoImplName, dbProperty, scope
- )
+ val returnPrefix = when (scope.language) {
+ CodeLanguage.JAVA ->
+ if (returnType.isVoid() || returnType.isKotlinUnit()) "" else "return "
+ CodeLanguage.KOTLIN -> "return "
}
- }
-
- private fun executeAndReturnJava(
- parameterNames: List<String>,
- daoName: XClassName,
- daoImplName: XClassName,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
- val adapterScope = scope.fork()
- val returnPrefix = if (returnType.isVoid() || returnType.isKotlinUnit()) "" else "return "
- adapter.createDelegateToSuperCode(
- parameterNames = parameterNames,
- daoName = daoName,
- daoImplName = daoImplName,
- scope = adapterScope
- )
- val connectionVar = scope.getTmpVar("_connection")
- val functionImpl: Any = if (javaLambdaSyntaxAvailable) {
- XCodeBlock.builder(scope.language).apply {
- add("(%L) -> {\n", connectionVar)
- add("%>$returnPrefix%L;\n", adapterScope.generate())
- if (returnPrefix.isEmpty()) {
- add("return %T.INSTANCE;\n", KotlinTypeNames.UNIT)
- }
- add("%<}")
- }.build()
- } else {
- Function1TypeSpec(
- language = scope.language,
+ val performBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
+ argFormat = listOf("%N", "%L", "%L"),
+ args = listOf(dbProperty, /* isReadOnly = */ false, /* inTransaction = */ true),
+ lambdaSpec = object : LambdaSpec(
parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
- parameterName = connectionVar,
- returnTypeName = returnType.asTypeName().box()
+ parameterName = when (scope.language) {
+ CodeLanguage.JAVA -> scope.getTmpVar("_connection")
+ CodeLanguage.KOTLIN -> "_"
+ },
+ returnTypeName = returnType.asTypeName().box(),
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
) {
- this.addStatement("$returnPrefix%L", adapterScope.generate())
- if (returnPrefix.isEmpty()) {
- addStatement("return %T.INSTANCE", KotlinTypeNames.UNIT)
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+ val adapterScope = scope.fork()
+ adapter.createDelegateToSuperCode(
+ parameterNames = parameterNames,
+ daoName = daoName,
+ daoImplName = daoImplName,
+ scope = adapterScope
+ )
+ when (scope.language) {
+ CodeLanguage.JAVA -> {
+ addStatement("$returnPrefix%L", adapterScope.generate())
+ if (returnPrefix.isEmpty()) {
+ addStatement("return %T.INSTANCE", KotlinTypeNames.UNIT)
+ }
+ }
+ CodeLanguage.KOTLIN -> {
+ addStatement("%L", adapterScope.generate())
+ }
+ }
}
}
- }
- scope.builder.addStatement(
- "$returnPrefix%M(%N, %L, %L, %L)",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- functionImpl
)
- }
-
- private fun executeAndReturnKotlin(
- parameterNames: List<String>,
- daoName: XClassName,
- daoImplName: XClassName,
- dbProperty: XPropertySpec,
- scope: CodeGenScope
- ) {
- scope.builder.apply {
- beginControlFlow(
- "return %M(%N, %L, %L) {",
- RoomTypeNames.DB_UTIL.packageMember("performBlocking"),
- dbProperty,
- false, // isReadOnly
- true, // inTransaction
- )
- val adapterScope = scope.fork()
- adapter.createDelegateToSuperCode(
- parameterNames = parameterNames,
- daoName = daoName,
- daoImplName = daoImplName,
- scope = adapterScope
- )
- addStatement("%L", adapterScope.generate())
- endControlFlow()
- }
+ scope.builder.add("$returnPrefix%L", performBlock)
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
index eceddad..ba77eb5 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
@@ -22,8 +22,15 @@
import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.processing.XNullability
-import androidx.room.ext.CollectionTypeNames
+import androidx.room.ext.CollectionTypeNames.ARRAY_MAP
+import androidx.room.ext.CollectionTypeNames.LONG_SPARSE_ARRAY
import androidx.room.ext.CommonTypeNames
+import androidx.room.ext.CommonTypeNames.ARRAY_LIST
+import androidx.room.ext.CommonTypeNames.HASH_MAP
+import androidx.room.ext.CommonTypeNames.HASH_SET
+import androidx.room.ext.KotlinCollectionMemberNames
+import androidx.room.ext.KotlinCollectionMemberNames.MUTABLE_LIST_OF
+import androidx.room.ext.KotlinCollectionMemberNames.MUTABLE_SET_OF
import androidx.room.ext.capitalize
import androidx.room.ext.stripNonJava
import androidx.room.parser.ParsedQuery
@@ -43,6 +50,7 @@
import androidx.room.verifier.DatabaseVerificationErrors
import androidx.room.writer.QueryWriter
import androidx.room.writer.RelationCollectorFunctionWriter
+import androidx.room.writer.RelationCollectorFunctionWriter.Companion.PARAM_CONNECTION_VARIABLE
import java.util.Locale
/**
@@ -69,23 +77,37 @@
// parsed relating entity query
val loadAllQuery: ParsedQuery,
// true if `relationTypeName` is a Collection, when it is `relationTypeName` is always non null.
- val relationTypeIsCollection: Boolean,
- val javaLambdaSyntaxAvailable: Boolean
+ val relationTypeIsCollection: Boolean
) {
+ // TODO(b/319660042): Remove once migration to driver API is done.
+ fun isMigratedToDriver(): Boolean = rowAdapter.isMigratedToDriver()
+
// variable name of map containing keys to relation collections, set when writing the code
// generator in writeInitCode
- lateinit var varName: String
+ private lateinit var varName: String
fun writeInitCode(scope: CodeGenScope) {
varName = scope.getTmpVar(
"_collection${relation.field.getPath().stripNonJava().capitalize(Locale.US)}"
)
scope.builder.apply {
- addLocalVariable(
- name = varName,
- typeName = mapTypeName,
- assignExpr = XCodeBlock.ofNewInstance(language, mapTypeName)
- )
+ if (language == CodeLanguage.JAVA ||
+ mapTypeName.rawTypeName == ARRAY_MAP ||
+ mapTypeName.rawTypeName == LONG_SPARSE_ARRAY
+ ) {
+ addLocalVariable(
+ name = varName,
+ typeName = mapTypeName,
+ assignExpr = XCodeBlock.ofNewInstance(language, mapTypeName)
+ )
+ } else {
+ addLocalVal(
+ name = varName,
+ typeName = mapTypeName,
+ "%M()",
+ KotlinCollectionMemberNames.MUTABLE_MAP_OF
+ )
+ }
}
}
@@ -106,9 +128,25 @@
// for relation collection put an empty collections in the map, otherwise put nulls
if (relationTypeIsCollection) {
beginControlFlow("if (!%L.containsKey(%L))", varName, tmpVar).apply {
+ val newEmptyCollection = when (language) {
+ CodeLanguage.JAVA ->
+ XCodeBlock.ofNewInstance(language, relationTypeName)
+ CodeLanguage.KOTLIN ->
+ XCodeBlock.of(
+ language = language,
+ "%M()",
+ if (relationTypeName == CommonTypeNames.MUTABLE_SET) {
+ MUTABLE_SET_OF
+ } else {
+ MUTABLE_LIST_OF
+ }
+ )
+ }
addStatement(
"%L.put(%L, %L)",
- varName, tmpVar, XCodeBlock.ofNewInstance(language, relationTypeName)
+ varName,
+ tmpVar,
+ newEmptyCollection
)
}
endControlFlow()
@@ -152,7 +190,7 @@
// values for all keys, so this is safe. Special case for LongSParseArray
// since it does not have a getValue() from Kotlin.
val usingLongSparseArray =
- mapTypeName.rawTypeName == CollectionTypeNames.LONG_SPARSE_ARRAY
+ mapTypeName.rawTypeName == LONG_SPARSE_ARRAY
when (language) {
CodeLanguage.JAVA -> addStatement(
"%L = %L.get(%L)",
@@ -187,9 +225,23 @@
},
onKeyUnavailable = {
if (relationTypeIsCollection) {
+ val newEmptyCollection = when (language) {
+ CodeLanguage.JAVA ->
+ XCodeBlock.ofNewInstance(language, relationTypeName)
+ CodeLanguage.KOTLIN ->
+ XCodeBlock.of(
+ language = language,
+ "%M()",
+ if (relationTypeName == CommonTypeNames.MUTABLE_SET) {
+ MUTABLE_SET_OF
+ } else {
+ MUTABLE_LIST_OF
+ }
+ )
+ }
addStatement(
"%L = %L",
- tmpRelationVar, XCodeBlock.ofNewInstance(language, relationTypeName)
+ tmpRelationVar, newEmptyCollection
)
} else {
addStatement("%L = null", tmpRelationVar)
@@ -203,8 +255,14 @@
// called to write the invocation to the fetch relationship method
fun writeFetchRelationCall(scope: CodeGenScope) {
val method = scope.writer
- .getOrCreateFunction(RelationCollectorFunctionWriter(this))
- scope.builder.addStatement("%L(%L)", method.name, varName)
+ .getOrCreateFunction(RelationCollectorFunctionWriter(this, scope.useDriverApi))
+ scope.builder.apply {
+ if (scope.useDriverApi) {
+ addStatement("%L(%L, %L)", method.name, PARAM_CONNECTION_VARIABLE, varName)
+ } else {
+ addStatement("%L(%L)", method.name, varName)
+ }
+ }
}
// called to read key and call `onKeyReady` to write code once it is successfully read
@@ -339,10 +397,10 @@
val resultInfo = parsedQuery.resultInfo
val usingLongSparseArray =
- tmpMapTypeName.rawTypeName == CollectionTypeNames.LONG_SPARSE_ARRAY
+ tmpMapTypeName.rawTypeName == LONG_SPARSE_ARRAY
val queryParam = if (usingLongSparseArray) {
val longSparseArrayElement = context.processingEnv
- .requireTypeElement(CollectionTypeNames.LONG_SPARSE_ARRAY.canonicalName)
+ .requireTypeElement(LONG_SPARSE_ARRAY.canonicalName)
QueryParameter(
name = RelationCollectorFunctionWriter.PARAM_MAP_VARIABLE,
sqlName = RelationCollectorFunctionWriter.PARAM_MAP_VARIABLE,
@@ -433,8 +491,7 @@
entityKeyColumnReader = entityKeyColumnReader,
rowAdapter = rowAdapter,
loadAllQuery = parsedQuery,
- relationTypeIsCollection = isRelationCollection,
- javaLambdaSyntaxAvailable = context.processingEnv.jvmVersion >= 8
+ relationTypeIsCollection = isRelationCollection
)
}
}.filterNotNull()
@@ -505,13 +562,20 @@
if (fieldType.typeArguments.isNotEmpty()) {
val rawType = fieldType.rawType
val paramTypeName =
- if (context.COMMON_TYPES.LIST.rawType.isAssignableFrom(rawType)) {
- CommonTypeNames.ARRAY_LIST.parametrizedBy(relation.pojoTypeName)
- } else if (context.COMMON_TYPES.SET.rawType.isAssignableFrom(rawType)) {
- CommonTypeNames.HASH_SET.parametrizedBy(relation.pojoTypeName)
+ if (context.COMMON_TYPES.SET.rawType.isAssignableFrom(rawType)) {
+ when (context.codeLanguage) {
+ CodeLanguage.KOTLIN ->
+ CommonTypeNames.MUTABLE_SET.parametrizedBy(relation.pojoTypeName)
+ CodeLanguage.JAVA ->
+ HASH_SET.parametrizedBy(relation.pojoTypeName)
+ }
} else {
- // Default to ArrayList and see how things go...
- CommonTypeNames.ARRAY_LIST.parametrizedBy(relation.pojoTypeName)
+ when (context.codeLanguage) {
+ CodeLanguage.KOTLIN ->
+ CommonTypeNames.MUTABLE_LIST.parametrizedBy(relation.pojoTypeName)
+ CodeLanguage.JAVA ->
+ ARRAY_LIST.parametrizedBy(relation.pojoTypeName)
+ }
}
paramTypeName to true
} else {
@@ -526,24 +590,30 @@
keyTypeName: XTypeName,
valueTypeName: XTypeName,
): XTypeName {
- val canUseLongSparseArray = context.processingEnv
- .findTypeElement(CollectionTypeNames.LONG_SPARSE_ARRAY.canonicalName) != null
- val canUseArrayMap = context.processingEnv
- .findTypeElement(CollectionTypeNames.ARRAY_MAP.canonicalName) != null
+ val canUseLongSparseArray =
+ context.processingEnv
+ .findTypeElement(LONG_SPARSE_ARRAY.canonicalName) != null
+ val canUseArrayMap =
+ context.processingEnv
+ .findTypeElement(ARRAY_MAP.canonicalName) != null
return when {
canUseLongSparseArray && affinity == SQLTypeAffinity.INTEGER ->
- CollectionTypeNames.LONG_SPARSE_ARRAY.parametrizedBy(valueTypeName)
+ LONG_SPARSE_ARRAY.parametrizedBy(valueTypeName)
canUseArrayMap ->
- CollectionTypeNames.ARRAY_MAP.parametrizedBy(keyTypeName, valueTypeName)
- else ->
- CommonTypeNames.HASH_MAP.parametrizedBy(keyTypeName, valueTypeName)
+ ARRAY_MAP.parametrizedBy(keyTypeName, valueTypeName)
+ else -> when (context.codeLanguage) {
+ CodeLanguage.JAVA ->
+ HASH_MAP.parametrizedBy(keyTypeName, valueTypeName)
+ CodeLanguage.KOTLIN ->
+ CommonTypeNames.MUTABLE_MAP.parametrizedBy(keyTypeName, valueTypeName)
+ }
}
}
// Gets the type name of the relationship key.
private fun keyTypeFor(context: Context, affinity: SQLTypeAffinity): XTypeName {
val canUseLongSparseArray = context.processingEnv
- .findTypeElement(CollectionTypeNames.LONG_SPARSE_ARRAY.canonicalName) != null
+ .findTypeElement(LONG_SPARSE_ARRAY.canonicalName) != null
return when (affinity) {
SQLTypeAffinity.INTEGER ->
if (canUseLongSparseArray) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
index ab93879..1ddb717 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
@@ -38,10 +38,11 @@
* Writes the implementation of migrations that were annotated with @AutoMigration.
*/
class AutoMigrationWriter(
+ private val autoMigration: AutoMigration,
private val dbElement: XTypeElement,
- val autoMigration: AutoMigration,
- codeLanguage: CodeLanguage
-) : TypeWriter(codeLanguage) {
+ codeLanguage: CodeLanguage,
+ javaLambdaSyntaxAvailable: Boolean
+) : TypeWriter(codeLanguage, javaLambdaSyntaxAvailable) {
private val addedColumns = autoMigration.schemaDiff.addedColumns
private val addedTables = autoMigration.schemaDiff.addedTables
private val renamedTables = autoMigration.schemaDiff.renamedTables
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
index 7a610ad..352ab40 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
@@ -65,8 +65,9 @@
class DaoWriter(
val dao: Dao,
private val dbElement: XElement,
- codeLanguage: CodeLanguage
-) : TypeWriter(codeLanguage) {
+ codeLanguage: CodeLanguage,
+ javaLambdaSyntaxAvailable: Boolean
+) : TypeWriter(codeLanguage, javaLambdaSyntaxAvailable) {
private val declaredDao = dao.element.type
// TODO nothing prevents this from conflicting, we should fix.
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
index 82bd6f6..44206f5 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
@@ -44,8 +44,9 @@
*/
class DatabaseWriter(
val database: Database,
- codeLanguage: CodeLanguage
-) : TypeWriter(codeLanguage) {
+ codeLanguage: CodeLanguage,
+ javaLambdaSyntaxAvailable: Boolean
+) : TypeWriter(codeLanguage, javaLambdaSyntaxAvailable) {
override fun createTypeSpecBuilder(): XTypeSpec.Builder {
return XTypeSpec.classBuilder(codeLanguage, database.implTypeName).apply {
addOriginatingElement(database.element)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorFunctionWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorFunctionWriter.kt
index 1ecfd81..eb0d29c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorFunctionWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorFunctionWriter.kt
@@ -20,18 +20,20 @@
import androidx.room.compiler.codegen.XCodeBlock
import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
import androidx.room.compiler.codegen.XFunSpec
-import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.addStatement
import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
import androidx.room.compiler.codegen.XTypeName
import androidx.room.ext.AndroidTypeNames
import androidx.room.ext.CollectionTypeNames
import androidx.room.ext.CollectionsSizeExprCode
import androidx.room.ext.CommonTypeNames
-import androidx.room.ext.Function1TypeSpec
+import androidx.room.ext.InvokeWithLambdaParameter
import androidx.room.ext.KotlinTypeNames
+import androidx.room.ext.LambdaSpec
import androidx.room.ext.MapKeySetExprCode
import androidx.room.ext.RoomMemberNames
import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.RELATION_UTIL
+import androidx.room.ext.SQLiteDriverTypeNames
import androidx.room.ext.stripNonJava
import androidx.room.solver.CodeGenScope
import androidx.room.solver.query.result.PojoRowAdapter
@@ -41,13 +43,20 @@
* Writes the function that fetches the relations of a POJO and assigns them into the given map.
*/
class RelationCollectorFunctionWriter(
- private val collector: RelationCollector
+ private val collector: RelationCollector,
+ private val useDriverApi: Boolean
) : TypeWriter.SharedFunctionSpec(
- "fetchRelationship${collector.relation.entity.tableName.stripNonJava()}" +
- "As${collector.relation.pojoTypeName.toString(CodeLanguage.JAVA).stripNonJava()}"
+ baseName = if (useDriverApi) {
+ "fetchRelationship${collector.relation.entity.tableName.stripNonJava()}" +
+ "As${collector.relation.pojoTypeName.toString(CodeLanguage.JAVA).stripNonJava()}"
+ } else {
+ "fetchCompatRelationship${collector.relation.entity.tableName.stripNonJava()}" +
+ "As${collector.relation.pojoTypeName.toString(CodeLanguage.JAVA).stripNonJava()}"
+ },
) {
companion object {
const val PARAM_MAP_VARIABLE = "_map"
+ const val PARAM_CONNECTION_VARIABLE = "_connection"
const val KEY_SET_VARIABLE = "__mapKeySet"
}
@@ -63,11 +72,12 @@
"-${relation.entity.typeName.toString(CodeLanguage.JAVA)}" +
"-${relation.entityField.columnName}" +
"-${relation.pojoTypeName}" +
- "-${relation.createLoadAllSql()}"
+ "-${relation.createLoadAllSql()}" +
+ "-$useDriverApi"
}
override fun prepare(methodName: String, writer: TypeWriter, builder: XFunSpec.Builder) {
- val scope = CodeGenScope(writer)
+ val scope = CodeGenScope(writer = writer, useDriverApi = useDriverApi)
scope.builder.apply {
// Check the input map key set for emptiness, returning early as no fetching is needed.
addIsInputEmptyCheck()
@@ -75,25 +85,59 @@
// Check if the input map key set exceeds MAX_BIND_PARAMETER_CNT, if so do a recursive
// fetch.
beginControlFlow(
- "if (%L > %T.MAX_BIND_PARAMETER_CNT)",
+ "if (%L > %L)",
if (usingLongSparseArray) {
XCodeBlock.of(language, "%L.size()", PARAM_MAP_VARIABLE)
} else {
CollectionsSizeExprCode(language, PARAM_MAP_VARIABLE)
},
- RoomTypeNames.ROOM_DB
+ if (useDriverApi) {
+ "999"
+ } else {
+ XCodeBlock.of(
+ language,
+ "%T.MAX_BIND_PARAMETER_CNT",
+ RoomTypeNames.ROOM_DB
+ )
+ }
).apply {
- addRecursiveFetchCall(methodName)
+ addRecursiveFetchCall(scope, methodName)
addStatement("return")
}.endControlFlow()
- // Create SQL query, acquire statement and bind parameters.
- val stmtVar = scope.getTmpVar("_stmt")
- val sqlQueryVar = scope.getTmpVar("_sql")
- collector.queryWriter.prepareReadAndBind(sqlQueryVar, stmtVar, scope)
+ createStmtAndReturn(scope)
+ }
+ builder.apply {
+ if (useDriverApi) {
+ addParameter(SQLiteDriverTypeNames.CONNECTION, PARAM_CONNECTION_VARIABLE)
+ }
+ addParameter(collector.mapTypeName, PARAM_MAP_VARIABLE)
+ addCode(scope.generate())
+ }
+ }
+ private fun XCodeBlock.Builder.createStmtAndReturn(
+ scope: CodeGenScope
+ ) {
+ // Create SQL query, acquire statement and bind parameters.
+ val stmtVar = scope.getTmpVar("_stmt")
+ val cursorVar = "_cursor"
+ val sqlQueryVar = scope.getTmpVar("_sql")
+
+ if (useDriverApi) {
+ val connectionVar = scope.getTmpVar(PARAM_CONNECTION_VARIABLE)
+ val listSizeVars = collector.queryWriter.prepareQuery(sqlQueryVar, scope)
+ addLocalVal(
+ stmtVar,
+ SQLiteDriverTypeNames.STATEMENT,
+ "%L.prepare(%L)",
+ connectionVar,
+ sqlQueryVar
+ )
+ collector.queryWriter.bindArgs(stmtVar, listSizeVars, scope)
+ } else {
+ collector.queryWriter.prepareReadAndBind(sqlQueryVar, stmtVar, scope)
// Perform query and get a Cursor
- val cursorVar = "_cursor"
val shouldCopyCursor = collector.rowAdapter.let {
it is PojoRowAdapter && it.relationCollectors.isNotEmpty()
}
@@ -110,86 +154,91 @@
"null"
)
)
+ }
+ addRelationCollectorCode(scope, if (useDriverApi) stmtVar else cursorVar)
+ }
- val relation = collector.relation
- beginControlFlow("try").apply {
- // Gets index of the column to be used as key
- val itemKeyIndexVar = "_itemKeyIndex"
- if (relation.junction != null) {
- // When using a junction table the relationship map is keyed on the parent
- // reference column of the junction table, the same column used in the WHERE IN
- // clause, this column is the rightmost column in the generated SELECT
- // clause.
- val junctionParentColumnIndex = relation.projection.size
- addStatement("// _junction.%L", relation.junction.parentField.columnName)
- addLocalVal(
- itemKeyIndexVar,
- XTypeName.PRIMITIVE_INT,
- "%L",
- junctionParentColumnIndex
- )
- } else {
- addLocalVal(
- itemKeyIndexVar,
- XTypeName.PRIMITIVE_INT,
- "%M(%L, %S)",
- RoomMemberNames.CURSOR_UTIL_GET_COLUMN_INDEX,
- cursorVar,
- relation.entityField.columnName
- )
- }
- // Check if index of column is not -1, indicating the column for the key is not in
- // the result, can happen if the user specified a bad projection in @Relation.
- beginControlFlow("if (%L == -1)", itemKeyIndexVar).apply {
- addStatement("return")
- }
- endControlFlow()
+ private fun XCodeBlock.Builder.addRelationCollectorCode(
+ scope: CodeGenScope,
+ cursorVar: String
+ ) {
+ val relation = collector.relation
+ beginControlFlow("try").apply {
+ // Gets index of the column to be used as key
+ val itemKeyIndexVar = "_itemKeyIndex"
+ if (relation.junction != null) {
+ // When using a junction table the relationship map is keyed on the parent
+ // reference column of the junction table, the same column used in the WHERE IN
+ // clause, this column is the rightmost column in the generated SELECT
+ // clause.
+ val junctionParentColumnIndex = relation.projection.size
+ addStatement("// _junction.%L", relation.junction.parentField.columnName)
+ addLocalVal(
+ itemKeyIndexVar,
+ XTypeName.PRIMITIVE_INT,
+ "%L",
+ junctionParentColumnIndex
+ )
+ } else {
+ addLocalVal(
+ name = itemKeyIndexVar,
+ typeName = XTypeName.PRIMITIVE_INT,
+ assignExprFormat = "%M(%L, %S)",
+ if (useDriverApi) {
+ RoomTypeNames.STATEMENT_UTIL.packageMember("getColumnIndex")
+ } else { RoomMemberNames.CURSOR_UTIL_GET_COLUMN_INDEX },
+ cursorVar,
+ relation.entityField.columnName
+ )
+ }
- // Prepare item column indices
- collector.rowAdapter.onCursorReady(cursorVarName = cursorVar, scope = scope)
+ // Check if index of column is not -1, indicating the column for the key is not in
+ // the result, can happen if the user specified a bad projection in @Relation.
+ beginControlFlow("if (%L == -1)", itemKeyIndexVar).apply {
+ addStatement("return")
+ }
+ endControlFlow()
- val tmpVarName = scope.getTmpVar("_item")
- beginControlFlow("while (%L.moveToNext())", cursorVar).apply {
- // Read key from the cursor, convert row to item and place it on map
- collector.readKey(
- cursorVarName = cursorVar,
- indexVar = itemKeyIndexVar,
- keyReader = collector.entityKeyColumnReader,
- scope = scope
- ) { keyVar ->
- if (collector.relationTypeIsCollection) {
- val relationVar = scope.getTmpVar("_tmpRelation")
- addLocalVal(
- relationVar,
- collector.relationTypeName.copy(nullable = true),
- "%L.get(%L)",
- PARAM_MAP_VARIABLE, keyVar
- )
- beginControlFlow("if (%L != null)", relationVar)
- addLocalVariable(tmpVarName, relation.pojoTypeName)
- collector.rowAdapter.convert(tmpVarName, cursorVar, scope)
- addStatement("%L.add(%L)", relationVar, tmpVarName)
- endControlFlow()
- } else {
- beginControlFlow("if (%N.containsKey(%L))", PARAM_MAP_VARIABLE, keyVar)
- addLocalVariable(tmpVarName, relation.pojoTypeName)
- collector.rowAdapter.convert(tmpVarName, cursorVar, scope)
- addStatement("%N.put(%L, %L)", PARAM_MAP_VARIABLE, keyVar, tmpVarName)
- endControlFlow()
- }
+ // Prepare item column indices
+ collector.rowAdapter.onCursorReady(cursorVarName = cursorVar, scope = scope)
+ val tmpVarName = scope.getTmpVar("_item")
+ val stepName = if (scope.useDriverApi) "step" else "moveToNext"
+ beginControlFlow("while (%L.$stepName())", cursorVar).apply {
+ // Read key from the cursor, convert row to item and place it on map
+ collector.readKey(
+ cursorVarName = cursorVar,
+ indexVar = itemKeyIndexVar,
+ keyReader = collector.entityKeyColumnReader,
+ scope = scope
+ ) { keyVar ->
+ if (collector.relationTypeIsCollection) {
+ val relationVar = scope.getTmpVar("_tmpRelation")
+ addLocalVal(
+ relationVar,
+ collector.relationTypeName.copy(nullable = true),
+ "%L.get(%L)",
+ PARAM_MAP_VARIABLE, keyVar
+ )
+ beginControlFlow("if (%L != null)", relationVar)
+ addLocalVariable(tmpVarName, relation.pojoTypeName)
+ collector.rowAdapter.convert(tmpVarName, cursorVar, scope)
+ addStatement("%L.add(%L)", relationVar, tmpVarName)
+ endControlFlow()
+ } else {
+ beginControlFlow("if (%N.containsKey(%L))", PARAM_MAP_VARIABLE, keyVar)
+ addLocalVariable(tmpVarName, relation.pojoTypeName)
+ collector.rowAdapter.convert(tmpVarName, cursorVar, scope)
+ addStatement("%N.put(%L, %L)", PARAM_MAP_VARIABLE, keyVar, tmpVarName)
+ endControlFlow()
}
}
- endControlFlow()
- }
- nextControlFlow("finally").apply {
- addStatement("%L.close()", cursorVar)
}
endControlFlow()
}
- builder.apply {
- addParameter(collector.mapTypeName, PARAM_MAP_VARIABLE)
- addCode(scope.generate())
+ nextControlFlow("finally").apply {
+ addStatement("%L.close()", cursorVar)
}
+ endControlFlow()
}
private fun XCodeBlock.Builder.addIsInputEmptyCheck() {
@@ -209,62 +258,56 @@
endControlFlow()
}
- private fun XCodeBlock.Builder.addRecursiveFetchCall(methodName: String) {
- fun getRecursiveCall(itVarName: String) =
- XCodeBlock.of(
- language,
- "%L(%L)",
- methodName, itVarName
- )
+ private fun XCodeBlock.Builder.addRecursiveFetchCall(
+ scope: CodeGenScope,
+ methodName: String,
+ ) {
val utilFunction =
- RoomTypeNames.RELATION_UTIL.let {
+ RELATION_UTIL.let {
when {
usingLongSparseArray ->
it.packageMember("recursiveFetchLongSparseArray")
usingArrayMap ->
it.packageMember("recursiveFetchArrayMap")
- else ->
- it.packageMember("recursiveFetchHashMap")
+ else -> when (language) {
+ CodeLanguage.JAVA -> it.packageMember("recursiveFetchHashMap")
+ CodeLanguage.KOTLIN -> it.packageMember("recursiveFetchMap")
+ }
}
}
- when (language) {
- CodeLanguage.JAVA -> {
- val paramName = "map"
- if (collector.javaLambdaSyntaxAvailable) {
- add("%M(%L, %L, (%L) -> {\n",
- utilFunction, PARAM_MAP_VARIABLE, collector.relationTypeIsCollection,
- paramName
- )
- indent()
- addStatement("%L", getRecursiveCall(paramName))
- addStatement("return %T.INSTANCE", KotlinTypeNames.UNIT)
- unindent()
- addStatement("})")
- } else {
- val functionImpl = Function1TypeSpec(
- language = language,
- parameterTypeName = collector.mapTypeName,
- parameterName = paramName,
- returnTypeName = KotlinTypeNames.UNIT,
- ) {
- addStatement("%L", getRecursiveCall(paramName))
+ val paramName = scope.getTmpVar("_tmpMap")
+ val recursiveFetchBlock = InvokeWithLambdaParameter(
+ scope = scope,
+ functionName = utilFunction,
+ argFormat = listOf("%L", "%L"),
+ args = listOf(PARAM_MAP_VARIABLE, collector.relationTypeIsCollection),
+ lambdaSpec = object : LambdaSpec(
+ parameterTypeName = collector.mapTypeName,
+ parameterName = paramName,
+ returnTypeName = KotlinTypeNames.UNIT,
+ javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
+ ) {
+ override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+ val recursiveCall = if (useDriverApi) {
+ XCodeBlock.of(
+ language,
+ "%L(%L, %L)",
+ methodName, PARAM_CONNECTION_VARIABLE, paramName
+ )
+ } else {
+ XCodeBlock.of(
+ language,
+ "%L(%L)",
+ methodName, paramName
+ )
+ }
+ addStatement("%L", recursiveCall)
+ if (language == CodeLanguage.JAVA) {
addStatement("return %T.INSTANCE", KotlinTypeNames.UNIT)
}
- addStatement(
- "%M(%L, %L, %L)",
- utilFunction, PARAM_MAP_VARIABLE, collector.relationTypeIsCollection,
- functionImpl
- )
}
}
- CodeLanguage.KOTLIN -> {
- beginControlFlow(
- "%M(%L, %L)",
- utilFunction, PARAM_MAP_VARIABLE, collector.relationTypeIsCollection
- )
- addStatement("%L", getRecursiveCall("it"))
- endControlFlow()
- }
- }
+ )
+ add("%L", recursiveFetchBlock)
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/TypeWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/TypeWriter.kt
index 96c64dc..1700a64 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/TypeWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/TypeWriter.kt
@@ -36,7 +36,10 @@
/**
* Base class for all writers that can produce a class.
*/
-abstract class TypeWriter(val codeLanguage: CodeLanguage) {
+abstract class TypeWriter(
+ val codeLanguage: CodeLanguage,
+ val javaLambdaSyntaxAvailable: Boolean,
+) {
private val sharedFieldSpecs = mutableMapOf<String, XPropertySpec>()
private val sharedMethodSpecs = mutableMapOf<String, XFunSpec>()
private val sharedFieldNames = mutableSetOf<String>()
diff --git a/room/room-compiler/src/main/resources/NOTICE.txt b/room/room-compiler/src/main/resources/META-INF/NOTICE.txt
similarity index 100%
rename from room/room-compiler/src/main/resources/NOTICE.txt
rename to room/room-compiler/src/main/resources/META-INF/NOTICE.txt
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/BaseDaoTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/BaseDaoTest.kt
index 2f432e3..a680f2d 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/BaseDaoTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/BaseDaoTest.kt
@@ -219,7 +219,7 @@
val processed = DaoProcessor(
invocation.context, dao, dbType, null
).process()
- DaoWriter(processed, dbElm, CodeLanguage.JAVA)
+ DaoWriter(processed, dbElm, CodeLanguage.JAVA, false)
.write(invocation.processingEnv)
}
}
@@ -270,7 +270,8 @@
invocation.context, daoElm, dbType, null
).process()
handler(processedDao)
- DaoWriter(processedDao, dbElm, CodeLanguage.JAVA).write(invocation.processingEnv)
+ DaoWriter(processedDao, dbElm, CodeLanguage.JAVA, false)
+ .write(invocation.processingEnv)
}
}
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
index bbf992b..028b786 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
@@ -335,7 +335,8 @@
"""
) { parsedQuery, invocation ->
val expected = MUTABLE_LIST.parametrizedBy(
- XTypeName.getTypeVariableName("T", listOf(XTypeName.ANY_OBJECT))
+ XTypeName.getTypeVariableName("T", listOf(XTypeName.ANY_OBJECT.copy(
+ nullable = true)))
)
assertThat(parsedQuery.returnType.asTypeName(), `is`(expected))
invocation.assertCompilationResult {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
index 997f6d3..d8a127c 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
@@ -402,7 +402,8 @@
dao = daoProcessor.process(),
dbElement = invocation.processingEnv
.requireTypeElement("androidx.room.RoomDatabase"),
- codeLanguage = CodeLanguage.JAVA
+ codeLanguage = CodeLanguage.JAVA,
+ javaLambdaSyntaxAvailable = false
).write(invocation.processingEnv)
invocation.assertCompilationResult {
generatedSourceFileWithPath("MyDao_Impl.java").let {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt b/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
index 5c4d981..2a63f03 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
@@ -332,7 +332,7 @@
fun testCodeGenScope(): CodeGenScope {
return CodeGenScope(
- object : TypeWriter(CodeLanguage.JAVA) {
+ object : TypeWriter(CodeLanguage.JAVA, true) {
override fun createTypeSpecBuilder(): XTypeSpec.Builder {
return XTypeSpec.classBuilder(codeLanguage, XClassName.get("test", "Foo"))
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/AutoMigrationWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/AutoMigrationWriterTest.kt
index e541095..298b09e 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/AutoMigrationWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/AutoMigrationWriterTest.kt
@@ -111,9 +111,11 @@
isSpecProvided = false
)
AutoMigrationWriter(
- invocation.processingEnv.requireTypeElement("foo.bar.MyDatabase"),
- autoMigrationResultWithNewAddedColumn,
- codeLanguage
+ autoMigration = autoMigrationResultWithNewAddedColumn,
+ dbElement =
+ invocation.processingEnv.requireTypeElement("foo.bar.MyDatabase"),
+ codeLanguage = codeLanguage,
+ javaLambdaSyntaxAvailable = false
).write(invocation.processingEnv)
val expectedFile = when (codeLanguage) {
@@ -185,9 +187,11 @@
isSpecProvided = false
)
AutoMigrationWriter(
- invocation.processingEnv.requireTypeElement("foo.bar.MyDatabase"),
- autoMigrationResultWithNewAddedColumn,
- codeLanguage
+ autoMigration = autoMigrationResultWithNewAddedColumn,
+ dbElement =
+ invocation.processingEnv.requireTypeElement("foo.bar.MyDatabase"),
+ codeLanguage = codeLanguage,
+ javaLambdaSyntaxAvailable = false
).write(invocation.processingEnv)
val expectedFile = when (codeLanguage) {
@@ -267,9 +271,11 @@
isSpecProvided = true
)
AutoMigrationWriter(
- invocation.processingEnv.requireTypeElement("foo.bar.MyDatabase"),
- autoMigrationResultWithNewAddedColumn,
- codeLanguage
+ autoMigration = autoMigrationResultWithNewAddedColumn,
+ dbElement =
+ invocation.processingEnv.requireTypeElement("foo.bar.MyDatabase"),
+ codeLanguage = codeLanguage,
+ javaLambdaSyntaxAvailable = false
).write(invocation.processingEnv)
val expectedFile = when (codeLanguage) {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoWriterTest.kt
index de81792..7643c91 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoWriterTest.kt
@@ -25,17 +25,19 @@
import androidx.room.ext.RoomTypeNames.ROOM_DB
import androidx.room.processor.DaoProcessor
import androidx.room.testing.context
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
import createVerifierFromEntitiesAndViews
import java.util.Locale
import loadTestSource
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
+import writeTestSource
-@RunWith(JUnit4::class)
+@RunWith(TestParameterInjector::class)
class DaoWriterTest {
@Test
- fun complexDao() {
+ fun complexDao(@TestParameter javaLambdaSyntaxAvailable: Boolean) {
singleDao(
loadTestSource(
fileName = "databasewriter/input/ComplexDatabase.java",
@@ -44,18 +46,10 @@
loadTestSource(
fileName = "daoWriter/input/ComplexDao.java",
qName = "foo.bar.ComplexDao"
- )
- ) {
- val backendFolder = backendFolder(it)
- it.assertCompilationResult {
- generatedSource(
- loadTestSource(
- fileName = "daoWriter/output/$backendFolder/ComplexDao.java",
- qName = "foo.bar.ComplexDao_Impl"
- )
- )
- }
- }
+ ),
+ javaLambdaSyntaxAvailable = javaLambdaSyntaxAvailable,
+ outputFileName = "ComplexDao.java"
+ )
}
@Test
@@ -63,95 +57,65 @@
val originalLocale = Locale.getDefault()
try {
Locale.setDefault(Locale("tr")) // Turkish has special upper/lowercase i chars
- complexDao()
+ complexDao(false)
} finally {
Locale.setDefault(originalLocale)
}
}
@Test
- fun writerDao() {
+ fun writerDao(@TestParameter javaLambdaSyntaxAvailable: Boolean) {
singleDao(
loadTestSource(
fileName = "daoWriter/input/WriterDao.java",
qName = "foo.bar.WriterDao"
- )
- ) {
- val backendFolder = backendFolder(it)
- it.assertCompilationResult {
- generatedSource(
- loadTestSource(
- fileName = "daoWriter/output/$backendFolder/WriterDao.java",
- qName = "foo.bar.WriterDao_Impl"
- )
- )
- }
- }
+ ),
+ javaLambdaSyntaxAvailable = javaLambdaSyntaxAvailable,
+ outputFileName = "WriterDao.java"
+ )
}
@Test
- fun deletionDao() {
+ fun deletionDao(@TestParameter javaLambdaSyntaxAvailable: Boolean) {
singleDao(
loadTestSource(
fileName = "daoWriter/input/DeletionDao.java",
qName = "foo.bar.DeletionDao"
- )
- ) {
- val backendFolder = backendFolder(it)
- it.assertCompilationResult {
- generatedSource(
- loadTestSource(
- fileName = "daoWriter/output/$backendFolder/DeletionDao.java",
- qName = "foo.bar.DeletionDao_Impl"
- )
- )
- }
- }
+ ),
+ javaLambdaSyntaxAvailable = javaLambdaSyntaxAvailable,
+ outputFileName = "DeletionDao.java"
+ )
}
@Test
- fun updateDao() {
+ fun updateDao(@TestParameter javaLambdaSyntaxAvailable: Boolean) {
singleDao(
loadTestSource(
fileName = "daoWriter/input/UpdateDao.java",
qName = "foo.bar.UpdateDao"
- )
- ) {
- val backendFolder = backendFolder(it)
- it.assertCompilationResult {
- generatedSource(
- loadTestSource(
- fileName = "daoWriter/output/$backendFolder/UpdateDao.java",
- qName = "foo.bar.UpdateDao_Impl"
- )
- )
- }
- }
+ ),
+ javaLambdaSyntaxAvailable = javaLambdaSyntaxAvailable,
+ outputFileName = "UpdateDao.java"
+ )
}
@Test
- fun upsertDao() {
+ fun upsertDao(@TestParameter javaLambdaSyntaxAvailable: Boolean) {
singleDao(
loadTestSource(
fileName = "daoWriter/input/UpsertDao.java",
qName = "foo.bar.UpsertDao"
- )
- ) {
- val backendFolder = backendFolder(it)
- it.assertCompilationResult {
- generatedSource(
- loadTestSource(
- fileName = "daoWriter/output/$backendFolder/UpsertDao.java",
- qName = "foo.bar.UpsertDao_Impl"
- )
- )
- }
- }
+ ),
+ javaLambdaSyntaxAvailable = javaLambdaSyntaxAvailable,
+ outputFileName = "UpsertDao.java"
+ )
}
private fun singleDao(
vararg inputs: Source,
- handler: (XTestInvocation) -> Unit
+ javaLambdaSyntaxAvailable: Boolean = false,
+ outputFileName: String,
+ handler: (XTestInvocation) -> Unit = { }
) {
val sources = listOf(
COMMON.USER, COMMON.MULTI_PKEY_ENTITY, COMMON.BOOK,
@@ -165,6 +129,10 @@
runProcessorTest(
sources = sources
) { invocation ->
+ if (invocation.isKsp && !javaLambdaSyntaxAvailable) {
+ // Skip KSP backend without lambda syntax, it is a nonsensical combination.
+ return@runProcessorTest
+ }
val dao = invocation.roundEnv
.getElementsAnnotatedWith(
androidx.room.Dao::class.qualifiedName!!
@@ -186,15 +154,42 @@
dbVerifier = dbVerifier
)
val parsedDao = parser.process()
- DaoWriter(parsedDao, db, CodeLanguage.JAVA)
+ DaoWriter(parsedDao, db, CodeLanguage.JAVA, javaLambdaSyntaxAvailable)
.write(invocation.processingEnv)
+ val outputSubFolder = outputFolder(invocation, javaLambdaSyntaxAvailable)
+ invocation.assertCompilationResult {
+ val expectedFilePath = "daoWriter/output/$outputSubFolder/$outputFileName"
+ val expectedSrc = loadTestSource(
+ fileName = expectedFilePath,
+ qName = parsedDao.implTypeName.canonicalName
+ )
+ // Set ROOM_TEST_WRITE_SRCS env variable to make tests write expected sources,
+ // handy for big sweeping code gen changes. ;)
+ if (System.getenv("ROOM_TEST_WRITE_SRCS") != null) {
+ writeTestSource(
+ checkNotNull(this.findGeneratedSource(expectedSrc.relativePath)) {
+ "Couldn't find gen src: $expectedSrc"
+ },
+ expectedFilePath
+ )
+ }
+ generatedSource(expectedSrc)
+ }
}
- // we could call handler inside the if block but if something happens and we cannot
- // find the dao, test will never assert on generated code.
handler(invocation)
}
}
- private fun backendFolder(invocation: XTestInvocation) =
- invocation.processingEnv.backend.name.lowercase()
+ private fun outputFolder(
+ invocation: XTestInvocation,
+ javaLambdaSyntaxAvailable: Boolean
+ ): String {
+ val backendFolder = invocation.processingEnv.backend.name.lowercase()
+ val lambdaFolder = if (javaLambdaSyntaxAvailable) "withLambda" else "withoutLambda"
+ if (invocation.isKsp) {
+ return backendFolder
+ } else {
+ return "$backendFolder/$lambdaFolder"
+ }
+ }
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
index 1ca847b..5600862 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
@@ -195,7 +195,7 @@
dbVerifier = createVerifierFromEntitiesAndViews(invocation)
)
val parsedDao = parser.process()
- DaoWriter(parsedDao, db, CodeLanguage.JAVA)
+ DaoWriter(parsedDao, db, CodeLanguage.JAVA, true)
.write(invocation.processingEnv)
invocation.assertCompilationResult {
val relativePath =
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt
index b0e8158..76d5b76 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt
@@ -122,7 +122,7 @@
) {
singleEntity(input) { entity, invocation ->
val className = XClassName.get("foo.bar", "MyContainerClass")
- val writer = object : TypeWriter(CodeLanguage.JAVA) {
+ val writer = object : TypeWriter(CodeLanguage.JAVA, true) {
override fun createTypeSpecBuilder(): XTypeSpec.Builder {
getOrCreateFunction(EntityCursorConverterWriter(entity))
return XTypeSpec.classBuilder(codeLanguage, className)
diff --git a/room/room-compiler/src/test/test-data/daoWriter/input/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/input/UpdateDao.java
index 226dcccc..317c7cc 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/input/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/input/UpdateDao.java
@@ -69,10 +69,4 @@
@Query("UPDATE User SET ageColumn = ageColumn + 1")
Maybe<Integer> ageUserAllMaybe();
-
- @Transaction
- default void updateAndAge(User user) {
- updateUser(user);
- ageUserByUid(String.valueOf(user.uid));
- }
}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
deleted file mode 100644
index b29e9ef..0000000
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
+++ /dev/null
@@ -1,759 +0,0 @@
-package foo.bar;
-
-import android.database.Cursor;
-import android.os.CancellationSignal;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.LiveData;
-import androidx.room.RoomDatabase;
-import androidx.room.RoomSQLiteQuery;
-import androidx.room.guava.GuavaRoom;
-import androidx.room.util.CursorUtil;
-import androidx.room.util.DBUtil;
-import androidx.room.util.SQLiteStatementUtil;
-import androidx.room.util.StringUtil;
-import androidx.sqlite.SQLiteConnection;
-import androidx.sqlite.SQLiteStatement;
-import androidx.sqlite.db.SupportSQLiteQuery;
-import com.google.common.util.concurrent.ListenableFuture;
-import java.lang.Class;
-import java.lang.Exception;
-import java.lang.Integer;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.StringBuilder;
-import java.lang.SuppressWarnings;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import javax.annotation.processing.Generated;
-import kotlin.jvm.functions.Function1;
-
-@Generated("androidx.room.RoomProcessor")
-@SuppressWarnings({"unchecked", "deprecation", "removal"})
-public final class ComplexDao_Impl extends ComplexDao {
- private final RoomDatabase __db;
-
- public ComplexDao_Impl(final ComplexDatabase __db) {
- super(__db);
- this.__db = __db;
- }
-
- @Override
- public boolean transactionMethod(final int i, final String s, final long l) {
- return DBUtil.performBlocking(__db, false, true, (_connection) -> {
- return ComplexDao_Impl.super.transactionMethod(i, s, l);
- });
- }
-
- @Override
- public List<ComplexDao.FullName> fullNames(final int id) {
- final String _sql = "SELECT name || lastName as fullName, uid as id FROM user where uid = ?";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<ComplexDao.FullName>>() {
- @Override
- @NonNull
- public List<ComplexDao.FullName> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, id);
- final int _cursorIndexOfFullName = 0;
- final int _cursorIndexOfId = 1;
- final List<ComplexDao.FullName> _result = new ArrayList<ComplexDao.FullName>();
- while (_stmt.step()) {
- final ComplexDao.FullName _item;
- _item = new ComplexDao.FullName();
- if (_stmt.isNull(_cursorIndexOfFullName)) {
- _item.fullName = null;
- } else {
- _item.fullName = _stmt.getText(_cursorIndexOfFullName);
- }
- _item.id = (int) (_stmt.getLong(_cursorIndexOfId));
- _result.add(_item);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public User getById(final int id) {
- final String _sql = "SELECT * FROM user where uid = ?";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, User>() {
- @Override
- @NonNull
- public User invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, id);
- final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
- final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
- final User _result;
- if (_stmt.step()) {
- _result = new User();
- _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
- if (_stmt.isNull(_cursorIndexOfName)) {
- _result.name = null;
- } else {
- _result.name = _stmt.getText(_cursorIndexOfName);
- }
- final String _tmpLastName;
- if (_stmt.isNull(_cursorIndexOfLastName)) {
- _tmpLastName = null;
- } else {
- _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
- }
- _result.setLastName(_tmpLastName);
- _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public User findByName(final String name, final String lastName) {
- final String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, User>() {
- @Override
- @NonNull
- public User invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (name == null) {
- _stmt.bindNull(_argIndex);
- } else {
- _stmt.bindText(_argIndex, name);
- }
- _argIndex = 2;
- if (lastName == null) {
- _stmt.bindNull(_argIndex);
- } else {
- _stmt.bindText(_argIndex, lastName);
- }
- final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
- final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
- final User _result;
- if (_stmt.step()) {
- _result = new User();
- _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
- if (_stmt.isNull(_cursorIndexOfName)) {
- _result.name = null;
- } else {
- _result.name = _stmt.getText(_cursorIndexOfName);
- }
- final String _tmpLastName;
- if (_stmt.isNull(_cursorIndexOfLastName)) {
- _tmpLastName = null;
- } else {
- _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
- }
- _result.setLastName(_tmpLastName);
- _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public List<User> loadAllByIds(final int... ids) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT * FROM user where uid IN (");
- final int _inputSize = ids == null ? 1 : ids.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<User>>() {
- @Override
- @NonNull
- public List<User> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (ids == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item : ids) {
- _stmt.bindLong(_argIndex, _item);
- _argIndex++;
- }
- }
- final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
- final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
- final List<User> _result = new ArrayList<User>();
- while (_stmt.step()) {
- final User _item_1;
- _item_1 = new User();
- _item_1.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
- if (_stmt.isNull(_cursorIndexOfName)) {
- _item_1.name = null;
- } else {
- _item_1.name = _stmt.getText(_cursorIndexOfName);
- }
- final String _tmpLastName;
- if (_stmt.isNull(_cursorIndexOfLastName)) {
- _tmpLastName = null;
- } else {
- _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
- }
- _item_1.setLastName(_tmpLastName);
- _item_1.age = (int) (_stmt.getLong(_cursorIndexOfAge));
- _result.add(_item_1);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- int getAge(final int id) {
- final String _sql = "SELECT ageColumn FROM user where uid = ?";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, id);
- final int _result;
- if (_stmt.step()) {
- _result = (int) (_stmt.getLong(0));
- } else {
- _result = 0;
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public int[] getAllAges(final int... ids) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
- final int _inputSize = ids == null ? 1 : ids.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, int[]>() {
- @Override
- @NonNull
- public int[] invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (ids == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item : ids) {
- _stmt.bindLong(_argIndex, _item);
- _argIndex++;
- }
- }
- final List<Integer> _listResult = new ArrayList<Integer>();
- while (_stmt.step()) {
- final Integer _item_1;
- if (_stmt.isNull(0)) {
- _item_1 = null;
- } else {
- _item_1 = (int) (_stmt.getLong(0));
- }
- _listResult.add(_item_1);
- }
- final int[] _tmpArrayResult = new int[_listResult.size()];
- int _index = 0;
- for (int _listItem : _listResult) {
- _tmpArrayResult[_index] = _listItem;
- _index++;
- }
- final int[] _result = _tmpArrayResult;
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public List<Integer> getAllAgesAsList(final List<Integer> ids) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
- final int _inputSize = ids == null ? 1 : ids.size();
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Integer>>() {
- @Override
- @NonNull
- public List<Integer> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (ids == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (Integer _item : ids) {
- if (_item == null) {
- _stmt.bindNull(_argIndex);
- } else {
- _stmt.bindLong(_argIndex, _item);
- }
- _argIndex++;
- }
- }
- final List<Integer> _result = new ArrayList<Integer>();
- while (_stmt.step()) {
- final Integer _item_1;
- if (_stmt.isNull(0)) {
- _item_1 = null;
- } else {
- _item_1 = (int) (_stmt.getLong(0));
- }
- _result.add(_item_1);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public List<Integer> getAllAgesAsList(final List<Integer> ids1, final int[] ids2,
- final int... ids3) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
- final int _inputSize = ids1 == null ? 1 : ids1.size();
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(") OR uid IN (");
- final int _inputSize_1 = ids2 == null ? 1 : ids2.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize_1);
- _stringBuilder.append(") OR uid IN (");
- final int _inputSize_2 = ids3 == null ? 1 : ids3.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize_2);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Integer>>() {
- @Override
- @NonNull
- public List<Integer> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (ids1 == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (Integer _item : ids1) {
- if (_item == null) {
- _stmt.bindNull(_argIndex);
- } else {
- _stmt.bindLong(_argIndex, _item);
- }
- _argIndex++;
- }
- }
- _argIndex = 1 + _inputSize;
- if (ids2 == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item_1 : ids2) {
- _stmt.bindLong(_argIndex, _item_1);
- _argIndex++;
- }
- }
- _argIndex = 1 + _inputSize + _inputSize_1;
- if (ids3 == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item_2 : ids3) {
- _stmt.bindLong(_argIndex, _item_2);
- _argIndex++;
- }
- }
- final List<Integer> _result = new ArrayList<Integer>();
- while (_stmt.step()) {
- final Integer _item_3;
- if (_stmt.isNull(0)) {
- _item_3 = null;
- } else {
- _item_3 = (int) (_stmt.getLong(0));
- }
- _result.add(_item_3);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public LiveData<User> getByIdLive(final int id) {
- final String _sql = "SELECT * FROM user where uid = ?";
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
- int _argIndex = 1;
- _statement.bindLong(_argIndex, id);
- return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<User>() {
- @Override
- @Nullable
- public User call() throws Exception {
- final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
- try {
- final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
- final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
- final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
- final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
- final User _result;
- if (_cursor.moveToFirst()) {
- _result = new User();
- _result.uid = _cursor.getInt(_cursorIndexOfUid);
- if (_cursor.isNull(_cursorIndexOfName)) {
- _result.name = null;
- } else {
- _result.name = _cursor.getString(_cursorIndexOfName);
- }
- final String _tmpLastName;
- if (_cursor.isNull(_cursorIndexOfLastName)) {
- _tmpLastName = null;
- } else {
- _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
- }
- _result.setLastName(_tmpLastName);
- _result.age = _cursor.getInt(_cursorIndexOfAge);
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _cursor.close();
- }
- }
-
- @Override
- protected void finalize() {
- _statement.release();
- }
- });
- }
-
- @Override
- public LiveData<List<User>> loadUsersByIdsLive(final int... ids) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT * FROM user where uid IN (");
- final int _inputSize = ids == null ? 1 : ids.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- final int _argCount = 0 + _inputSize;
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
- int _argIndex = 1;
- if (ids == null) {
- _statement.bindNull(_argIndex);
- } else {
- for (int _item : ids) {
- _statement.bindLong(_argIndex, _item);
- _argIndex++;
- }
- }
- return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<List<User>>() {
- @Override
- @Nullable
- public List<User> call() throws Exception {
- final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
- try {
- final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
- final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
- final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
- final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
- final List<User> _result = new ArrayList<User>();
- while (_cursor.moveToNext()) {
- final User _item_1;
- _item_1 = new User();
- _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
- if (_cursor.isNull(_cursorIndexOfName)) {
- _item_1.name = null;
- } else {
- _item_1.name = _cursor.getString(_cursorIndexOfName);
- }
- final String _tmpLastName;
- if (_cursor.isNull(_cursorIndexOfLastName)) {
- _tmpLastName = null;
- } else {
- _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
- }
- _item_1.setLastName(_tmpLastName);
- _item_1.age = _cursor.getInt(_cursorIndexOfAge);
- _result.add(_item_1);
- }
- return _result;
- } finally {
- _cursor.close();
- }
- }
-
- @Override
- protected void finalize() {
- _statement.release();
- }
- });
- }
-
- @Override
- public List<Child1> getChild1List() {
- final String _sql = "SELECT * FROM Child1";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Child1>>() {
- @Override
- @NonNull
- public List<Child1> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
- final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
- final List<Child1> _result = new ArrayList<Child1>();
- while (_stmt.step()) {
- final Child1 _item;
- final int _tmpId;
- _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
- final String _tmpName;
- if (_stmt.isNull(_cursorIndexOfName)) {
- _tmpName = null;
- } else {
- _tmpName = _stmt.getText(_cursorIndexOfName);
- }
- final Info _tmpInfo;
- if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
- _tmpInfo = new Info();
- _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
- if (_stmt.isNull(_cursorIndexOfCode)) {
- _tmpInfo.code = null;
- } else {
- _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
- }
- } else {
- _tmpInfo = null;
- }
- _item = new Child1(_tmpId,_tmpName,_tmpInfo);
- _result.add(_item);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public List<Child2> getChild2List() {
- final String _sql = "SELECT * FROM Child2";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Child2>>() {
- @Override
- @NonNull
- public List<Child2> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
- final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
- final List<Child2> _result = new ArrayList<Child2>();
- while (_stmt.step()) {
- final Child2 _item;
- final int _tmpId;
- _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
- final String _tmpName;
- if (_stmt.isNull(_cursorIndexOfName)) {
- _tmpName = null;
- } else {
- _tmpName = _stmt.getText(_cursorIndexOfName);
- }
- final Info _tmpInfo;
- if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
- _tmpInfo = new Info();
- _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
- if (_stmt.isNull(_cursorIndexOfCode)) {
- _tmpInfo.code = null;
- } else {
- _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
- }
- } else {
- _tmpInfo = null;
- }
- _item = new Child2(_tmpId,_tmpName,_tmpInfo);
- _result.add(_item);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public ListenableFuture<List<Child1>> getChild1ListListenableFuture() {
- final String _sql = "SELECT * FROM Child1";
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
- final CancellationSignal _cancellationSignal = new CancellationSignal();
- return GuavaRoom.createListenableFuture(__db, false, new Callable<List<Child1>>() {
- @Override
- public List<Child1> call() throws Exception {
- final Cursor _cursor = DBUtil.query(__db, _statement, false, _cancellationSignal);
- try {
- final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
- final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
- final int _cursorIndexOfSerial = CursorUtil.getColumnIndexOrThrow(_cursor, "serial");
- final int _cursorIndexOfCode = CursorUtil.getColumnIndexOrThrow(_cursor, "code");
- final List<Child1> _result = new ArrayList<Child1>();
- while (_cursor.moveToNext()) {
- final Child1 _item;
- final int _tmpId;
- _tmpId = _cursor.getInt(_cursorIndexOfId);
- final String _tmpName;
- if (_cursor.isNull(_cursorIndexOfName)) {
- _tmpName = null;
- } else {
- _tmpName = _cursor.getString(_cursorIndexOfName);
- }
- final Info _tmpInfo;
- if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
- _tmpInfo = new Info();
- _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
- if (_cursor.isNull(_cursorIndexOfCode)) {
- _tmpInfo.code = null;
- } else {
- _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
- }
- } else {
- _tmpInfo = null;
- }
- _item = new Child1(_tmpId,_tmpName,_tmpInfo);
- _result.add(_item);
- }
- return _result;
- } finally {
- _cursor.close();
- }
- }
- }, _statement, true, _cancellationSignal);
- }
-
- @Override
- public List<UserSummary> getUserNames() {
- final String _sql = "SELECT `uid`, `name` FROM (SELECT * FROM User)";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<UserSummary>>() {
- @Override
- @NonNull
- public List<UserSummary> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- final int _cursorIndexOfUid = 0;
- final int _cursorIndexOfName = 1;
- final List<UserSummary> _result = new ArrayList<UserSummary>();
- while (_stmt.step()) {
- final UserSummary _item;
- _item = new UserSummary();
- _item.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
- if (_stmt.isNull(_cursorIndexOfName)) {
- _item.name = null;
- } else {
- _item.name = _stmt.getText(_cursorIndexOfName);
- }
- _result.add(_item);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public User getUserViaRawQuery(final SupportSQLiteQuery rawQuery) {
- __db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, rawQuery, false, null);
- try {
- final User _result;
- if (_cursor.moveToFirst()) {
- _result = __entityCursorConverter_fooBarUser(_cursor);
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _cursor.close();
- }
- }
-
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
-
- private User __entityCursorConverter_fooBarUser(@NonNull final Cursor cursor) {
- final User _entity;
- final int _cursorIndexOfUid = CursorUtil.getColumnIndex(cursor, "uid");
- final int _cursorIndexOfName = CursorUtil.getColumnIndex(cursor, "name");
- final int _cursorIndexOfLastName = CursorUtil.getColumnIndex(cursor, "lastName");
- final int _cursorIndexOfAge = CursorUtil.getColumnIndex(cursor, "ageColumn");
- _entity = new User();
- if (_cursorIndexOfUid != -1) {
- _entity.uid = cursor.getInt(_cursorIndexOfUid);
- }
- if (_cursorIndexOfName != -1) {
- if (cursor.isNull(_cursorIndexOfName)) {
- _entity.name = null;
- } else {
- _entity.name = cursor.getString(_cursorIndexOfName);
- }
- }
- if (_cursorIndexOfLastName != -1) {
- final String _tmpLastName;
- if (cursor.isNull(_cursorIndexOfLastName)) {
- _tmpLastName = null;
- } else {
- _tmpLastName = cursor.getString(_cursorIndexOfLastName);
- }
- _entity.setLastName(_tmpLastName);
- }
- if (_cursorIndexOfAge != -1) {
- _entity.age = cursor.getInt(_cursorIndexOfAge);
- }
- return _entity;
- }
-}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
deleted file mode 100644
index c773530..0000000
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
+++ /dev/null
@@ -1,417 +0,0 @@
-package foo.bar;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.room.EntityDeleteOrUpdateAdapter;
-import androidx.room.EntityDeletionOrUpdateAdapter;
-import androidx.room.RoomDatabase;
-import androidx.room.util.DBUtil;
-import androidx.room.util.SQLiteConnectionUtil;
-import androidx.room.util.StringUtil;
-import androidx.sqlite.SQLiteConnection;
-import androidx.sqlite.SQLiteStatement;
-import androidx.sqlite.db.SupportSQLiteStatement;
-import io.reactivex.Completable;
-import io.reactivex.Maybe;
-import io.reactivex.Single;
-import java.lang.Class;
-import java.lang.Exception;
-import java.lang.Integer;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.StringBuilder;
-import java.lang.SuppressWarnings;
-import java.lang.Void;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import javax.annotation.processing.Generated;
-import kotlin.jvm.functions.Function1;
-
-@Generated("androidx.room.RoomProcessor")
-@SuppressWarnings({"unchecked", "deprecation", "removal"})
-public final class DeletionDao_Impl implements DeletionDao {
- private final RoomDatabase __db;
-
- private final EntityDeleteOrUpdateAdapter<User> __deleteAdapterOfUser;
-
- private final EntityDeletionOrUpdateAdapter<User> __deleteCompatAdapterOfUser;
-
- private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __deleteAdapterOfMultiPKeyEntity;
-
- private final EntityDeleteOrUpdateAdapter<Book> __deleteAdapterOfBook;
-
- public DeletionDao_Impl(@NonNull final RoomDatabase __db) {
- this.__db = __db;
- this.__deleteAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "DELETE FROM `User` WHERE `uid` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- }
- };
- this.__deleteCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
- @Override
- @NonNull
- protected String createQuery() {
- return "DELETE FROM `User` WHERE `uid` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- }
- };
- this.__deleteAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final MultiPKeyEntity entity) {
- if (entity.name == null) {
- statement.bindNull(1);
- } else {
- statement.bindText(1, entity.name);
- }
- if (entity.lastName == null) {
- statement.bindNull(2);
- } else {
- statement.bindText(2, entity.lastName);
- }
- }
- };
- this.__deleteAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "DELETE FROM `Book` WHERE `bookId` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
- statement.bindLong(1, entity.bookId);
- }
- };
- }
-
- @Override
- public void deleteUser(final User user) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __deleteAdapterOfUser.handle(_connection, user);
- return null;
- }
- });
- }
-
- @Override
- public void deleteUsers(final User user1, final List<User> others) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __deleteAdapterOfUser.handle(_connection, user1);
- __deleteAdapterOfUser.handleMultiple(_connection, others);
- return null;
- }
- });
- }
-
- @Override
- public void deleteArrayOfUsers(final User[] users) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __deleteAdapterOfUser.handleMultiple(_connection, users);
- return null;
- }
- });
- }
-
- @Override
- public Integer deleteUserAndReturnCountObject(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfUser.handle(_connection, user);
- return _result;
- }
- });
- }
-
- @Override
- public int deleteUserAndReturnCount(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfUser.handle(_connection, user);
- return _result;
- }
- });
- }
-
- @Override
- public int deleteUserAndReturnCount(final User user1, final List<User> others) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfUser.handle(_connection, user1);
- _result += __deleteAdapterOfUser.handleMultiple(_connection, others);
- return _result;
- }
- });
- }
-
- @Override
- public int deleteUserAndReturnCount(final User[] users) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfUser.handleMultiple(_connection, users);
- return _result;
- }
- });
- }
-
- @Override
- public Completable deleteUserCompletable(final User user) {
- return Completable.fromCallable(new Callable<Void>() {
- @Override
- @Nullable
- public Void call() throws Exception {
- __db.beginTransaction();
- try {
- __deleteCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return null;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public Single<Integer> deleteUserSingle(final User user) {
- return Single.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total += __deleteCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public Maybe<Integer> deleteUserMaybe(final User user) {
- return Maybe.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total += __deleteCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public int multiPKey(final MultiPKeyEntity entity) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfMultiPKeyEntity.handle(_connection, entity);
- return _result;
- }
- });
- }
-
- @Override
- public void deleteUserAndBook(final User user, final Book book) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __deleteAdapterOfUser.handle(_connection, user);
- __deleteAdapterOfBook.handle(_connection, book);
- return null;
- }
- });
- }
-
- @Override
- public int deleteByUid(final int uid) {
- final String _sql = "DELETE FROM user where uid = ?";
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, uid);
- _stmt.step();
- return SQLiteConnectionUtil.getTotalChangedRows(_connection);
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public Completable deleteByUidCompletable(final int uid) {
- return Completable.fromCallable(new Callable<Void>() {
- @Override
- @Nullable
- public Void call() throws Exception {
- final String _sql = "DELETE FROM user where uid = ?";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, uid);
- __db.beginTransaction();
- try {
- _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return null;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public Single<Integer> deleteByUidSingle(final int uid) {
- return Single.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- final String _sql = "DELETE FROM user where uid = ?";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, uid);
- __db.beginTransaction();
- try {
- final Integer _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public Maybe<Integer> deleteByUidMaybe(final int uid) {
- return Maybe.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- final String _sql = "DELETE FROM user where uid = ?";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, uid);
- __db.beginTransaction();
- try {
- final Integer _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public int deleteByUidList(final int... uid) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("DELETE FROM user where uid IN(");
- final int _inputSize = uid == null ? 1 : uid.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (uid == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item : uid) {
- _stmt.bindLong(_argIndex, _item);
- _argIndex++;
- }
- }
- _stmt.step();
- return SQLiteConnectionUtil.getTotalChangedRows(_connection);
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public int deleteEverything() {
- final String _sql = "DELETE FROM user";
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- _stmt.step();
- return SQLiteConnectionUtil.getTotalChangedRows(_connection);
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
-}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
deleted file mode 100644
index 39eb440..0000000
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
+++ /dev/null
@@ -1,464 +0,0 @@
-package foo.bar;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.room.EntityDeleteOrUpdateAdapter;
-import androidx.room.EntityDeletionOrUpdateAdapter;
-import androidx.room.RoomDatabase;
-import androidx.room.util.DBUtil;
-import androidx.sqlite.SQLiteConnection;
-import androidx.sqlite.SQLiteStatement;
-import androidx.sqlite.db.SupportSQLiteStatement;
-import io.reactivex.Completable;
-import io.reactivex.Maybe;
-import io.reactivex.Single;
-import java.lang.Class;
-import java.lang.Exception;
-import java.lang.Integer;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.SuppressWarnings;
-import java.lang.Void;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import javax.annotation.processing.Generated;
-import kotlin.Unit;
-import kotlin.jvm.functions.Function1;
-
-@Generated("androidx.room.RoomProcessor")
-@SuppressWarnings({"unchecked", "deprecation", "removal"})
-public final class UpdateDao_Impl implements UpdateDao {
- private final RoomDatabase __db;
-
- private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser;
-
- private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser_1;
-
- private final EntityDeletionOrUpdateAdapter<User> __updateCompatAdapterOfUser;
-
- private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __updateAdapterOfMultiPKeyEntity;
-
- private final EntityDeleteOrUpdateAdapter<Book> __updateAdapterOfBook;
-
- public UpdateDao_Impl(@NonNull final RoomDatabase __db) {
- this.__db = __db;
- this.__updateAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- if (entity.name == null) {
- statement.bindNull(2);
- } else {
- statement.bindText(2, entity.name);
- }
- if (entity.getLastName() == null) {
- statement.bindNull(3);
- } else {
- statement.bindText(3, entity.getLastName());
- }
- statement.bindLong(4, entity.age);
- statement.bindLong(5, entity.uid);
- }
- };
- this.__updateAdapterOfUser_1 = new EntityDeleteOrUpdateAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- if (entity.name == null) {
- statement.bindNull(2);
- } else {
- statement.bindText(2, entity.name);
- }
- if (entity.getLastName() == null) {
- statement.bindNull(3);
- } else {
- statement.bindText(3, entity.getLastName());
- }
- statement.bindLong(4, entity.age);
- statement.bindLong(5, entity.uid);
- }
- };
- this.__updateCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- if (entity.name == null) {
- statement.bindNull(2);
- } else {
- statement.bindString(2, entity.name);
- }
- if (entity.getLastName() == null) {
- statement.bindNull(3);
- } else {
- statement.bindString(3, entity.getLastName());
- }
- statement.bindLong(4, entity.age);
- statement.bindLong(5, entity.uid);
- }
- };
- this.__updateAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final MultiPKeyEntity entity) {
- if (entity.name == null) {
- statement.bindNull(1);
- } else {
- statement.bindText(1, entity.name);
- }
- if (entity.lastName == null) {
- statement.bindNull(2);
- } else {
- statement.bindText(2, entity.lastName);
- }
- if (entity.name == null) {
- statement.bindNull(3);
- } else {
- statement.bindText(3, entity.name);
- }
- if (entity.lastName == null) {
- statement.bindNull(4);
- } else {
- statement.bindText(4, entity.lastName);
- }
- }
- };
- this.__updateAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
- statement.bindLong(1, entity.bookId);
- statement.bindLong(2, entity.uid);
- statement.bindLong(3, entity.bookId);
- }
- };
- }
-
- @Override
- public void updateUser(final User user) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser.handle(_connection, user);
- return null;
- }
- });
- }
-
- @Override
- public void updateUsers(final User user1, final List<User> others) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser.handle(_connection, user1);
- __updateAdapterOfUser.handleMultiple(_connection, others);
- return null;
- }
- });
- }
-
- @Override
- public void updateArrayOfUsers(final User[] users) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser.handleMultiple(_connection, users);
- return null;
- }
- });
- }
-
- @Override
- public void updateTwoUsers(final User userOne, final User userTwo) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser_1.handle(_connection, userOne);
- __updateAdapterOfUser_1.handle(_connection, userTwo);
- return null;
- }
- });
- }
-
- @Override
- public int updateUserAndReturnCount(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfUser.handle(_connection, user);
- return _result;
- }
- });
- }
-
- @Override
- public int updateUserAndReturnCount(final User user1, final List<User> others) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfUser.handle(_connection, user1);
- _result += __updateAdapterOfUser.handleMultiple(_connection, others);
- return _result;
- }
- });
- }
-
- @Override
- public int updateUserAndReturnCount(final User[] users) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfUser.handleMultiple(_connection, users);
- return _result;
- }
- });
- }
-
- @Override
- public Integer updateUserAndReturnCountObject(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfUser.handle(_connection, user);
- return _result;
- }
- });
- }
-
- @Override
- public Completable updateUserAndReturnCountCompletable(final User user) {
- return Completable.fromCallable(new Callable<Void>() {
- @Override
- @Nullable
- public Void call() throws Exception {
- __db.beginTransaction();
- try {
- __updateCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return null;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public Single<Integer> updateUserAndReturnCountSingle(final User user) {
- return Single.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total += __updateCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public Maybe<Integer> updateUserAndReturnCountMaybe(final User user) {
- return Maybe.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total += __updateCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public int multiPKey(final MultiPKeyEntity entity) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfMultiPKeyEntity.handle(_connection, entity);
- return _result;
- }
- });
- }
-
- @Override
- public void updateUserAndBook(final User user, final Book book) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser.handle(_connection, user);
- __updateAdapterOfBook.handle(_connection, book);
- return null;
- }
- });
- }
-
- @Override
- public void updateAndAge(final User user) {
- DBUtil.performBlocking(__db, false, true, (_connection) -> {
- UpdateDao.super.updateAndAge(user);
- return Unit.INSTANCE;
- });
- }
-
- @Override
- public void ageUserByUid(final String uid) {
- final String _sql = "UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = ?";
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (uid == null) {
- _stmt.bindNull(_argIndex);
- } else {
- _stmt.bindText(_argIndex, uid);
- }
- _stmt.step();
- return null;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public void ageUserAll() {
- final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- _stmt.step();
- return null;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public Completable ageUserAllCompletable() {
- return Completable.fromCallable(new Callable<Void>() {
- @Override
- @Nullable
- public Void call() throws Exception {
- final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- __db.beginTransaction();
- try {
- _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return null;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public Single<Integer> ageUserAllSingle() {
- return Single.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- __db.beginTransaction();
- try {
- final Integer _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @Override
- public Maybe<Integer> ageUserAllMaybe() {
- return Maybe.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- __db.beginTransaction();
- try {
- final Integer _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
-}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java
deleted file mode 100644
index 89ad4f4..0000000
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package foo.bar;
-
-import androidx.annotation.NonNull;
-import androidx.room.EntityDeleteOrUpdateAdapter;
-import androidx.room.EntityInsertAdapter;
-import androidx.room.EntityUpsertAdapter;
-import androidx.room.RoomDatabase;
-import androidx.room.util.DBUtil;
-import androidx.sqlite.SQLiteConnection;
-import androidx.sqlite.SQLiteStatement;
-import java.lang.Class;
-import java.lang.Long;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.SuppressWarnings;
-import java.lang.Void;
-import java.util.Collections;
-import java.util.List;
-import javax.annotation.processing.Generated;
-import kotlin.jvm.functions.Function1;
-
-@Generated("androidx.room.RoomProcessor")
-@SuppressWarnings({"unchecked", "deprecation", "removal"})
-public final class UpsertDao_Impl implements UpsertDao {
- private final RoomDatabase __db;
-
- private final EntityUpsertAdapter<User> __upsertAdapterOfUser;
-
- private final EntityUpsertAdapter<Book> __upsertAdapterOfBook;
-
- public UpsertDao_Impl(@NonNull final RoomDatabase __db) {
- this.__db = __db;
- this.__upsertAdapterOfUser = new EntityUpsertAdapter<User>(new EntityInsertAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- if (entity.name == null) {
- statement.bindNull(2);
- } else {
- statement.bindText(2, entity.name);
- }
- if (entity.getLastName() == null) {
- statement.bindNull(3);
- } else {
- statement.bindText(3, entity.getLastName());
- }
- statement.bindLong(4, entity.age);
- }
- }, new EntityDeleteOrUpdateAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- if (entity.name == null) {
- statement.bindNull(2);
- } else {
- statement.bindText(2, entity.name);
- }
- if (entity.getLastName() == null) {
- statement.bindNull(3);
- } else {
- statement.bindText(3, entity.getLastName());
- }
- statement.bindLong(4, entity.age);
- statement.bindLong(5, entity.uid);
- }
- });
- this.__upsertAdapterOfBook = new EntityUpsertAdapter<Book>(new EntityInsertAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
- statement.bindLong(1, entity.bookId);
- statement.bindLong(2, entity.uid);
- }
- }, new EntityDeleteOrUpdateAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
- statement.bindLong(1, entity.bookId);
- statement.bindLong(2, entity.uid);
- statement.bindLong(3, entity.bookId);
- }
- });
- }
-
- @Override
- public void upsertUser(final User user) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, user);
- return null;
- }
- });
- }
-
- @Override
- public void upsertUsers(final User user1, final List<User> others) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, user1);
- __upsertAdapterOfUser.upsert(_connection, others);
- return null;
- }
- });
- }
-
- @Override
- public void upsertUsers(final User[] users) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, users);
- return null;
- }
- });
- }
-
- @Override
- public void upsertTwoUsers(final User userOne, final User userTwo) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, userOne);
- __upsertAdapterOfUser.upsert(_connection, userTwo);
- return null;
- }
- });
- }
-
- @Override
- public void upsertUserAndBook(final User user, final Book book) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, user);
- __upsertAdapterOfBook.upsert(_connection, book);
- return null;
- }
- });
- }
-
- @Override
- public long upsertAndReturnId(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Long>() {
- @Override
- @NonNull
- public Long invoke(@NonNull final SQLiteConnection _connection) {
- return __upsertAdapterOfUser.upsertAndReturnId(_connection, user);
- }
- });
- }
-
- @Override
- public long[] upsertAndReturnIdsArray(final User[] users) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, long[]>() {
- @Override
- @NonNull
- public long[] invoke(@NonNull final SQLiteConnection _connection) {
- return __upsertAdapterOfUser.upsertAndReturnIdsArray(_connection, users);
- }
- });
- }
-
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
-}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java
deleted file mode 100644
index 6bfe333..0000000
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package foo.bar;
-
-import androidx.annotation.NonNull;
-import androidx.room.EntityInsertAdapter;
-import androidx.room.RoomDatabase;
-import androidx.room.util.DBUtil;
-import androidx.sqlite.SQLiteConnection;
-import androidx.sqlite.SQLiteStatement;
-import java.lang.Class;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.SuppressWarnings;
-import java.lang.Void;
-import java.util.Collections;
-import java.util.List;
-import javax.annotation.processing.Generated;
-import kotlin.jvm.functions.Function1;
-
-@Generated("androidx.room.RoomProcessor")
-@SuppressWarnings({"unchecked", "deprecation", "removal"})
-public final class WriterDao_Impl implements WriterDao {
- private final RoomDatabase __db;
-
- private final EntityInsertAdapter<User> __insertAdapterOfUser;
-
- private final EntityInsertAdapter<User> __insertAdapterOfUser_1;
-
- private final EntityInsertAdapter<User> __insertAdapterOfUser_2;
-
- private final EntityInsertAdapter<Book> __insertAdapterOfBook;
-
- public WriterDao_Impl(@NonNull final RoomDatabase __db) {
- this.__db = __db;
- this.__insertAdapterOfUser = new EntityInsertAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT OR ABORT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- if (entity.name == null) {
- statement.bindNull(2);
- } else {
- statement.bindText(2, entity.name);
- }
- if (entity.getLastName() == null) {
- statement.bindNull(3);
- } else {
- statement.bindText(3, entity.getLastName());
- }
- statement.bindLong(4, entity.age);
- }
- };
- this.__insertAdapterOfUser_1 = new EntityInsertAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT OR REPLACE INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- if (entity.name == null) {
- statement.bindNull(2);
- } else {
- statement.bindText(2, entity.name);
- }
- if (entity.getLastName() == null) {
- statement.bindNull(3);
- } else {
- statement.bindText(3, entity.getLastName());
- }
- statement.bindLong(4, entity.age);
- }
- };
- this.__insertAdapterOfUser_2 = new EntityInsertAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
- statement.bindLong(1, entity.uid);
- if (entity.name == null) {
- statement.bindNull(2);
- } else {
- statement.bindText(2, entity.name);
- }
- if (entity.getLastName() == null) {
- statement.bindNull(3);
- } else {
- statement.bindText(3, entity.getLastName());
- }
- statement.bindLong(4, entity.age);
- }
- };
- this.__insertAdapterOfBook = new EntityInsertAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT OR ABORT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
- }
-
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
- statement.bindLong(1, entity.bookId);
- statement.bindLong(2, entity.uid);
- }
- };
- }
-
- @Override
- public void insertUser(final User user) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser.insert(_connection, user);
- return null;
- }
- });
- }
-
- @Override
- public void insertUsers(final User user1, final List<User> others) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser.insert(_connection, user1);
- __insertAdapterOfUser.insert(_connection, others);
- return null;
- }
- });
- }
-
- @Override
- public void insertUsers(final User[] users) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser_1.insert(_connection, users);
- return null;
- }
- });
- }
-
- @Override
- public void insertTwoUsers(final User userOne, final User userTwo) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser_2.insert(_connection, userOne);
- __insertAdapterOfUser_2.insert(_connection, userTwo);
- return null;
- }
- });
- }
-
- @Override
- public void insertUserAndBook(final User user, final Book book) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser.insert(_connection, user);
- __insertAdapterOfBook.insert(_connection, book);
- return null;
- }
- });
- }
-
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
-}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/ComplexDao.java
new file mode 100644
index 0000000..064c6a6
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/ComplexDao.java
@@ -0,0 +1,713 @@
+package foo.bar;
+
+import android.database.Cursor;
+import android.os.CancellationSignal;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+import androidx.room.RoomDatabase;
+import androidx.room.RoomSQLiteQuery;
+import androidx.room.guava.GuavaRoom;
+import androidx.room.util.CursorUtil;
+import androidx.room.util.DBUtil;
+import androidx.room.util.SQLiteStatementUtil;
+import androidx.room.util.StringUtil;
+import androidx.sqlite.SQLiteStatement;
+import androidx.sqlite.db.SupportSQLiteQuery;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.lang.Class;
+import java.lang.Exception;
+import java.lang.Integer;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.lang.SuppressWarnings;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class ComplexDao_Impl extends ComplexDao {
+ private final RoomDatabase __db;
+
+ public ComplexDao_Impl(final ComplexDatabase __db) {
+ super(__db);
+ this.__db = __db;
+ }
+
+ @Override
+ public boolean transactionMethod(final int i, final String s, final long l) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ return ComplexDao_Impl.super.transactionMethod(i, s, l);
+ });
+ }
+
+ @Override
+ public List<ComplexDao.FullName> fullNames(final int id) {
+ final String _sql = "SELECT name || lastName as fullName, uid as id FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, id);
+ final int _cursorIndexOfFullName = 0;
+ final int _cursorIndexOfId = 1;
+ final List<ComplexDao.FullName> _result = new ArrayList<ComplexDao.FullName>();
+ while (_stmt.step()) {
+ final ComplexDao.FullName _item;
+ _item = new ComplexDao.FullName();
+ if (_stmt.isNull(_cursorIndexOfFullName)) {
+ _item.fullName = null;
+ } else {
+ _item.fullName = _stmt.getText(_cursorIndexOfFullName);
+ }
+ _item.id = (int) (_stmt.getLong(_cursorIndexOfId));
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public User getById(final int id) {
+ final String _sql = "SELECT * FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, id);
+ final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
+ final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
+ final User _result;
+ if (_stmt.step()) {
+ _result = new User();
+ _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _result.name = null;
+ } else {
+ _result.name = _stmt.getText(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_stmt.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
+ }
+ _result.setLastName(_tmpLastName);
+ _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public User findByName(final String name, final String lastName) {
+ final String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (name == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindText(_argIndex, name);
+ }
+ _argIndex = 2;
+ if (lastName == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindText(_argIndex, lastName);
+ }
+ final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
+ final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
+ final User _result;
+ if (_stmt.step()) {
+ _result = new User();
+ _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _result.name = null;
+ } else {
+ _result.name = _stmt.getText(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_stmt.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
+ }
+ _result.setLastName(_tmpLastName);
+ _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public List<User> loadAllByIds(final int... ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT * FROM user where uid IN (");
+ final int _inputSize = ids == null ? 1 : ids.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
+ final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
+ final List<User> _result = new ArrayList<User>();
+ while (_stmt.step()) {
+ final User _item_1;
+ _item_1 = new User();
+ _item_1.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _item_1.name = null;
+ } else {
+ _item_1.name = _stmt.getText(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_stmt.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
+ }
+ _item_1.setLastName(_tmpLastName);
+ _item_1.age = (int) (_stmt.getLong(_cursorIndexOfAge));
+ _result.add(_item_1);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ int getAge(final int id) {
+ final String _sql = "SELECT ageColumn FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, id);
+ final int _result;
+ if (_stmt.step()) {
+ _result = (int) (_stmt.getLong(0));
+ } else {
+ _result = 0;
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public int[] getAllAges(final int... ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+ final int _inputSize = ids == null ? 1 : ids.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ final List<Integer> _listResult = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_1;
+ if (_stmt.isNull(0)) {
+ _item_1 = null;
+ } else {
+ _item_1 = (int) (_stmt.getLong(0));
+ }
+ _listResult.add(_item_1);
+ }
+ final int[] _tmpArrayResult = new int[_listResult.size()];
+ int _index = 0;
+ for (int _listItem : _listResult) {
+ _tmpArrayResult[_index] = _listItem;
+ _index++;
+ }
+ final int[] _result = _tmpArrayResult;
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public List<Integer> getAllAgesAsList(final List<Integer> ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+ final int _inputSize = ids == null ? 1 : ids.size();
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (Integer _item : ids) {
+ if (_item == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindLong(_argIndex, _item);
+ }
+ _argIndex++;
+ }
+ }
+ final List<Integer> _result = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_1;
+ if (_stmt.isNull(0)) {
+ _item_1 = null;
+ } else {
+ _item_1 = (int) (_stmt.getLong(0));
+ }
+ _result.add(_item_1);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public List<Integer> getAllAgesAsList(final List<Integer> ids1, final int[] ids2,
+ final int... ids3) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+ final int _inputSize = ids1 == null ? 1 : ids1.size();
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(") OR uid IN (");
+ final int _inputSize_1 = ids2 == null ? 1 : ids2.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize_1);
+ _stringBuilder.append(") OR uid IN (");
+ final int _inputSize_2 = ids3 == null ? 1 : ids3.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize_2);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids1 == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (Integer _item : ids1) {
+ if (_item == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindLong(_argIndex, _item);
+ }
+ _argIndex++;
+ }
+ }
+ _argIndex = 1 + _inputSize;
+ if (ids2 == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item_1 : ids2) {
+ _stmt.bindLong(_argIndex, _item_1);
+ _argIndex++;
+ }
+ }
+ _argIndex = 1 + _inputSize + _inputSize_1;
+ if (ids3 == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item_2 : ids3) {
+ _stmt.bindLong(_argIndex, _item_2);
+ _argIndex++;
+ }
+ }
+ final List<Integer> _result = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_3;
+ if (_stmt.isNull(0)) {
+ _item_3 = null;
+ } else {
+ _item_3 = (int) (_stmt.getLong(0));
+ }
+ _result.add(_item_3);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public LiveData<User> getByIdLive(final int id) {
+ final String _sql = "SELECT * FROM user where uid = ?";
+ final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
+ int _argIndex = 1;
+ _statement.bindLong(_argIndex, id);
+ return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<User>() {
+ @Override
+ @Nullable
+ public User call() throws Exception {
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
+ try {
+ final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
+ final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
+ final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
+ final User _result;
+ if (_cursor.moveToFirst()) {
+ _result = new User();
+ _result.uid = _cursor.getInt(_cursorIndexOfUid);
+ if (_cursor.isNull(_cursorIndexOfName)) {
+ _result.name = null;
+ } else {
+ _result.name = _cursor.getString(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_cursor.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+ }
+ _result.setLastName(_tmpLastName);
+ _result.age = _cursor.getInt(_cursorIndexOfAge);
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ _statement.release();
+ }
+ });
+ }
+
+ @Override
+ public LiveData<List<User>> loadUsersByIdsLive(final int... ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT * FROM user where uid IN (");
+ final int _inputSize = ids == null ? 1 : ids.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ final int _argCount = 0 + _inputSize;
+ final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
+ int _argIndex = 1;
+ if (ids == null) {
+ _statement.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _statement.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<List<User>>() {
+ @Override
+ @Nullable
+ public List<User> call() throws Exception {
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
+ try {
+ final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
+ final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
+ final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
+ final List<User> _result = new ArrayList<User>();
+ while (_cursor.moveToNext()) {
+ final User _item_1;
+ _item_1 = new User();
+ _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
+ if (_cursor.isNull(_cursorIndexOfName)) {
+ _item_1.name = null;
+ } else {
+ _item_1.name = _cursor.getString(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_cursor.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+ }
+ _item_1.setLastName(_tmpLastName);
+ _item_1.age = _cursor.getInt(_cursorIndexOfAge);
+ _result.add(_item_1);
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ _statement.release();
+ }
+ });
+ }
+
+ @Override
+ public List<Child1> getChild1List() {
+ final String _sql = "SELECT * FROM Child1";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
+ final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
+ final List<Child1> _result = new ArrayList<Child1>();
+ while (_stmt.step()) {
+ final Child1 _item;
+ final int _tmpId;
+ _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
+ final String _tmpName;
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _tmpName = null;
+ } else {
+ _tmpName = _stmt.getText(_cursorIndexOfName);
+ }
+ final Info _tmpInfo;
+ if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
+ _tmpInfo = new Info();
+ _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
+ if (_stmt.isNull(_cursorIndexOfCode)) {
+ _tmpInfo.code = null;
+ } else {
+ _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
+ }
+ } else {
+ _tmpInfo = null;
+ }
+ _item = new Child1(_tmpId,_tmpName,_tmpInfo);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public List<Child2> getChild2List() {
+ final String _sql = "SELECT * FROM Child2";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
+ final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
+ final List<Child2> _result = new ArrayList<Child2>();
+ while (_stmt.step()) {
+ final Child2 _item;
+ final int _tmpId;
+ _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
+ final String _tmpName;
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _tmpName = null;
+ } else {
+ _tmpName = _stmt.getText(_cursorIndexOfName);
+ }
+ final Info _tmpInfo;
+ if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
+ _tmpInfo = new Info();
+ _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
+ if (_stmt.isNull(_cursorIndexOfCode)) {
+ _tmpInfo.code = null;
+ } else {
+ _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
+ }
+ } else {
+ _tmpInfo = null;
+ }
+ _item = new Child2(_tmpId,_tmpName,_tmpInfo);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public ListenableFuture<List<Child1>> getChild1ListListenableFuture() {
+ final String _sql = "SELECT * FROM Child1";
+ final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
+ final CancellationSignal _cancellationSignal = new CancellationSignal();
+ return GuavaRoom.createListenableFuture(__db, false, new Callable<List<Child1>>() {
+ @Override
+ public List<Child1> call() throws Exception {
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, _cancellationSignal);
+ try {
+ final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
+ final int _cursorIndexOfSerial = CursorUtil.getColumnIndexOrThrow(_cursor, "serial");
+ final int _cursorIndexOfCode = CursorUtil.getColumnIndexOrThrow(_cursor, "code");
+ final List<Child1> _result = new ArrayList<Child1>();
+ while (_cursor.moveToNext()) {
+ final Child1 _item;
+ final int _tmpId;
+ _tmpId = _cursor.getInt(_cursorIndexOfId);
+ final String _tmpName;
+ if (_cursor.isNull(_cursorIndexOfName)) {
+ _tmpName = null;
+ } else {
+ _tmpName = _cursor.getString(_cursorIndexOfName);
+ }
+ final Info _tmpInfo;
+ if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
+ _tmpInfo = new Info();
+ _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
+ if (_cursor.isNull(_cursorIndexOfCode)) {
+ _tmpInfo.code = null;
+ } else {
+ _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
+ }
+ } else {
+ _tmpInfo = null;
+ }
+ _item = new Child1(_tmpId,_tmpName,_tmpInfo);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+ }, _statement, true, _cancellationSignal);
+ }
+
+ @Override
+ public List<UserSummary> getUserNames() {
+ final String _sql = "SELECT `uid`, `name` FROM (SELECT * FROM User)";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ final int _cursorIndexOfUid = 0;
+ final int _cursorIndexOfName = 1;
+ final List<UserSummary> _result = new ArrayList<UserSummary>();
+ while (_stmt.step()) {
+ final UserSummary _item;
+ _item = new UserSummary();
+ _item.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _item.name = null;
+ } else {
+ _item.name = _stmt.getText(_cursorIndexOfName);
+ }
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public User getUserViaRawQuery(final SupportSQLiteQuery rawQuery) {
+ __db.assertNotSuspendingTransaction();
+ final Cursor _cursor = DBUtil.query(__db, rawQuery, false, null);
+ try {
+ final User _result;
+ if (_cursor.moveToFirst()) {
+ _result = __entityCursorConverter_fooBarUser(_cursor);
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+
+ private User __entityCursorConverter_fooBarUser(@NonNull final Cursor cursor) {
+ final User _entity;
+ final int _cursorIndexOfUid = CursorUtil.getColumnIndex(cursor, "uid");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndex(cursor, "name");
+ final int _cursorIndexOfLastName = CursorUtil.getColumnIndex(cursor, "lastName");
+ final int _cursorIndexOfAge = CursorUtil.getColumnIndex(cursor, "ageColumn");
+ _entity = new User();
+ if (_cursorIndexOfUid != -1) {
+ _entity.uid = cursor.getInt(_cursorIndexOfUid);
+ }
+ if (_cursorIndexOfName != -1) {
+ if (cursor.isNull(_cursorIndexOfName)) {
+ _entity.name = null;
+ } else {
+ _entity.name = cursor.getString(_cursorIndexOfName);
+ }
+ }
+ if (_cursorIndexOfLastName != -1) {
+ final String _tmpLastName;
+ if (cursor.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = cursor.getString(_cursorIndexOfLastName);
+ }
+ _entity.setLastName(_tmpLastName);
+ }
+ if (_cursorIndexOfAge != -1) {
+ _entity.age = cursor.getInt(_cursorIndexOfAge);
+ }
+ return _entity;
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/DeletionDao.java
new file mode 100644
index 0000000..996eaf5
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/DeletionDao.java
@@ -0,0 +1,367 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.room.EntityDeleteOrUpdateAdapter;
+import androidx.room.EntityDeletionOrUpdateAdapter;
+import androidx.room.RoomDatabase;
+import androidx.room.util.DBUtil;
+import androidx.room.util.SQLiteConnectionUtil;
+import androidx.room.util.StringUtil;
+import androidx.sqlite.SQLiteStatement;
+import androidx.sqlite.db.SupportSQLiteStatement;
+import io.reactivex.Completable;
+import io.reactivex.Maybe;
+import io.reactivex.Single;
+import java.lang.Class;
+import java.lang.Exception;
+import java.lang.Integer;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.lang.SuppressWarnings;
+import java.lang.Void;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class DeletionDao_Impl implements DeletionDao {
+ private final RoomDatabase __db;
+
+ private final EntityDeleteOrUpdateAdapter<User> __deleteAdapterOfUser;
+
+ private final EntityDeletionOrUpdateAdapter<User> __deleteCompatAdapterOfUser;
+
+ private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __deleteAdapterOfMultiPKeyEntity;
+
+ private final EntityDeleteOrUpdateAdapter<Book> __deleteAdapterOfBook;
+
+ public DeletionDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__deleteAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `User` WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ }
+ };
+ this.__deleteCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `User` WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ }
+ };
+ this.__deleteAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final MultiPKeyEntity entity) {
+ if (entity.name == null) {
+ statement.bindNull(1);
+ } else {
+ statement.bindText(1, entity.name);
+ }
+ if (entity.lastName == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.lastName);
+ }
+ }
+ };
+ this.__deleteAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `Book` WHERE `bookId` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ }
+ };
+ }
+
+ @Override
+ public void deleteUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __deleteAdapterOfUser.handle(_connection, user);
+ return null;
+ });
+ }
+
+ @Override
+ public void deleteUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __deleteAdapterOfUser.handle(_connection, user1);
+ __deleteAdapterOfUser.handleMultiple(_connection, others);
+ return null;
+ });
+ }
+
+ @Override
+ public void deleteArrayOfUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __deleteAdapterOfUser.handleMultiple(_connection, users);
+ return null;
+ });
+ }
+
+ @Override
+ public Integer deleteUserAndReturnCountObject(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handle(_connection, user);
+ return _result;
+ });
+ }
+
+ @Override
+ public int deleteUserAndReturnCount(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handle(_connection, user);
+ return _result;
+ });
+ }
+
+ @Override
+ public int deleteUserAndReturnCount(final User user1, final List<User> others) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handle(_connection, user1);
+ _result += __deleteAdapterOfUser.handleMultiple(_connection, others);
+ return _result;
+ });
+ }
+
+ @Override
+ public int deleteUserAndReturnCount(final User[] users) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handleMultiple(_connection, users);
+ return _result;
+ });
+ }
+
+ @Override
+ public Completable deleteUserCompletable(final User user) {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ __db.beginTransaction();
+ try {
+ __deleteCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Single<Integer> deleteUserSingle(final User user) {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __deleteCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Maybe<Integer> deleteUserMaybe(final User user) {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __deleteCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public int multiPKey(final MultiPKeyEntity entity) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfMultiPKeyEntity.handle(_connection, entity);
+ return _result;
+ });
+ }
+
+ @Override
+ public void deleteUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __deleteAdapterOfUser.handle(_connection, user);
+ __deleteAdapterOfBook.handle(_connection, book);
+ return null;
+ });
+ }
+
+ @Override
+ public int deleteByUid(final int uid) {
+ final String _sql = "DELETE FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ _stmt.step();
+ return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public Completable deleteByUidCompletable(final int uid) {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ final String _sql = "DELETE FROM user where uid = ?";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ __db.beginTransaction();
+ try {
+ _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Single<Integer> deleteByUidSingle(final int uid) {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "DELETE FROM user where uid = ?";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Maybe<Integer> deleteByUidMaybe(final int uid) {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "DELETE FROM user where uid = ?";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public int deleteByUidList(final int... uid) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("DELETE FROM user where uid IN(");
+ final int _inputSize = uid == null ? 1 : uid.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (uid == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : uid) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ _stmt.step();
+ return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public int deleteEverything() {
+ final String _sql = "DELETE FROM user";
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ _stmt.step();
+ return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpdateDao.java
new file mode 100644
index 0000000..6214715
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpdateDao.java
@@ -0,0 +1,405 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.room.EntityDeleteOrUpdateAdapter;
+import androidx.room.EntityDeletionOrUpdateAdapter;
+import androidx.room.RoomDatabase;
+import androidx.room.util.DBUtil;
+import androidx.sqlite.SQLiteStatement;
+import androidx.sqlite.db.SupportSQLiteStatement;
+import io.reactivex.Completable;
+import io.reactivex.Maybe;
+import io.reactivex.Single;
+import java.lang.Class;
+import java.lang.Exception;
+import java.lang.Integer;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.SuppressWarnings;
+import java.lang.Void;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class UpdateDao_Impl implements UpdateDao {
+ private final RoomDatabase __db;
+
+ private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser;
+
+ private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser_1;
+
+ private final EntityDeletionOrUpdateAdapter<User> __updateCompatAdapterOfUser;
+
+ private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __updateAdapterOfMultiPKeyEntity;
+
+ private final EntityDeleteOrUpdateAdapter<Book> __updateAdapterOfBook;
+
+ public UpdateDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__updateAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ };
+ this.__updateAdapterOfUser_1 = new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ };
+ this.__updateCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindString(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindString(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ };
+ this.__updateAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final MultiPKeyEntity entity) {
+ if (entity.name == null) {
+ statement.bindNull(1);
+ } else {
+ statement.bindText(1, entity.name);
+ }
+ if (entity.lastName == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.lastName);
+ }
+ if (entity.name == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.name);
+ }
+ if (entity.lastName == null) {
+ statement.bindNull(4);
+ } else {
+ statement.bindText(4, entity.lastName);
+ }
+ }
+ };
+ this.__updateAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ statement.bindLong(3, entity.bookId);
+ }
+ };
+ }
+
+ @Override
+ public void updateUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser.handle(_connection, user);
+ return null;
+ });
+ }
+
+ @Override
+ public void updateUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser.handle(_connection, user1);
+ __updateAdapterOfUser.handleMultiple(_connection, others);
+ return null;
+ });
+ }
+
+ @Override
+ public void updateArrayOfUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser.handleMultiple(_connection, users);
+ return null;
+ });
+ }
+
+ @Override
+ public void updateTwoUsers(final User userOne, final User userTwo) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser_1.handle(_connection, userOne);
+ __updateAdapterOfUser_1.handle(_connection, userTwo);
+ return null;
+ });
+ }
+
+ @Override
+ public int updateUserAndReturnCount(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handle(_connection, user);
+ return _result;
+ });
+ }
+
+ @Override
+ public int updateUserAndReturnCount(final User user1, final List<User> others) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handle(_connection, user1);
+ _result += __updateAdapterOfUser.handleMultiple(_connection, others);
+ return _result;
+ });
+ }
+
+ @Override
+ public int updateUserAndReturnCount(final User[] users) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handleMultiple(_connection, users);
+ return _result;
+ });
+ }
+
+ @Override
+ public Integer updateUserAndReturnCountObject(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handle(_connection, user);
+ return _result;
+ });
+ }
+
+ @Override
+ public Completable updateUserAndReturnCountCompletable(final User user) {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ __db.beginTransaction();
+ try {
+ __updateCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Single<Integer> updateUserAndReturnCountSingle(final User user) {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __updateCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Maybe<Integer> updateUserAndReturnCountMaybe(final User user) {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __updateCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public int multiPKey(final MultiPKeyEntity entity) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfMultiPKeyEntity.handle(_connection, entity);
+ return _result;
+ });
+ }
+
+ @Override
+ public void updateUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser.handle(_connection, user);
+ __updateAdapterOfBook.handle(_connection, book);
+ return null;
+ });
+ }
+
+ @Override
+ public void ageUserByUid(final String uid) {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = ?";
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (uid == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindText(_argIndex, uid);
+ }
+ _stmt.step();
+ return null;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public void ageUserAll() {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ _stmt.step();
+ return null;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public Completable ageUserAllCompletable() {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ __db.beginTransaction();
+ try {
+ _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Single<Integer> ageUserAllSingle() {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Maybe<Integer> ageUserAllMaybe() {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpsertDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpsertDao.java
new file mode 100644
index 0000000..5cacce2
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpsertDao.java
@@ -0,0 +1,164 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.room.EntityDeleteOrUpdateAdapter;
+import androidx.room.EntityInsertAdapter;
+import androidx.room.EntityUpsertAdapter;
+import androidx.room.RoomDatabase;
+import androidx.room.util.DBUtil;
+import androidx.sqlite.SQLiteStatement;
+import java.lang.Class;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.SuppressWarnings;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class UpsertDao_Impl implements UpsertDao {
+ private final RoomDatabase __db;
+
+ private final EntityUpsertAdapter<User> __upsertAdapterOfUser;
+
+ private final EntityUpsertAdapter<Book> __upsertAdapterOfBook;
+
+ public UpsertDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__upsertAdapterOfUser = new EntityUpsertAdapter<User>(new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ }
+ }, new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ });
+ this.__upsertAdapterOfBook = new EntityUpsertAdapter<Book>(new EntityInsertAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ }
+ }, new EntityDeleteOrUpdateAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ statement.bindLong(3, entity.bookId);
+ }
+ });
+ }
+
+ @Override
+ public void upsertUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, user);
+ return null;
+ });
+ }
+
+ @Override
+ public void upsertUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, user1);
+ __upsertAdapterOfUser.upsert(_connection, others);
+ return null;
+ });
+ }
+
+ @Override
+ public void upsertUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, users);
+ return null;
+ });
+ }
+
+ @Override
+ public void upsertTwoUsers(final User userOne, final User userTwo) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, userOne);
+ __upsertAdapterOfUser.upsert(_connection, userTwo);
+ return null;
+ });
+ }
+
+ @Override
+ public void upsertUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, user);
+ __upsertAdapterOfBook.upsert(_connection, book);
+ return null;
+ });
+ }
+
+ @Override
+ public long upsertAndReturnId(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ return __upsertAdapterOfUser.upsertAndReturnId(_connection, user);
+ });
+ }
+
+ @Override
+ public long[] upsertAndReturnIdsArray(final User[] users) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ return __upsertAdapterOfUser.upsertAndReturnIdsArray(_connection, users);
+ });
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/WriterDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/WriterDao.java
new file mode 100644
index 0000000..2f679db7
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/WriterDao.java
@@ -0,0 +1,162 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.room.EntityInsertAdapter;
+import androidx.room.RoomDatabase;
+import androidx.room.util.DBUtil;
+import androidx.sqlite.SQLiteStatement;
+import java.lang.Class;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.SuppressWarnings;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class WriterDao_Impl implements WriterDao {
+ private final RoomDatabase __db;
+
+ private final EntityInsertAdapter<User> __insertAdapterOfUser;
+
+ private final EntityInsertAdapter<User> __insertAdapterOfUser_1;
+
+ private final EntityInsertAdapter<User> __insertAdapterOfUser_2;
+
+ private final EntityInsertAdapter<Book> __insertAdapterOfBook;
+
+ public WriterDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__insertAdapterOfUser = new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT OR ABORT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ }
+ };
+ this.__insertAdapterOfUser_1 = new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT OR REPLACE INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ }
+ };
+ this.__insertAdapterOfUser_2 = new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ }
+ };
+ this.__insertAdapterOfBook = new EntityInsertAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT OR ABORT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ }
+ };
+ }
+
+ @Override
+ public void insertUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser.insert(_connection, user);
+ return null;
+ });
+ }
+
+ @Override
+ public void insertUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser.insert(_connection, user1);
+ __insertAdapterOfUser.insert(_connection, others);
+ return null;
+ });
+ }
+
+ @Override
+ public void insertUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser_1.insert(_connection, users);
+ return null;
+ });
+ }
+
+ @Override
+ public void insertTwoUsers(final User userOne, final User userTwo) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser_2.insert(_connection, userOne);
+ __insertAdapterOfUser_2.insert(_connection, userTwo);
+ return null;
+ });
+ }
+
+ @Override
+ public void insertUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser.insert(_connection, user);
+ __insertAdapterOfBook.insert(_connection, book);
+ return null;
+ });
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/ComplexDao.java
new file mode 100644
index 0000000..c750934
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/ComplexDao.java
@@ -0,0 +1,764 @@
+package foo.bar;
+
+import android.database.Cursor;
+import android.os.CancellationSignal;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+import androidx.room.RoomDatabase;
+import androidx.room.RoomSQLiteQuery;
+import androidx.room.guava.GuavaRoom;
+import androidx.room.util.CursorUtil;
+import androidx.room.util.DBUtil;
+import androidx.room.util.SQLiteStatementUtil;
+import androidx.room.util.StringUtil;
+import androidx.sqlite.SQLiteConnection;
+import androidx.sqlite.SQLiteStatement;
+import androidx.sqlite.db.SupportSQLiteQuery;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.lang.Boolean;
+import java.lang.Class;
+import java.lang.Exception;
+import java.lang.Integer;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.lang.SuppressWarnings;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import javax.annotation.processing.Generated;
+import kotlin.jvm.functions.Function1;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class ComplexDao_Impl extends ComplexDao {
+ private final RoomDatabase __db;
+
+ public ComplexDao_Impl(final ComplexDatabase __db) {
+ super(__db);
+ this.__db = __db;
+ }
+
+ @Override
+ public boolean transactionMethod(final int i, final String s, final long l) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Boolean>() {
+ @Override
+ @NonNull
+ public Boolean invoke(@NonNull final SQLiteConnection _connection) {
+ return ComplexDao_Impl.super.transactionMethod(i, s, l);
+ }
+ });
+ }
+
+ @Override
+ public List<ComplexDao.FullName> fullNames(final int id) {
+ final String _sql = "SELECT name || lastName as fullName, uid as id FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<ComplexDao.FullName>>() {
+ @Override
+ @NonNull
+ public List<ComplexDao.FullName> invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, id);
+ final int _cursorIndexOfFullName = 0;
+ final int _cursorIndexOfId = 1;
+ final List<ComplexDao.FullName> _result = new ArrayList<ComplexDao.FullName>();
+ while (_stmt.step()) {
+ final ComplexDao.FullName _item;
+ _item = new ComplexDao.FullName();
+ if (_stmt.isNull(_cursorIndexOfFullName)) {
+ _item.fullName = null;
+ } else {
+ _item.fullName = _stmt.getText(_cursorIndexOfFullName);
+ }
+ _item.id = (int) (_stmt.getLong(_cursorIndexOfId));
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public User getById(final int id) {
+ final String _sql = "SELECT * FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, User>() {
+ @Override
+ @NonNull
+ public User invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, id);
+ final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
+ final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
+ final User _result;
+ if (_stmt.step()) {
+ _result = new User();
+ _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _result.name = null;
+ } else {
+ _result.name = _stmt.getText(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_stmt.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
+ }
+ _result.setLastName(_tmpLastName);
+ _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public User findByName(final String name, final String lastName) {
+ final String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, User>() {
+ @Override
+ @NonNull
+ public User invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (name == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindText(_argIndex, name);
+ }
+ _argIndex = 2;
+ if (lastName == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindText(_argIndex, lastName);
+ }
+ final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
+ final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
+ final User _result;
+ if (_stmt.step()) {
+ _result = new User();
+ _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _result.name = null;
+ } else {
+ _result.name = _stmt.getText(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_stmt.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
+ }
+ _result.setLastName(_tmpLastName);
+ _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public List<User> loadAllByIds(final int... ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT * FROM user where uid IN (");
+ final int _inputSize = ids == null ? 1 : ids.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<User>>() {
+ @Override
+ @NonNull
+ public List<User> invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
+ final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
+ final List<User> _result = new ArrayList<User>();
+ while (_stmt.step()) {
+ final User _item_1;
+ _item_1 = new User();
+ _item_1.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _item_1.name = null;
+ } else {
+ _item_1.name = _stmt.getText(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_stmt.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
+ }
+ _item_1.setLastName(_tmpLastName);
+ _item_1.age = (int) (_stmt.getLong(_cursorIndexOfAge));
+ _result.add(_item_1);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ int getAge(final int id) {
+ final String _sql = "SELECT ageColumn FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, id);
+ final int _result;
+ if (_stmt.step()) {
+ _result = (int) (_stmt.getLong(0));
+ } else {
+ _result = 0;
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public int[] getAllAges(final int... ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+ final int _inputSize = ids == null ? 1 : ids.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, int[]>() {
+ @Override
+ @NonNull
+ public int[] invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ final List<Integer> _listResult = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_1;
+ if (_stmt.isNull(0)) {
+ _item_1 = null;
+ } else {
+ _item_1 = (int) (_stmt.getLong(0));
+ }
+ _listResult.add(_item_1);
+ }
+ final int[] _tmpArrayResult = new int[_listResult.size()];
+ int _index = 0;
+ for (int _listItem : _listResult) {
+ _tmpArrayResult[_index] = _listItem;
+ _index++;
+ }
+ final int[] _result = _tmpArrayResult;
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public List<Integer> getAllAgesAsList(final List<Integer> ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+ final int _inputSize = ids == null ? 1 : ids.size();
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Integer>>() {
+ @Override
+ @NonNull
+ public List<Integer> invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (Integer _item : ids) {
+ if (_item == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindLong(_argIndex, _item);
+ }
+ _argIndex++;
+ }
+ }
+ final List<Integer> _result = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_1;
+ if (_stmt.isNull(0)) {
+ _item_1 = null;
+ } else {
+ _item_1 = (int) (_stmt.getLong(0));
+ }
+ _result.add(_item_1);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public List<Integer> getAllAgesAsList(final List<Integer> ids1, final int[] ids2,
+ final int... ids3) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+ final int _inputSize = ids1 == null ? 1 : ids1.size();
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(") OR uid IN (");
+ final int _inputSize_1 = ids2 == null ? 1 : ids2.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize_1);
+ _stringBuilder.append(") OR uid IN (");
+ final int _inputSize_2 = ids3 == null ? 1 : ids3.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize_2);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Integer>>() {
+ @Override
+ @NonNull
+ public List<Integer> invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids1 == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (Integer _item : ids1) {
+ if (_item == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindLong(_argIndex, _item);
+ }
+ _argIndex++;
+ }
+ }
+ _argIndex = 1 + _inputSize;
+ if (ids2 == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item_1 : ids2) {
+ _stmt.bindLong(_argIndex, _item_1);
+ _argIndex++;
+ }
+ }
+ _argIndex = 1 + _inputSize + _inputSize_1;
+ if (ids3 == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item_2 : ids3) {
+ _stmt.bindLong(_argIndex, _item_2);
+ _argIndex++;
+ }
+ }
+ final List<Integer> _result = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_3;
+ if (_stmt.isNull(0)) {
+ _item_3 = null;
+ } else {
+ _item_3 = (int) (_stmt.getLong(0));
+ }
+ _result.add(_item_3);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public LiveData<User> getByIdLive(final int id) {
+ final String _sql = "SELECT * FROM user where uid = ?";
+ final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
+ int _argIndex = 1;
+ _statement.bindLong(_argIndex, id);
+ return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<User>() {
+ @Override
+ @Nullable
+ public User call() throws Exception {
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
+ try {
+ final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
+ final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
+ final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
+ final User _result;
+ if (_cursor.moveToFirst()) {
+ _result = new User();
+ _result.uid = _cursor.getInt(_cursorIndexOfUid);
+ if (_cursor.isNull(_cursorIndexOfName)) {
+ _result.name = null;
+ } else {
+ _result.name = _cursor.getString(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_cursor.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+ }
+ _result.setLastName(_tmpLastName);
+ _result.age = _cursor.getInt(_cursorIndexOfAge);
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ _statement.release();
+ }
+ });
+ }
+
+ @Override
+ public LiveData<List<User>> loadUsersByIdsLive(final int... ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT * FROM user where uid IN (");
+ final int _inputSize = ids == null ? 1 : ids.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ final int _argCount = 0 + _inputSize;
+ final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
+ int _argIndex = 1;
+ if (ids == null) {
+ _statement.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _statement.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<List<User>>() {
+ @Override
+ @Nullable
+ public List<User> call() throws Exception {
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
+ try {
+ final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
+ final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
+ final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
+ final List<User> _result = new ArrayList<User>();
+ while (_cursor.moveToNext()) {
+ final User _item_1;
+ _item_1 = new User();
+ _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
+ if (_cursor.isNull(_cursorIndexOfName)) {
+ _item_1.name = null;
+ } else {
+ _item_1.name = _cursor.getString(_cursorIndexOfName);
+ }
+ final String _tmpLastName;
+ if (_cursor.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+ }
+ _item_1.setLastName(_tmpLastName);
+ _item_1.age = _cursor.getInt(_cursorIndexOfAge);
+ _result.add(_item_1);
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ _statement.release();
+ }
+ });
+ }
+
+ @Override
+ public List<Child1> getChild1List() {
+ final String _sql = "SELECT * FROM Child1";
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Child1>>() {
+ @Override
+ @NonNull
+ public List<Child1> invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
+ final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
+ final List<Child1> _result = new ArrayList<Child1>();
+ while (_stmt.step()) {
+ final Child1 _item;
+ final int _tmpId;
+ _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
+ final String _tmpName;
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _tmpName = null;
+ } else {
+ _tmpName = _stmt.getText(_cursorIndexOfName);
+ }
+ final Info _tmpInfo;
+ if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
+ _tmpInfo = new Info();
+ _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
+ if (_stmt.isNull(_cursorIndexOfCode)) {
+ _tmpInfo.code = null;
+ } else {
+ _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
+ }
+ } else {
+ _tmpInfo = null;
+ }
+ _item = new Child1(_tmpId,_tmpName,_tmpInfo);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public List<Child2> getChild2List() {
+ final String _sql = "SELECT * FROM Child2";
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Child2>>() {
+ @Override
+ @NonNull
+ public List<Child2> invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
+ final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
+ final List<Child2> _result = new ArrayList<Child2>();
+ while (_stmt.step()) {
+ final Child2 _item;
+ final int _tmpId;
+ _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
+ final String _tmpName;
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _tmpName = null;
+ } else {
+ _tmpName = _stmt.getText(_cursorIndexOfName);
+ }
+ final Info _tmpInfo;
+ if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
+ _tmpInfo = new Info();
+ _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
+ if (_stmt.isNull(_cursorIndexOfCode)) {
+ _tmpInfo.code = null;
+ } else {
+ _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
+ }
+ } else {
+ _tmpInfo = null;
+ }
+ _item = new Child2(_tmpId,_tmpName,_tmpInfo);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public ListenableFuture<List<Child1>> getChild1ListListenableFuture() {
+ final String _sql = "SELECT * FROM Child1";
+ final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
+ final CancellationSignal _cancellationSignal = new CancellationSignal();
+ return GuavaRoom.createListenableFuture(__db, false, new Callable<List<Child1>>() {
+ @Override
+ public List<Child1> call() throws Exception {
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, _cancellationSignal);
+ try {
+ final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
+ final int _cursorIndexOfSerial = CursorUtil.getColumnIndexOrThrow(_cursor, "serial");
+ final int _cursorIndexOfCode = CursorUtil.getColumnIndexOrThrow(_cursor, "code");
+ final List<Child1> _result = new ArrayList<Child1>();
+ while (_cursor.moveToNext()) {
+ final Child1 _item;
+ final int _tmpId;
+ _tmpId = _cursor.getInt(_cursorIndexOfId);
+ final String _tmpName;
+ if (_cursor.isNull(_cursorIndexOfName)) {
+ _tmpName = null;
+ } else {
+ _tmpName = _cursor.getString(_cursorIndexOfName);
+ }
+ final Info _tmpInfo;
+ if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
+ _tmpInfo = new Info();
+ _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
+ if (_cursor.isNull(_cursorIndexOfCode)) {
+ _tmpInfo.code = null;
+ } else {
+ _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
+ }
+ } else {
+ _tmpInfo = null;
+ }
+ _item = new Child1(_tmpId,_tmpName,_tmpInfo);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+ }, _statement, true, _cancellationSignal);
+ }
+
+ @Override
+ public List<UserSummary> getUserNames() {
+ final String _sql = "SELECT `uid`, `name` FROM (SELECT * FROM User)";
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<UserSummary>>() {
+ @Override
+ @NonNull
+ public List<UserSummary> invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ final int _cursorIndexOfUid = 0;
+ final int _cursorIndexOfName = 1;
+ final List<UserSummary> _result = new ArrayList<UserSummary>();
+ while (_stmt.step()) {
+ final UserSummary _item;
+ _item = new UserSummary();
+ _item.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ if (_stmt.isNull(_cursorIndexOfName)) {
+ _item.name = null;
+ } else {
+ _item.name = _stmt.getText(_cursorIndexOfName);
+ }
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public User getUserViaRawQuery(final SupportSQLiteQuery rawQuery) {
+ __db.assertNotSuspendingTransaction();
+ final Cursor _cursor = DBUtil.query(__db, rawQuery, false, null);
+ try {
+ final User _result;
+ if (_cursor.moveToFirst()) {
+ _result = __entityCursorConverter_fooBarUser(_cursor);
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+
+ private User __entityCursorConverter_fooBarUser(@NonNull final Cursor cursor) {
+ final User _entity;
+ final int _cursorIndexOfUid = CursorUtil.getColumnIndex(cursor, "uid");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndex(cursor, "name");
+ final int _cursorIndexOfLastName = CursorUtil.getColumnIndex(cursor, "lastName");
+ final int _cursorIndexOfAge = CursorUtil.getColumnIndex(cursor, "ageColumn");
+ _entity = new User();
+ if (_cursorIndexOfUid != -1) {
+ _entity.uid = cursor.getInt(_cursorIndexOfUid);
+ }
+ if (_cursorIndexOfName != -1) {
+ if (cursor.isNull(_cursorIndexOfName)) {
+ _entity.name = null;
+ } else {
+ _entity.name = cursor.getString(_cursorIndexOfName);
+ }
+ }
+ if (_cursorIndexOfLastName != -1) {
+ final String _tmpLastName;
+ if (cursor.isNull(_cursorIndexOfLastName)) {
+ _tmpLastName = null;
+ } else {
+ _tmpLastName = cursor.getString(_cursorIndexOfLastName);
+ }
+ _entity.setLastName(_tmpLastName);
+ }
+ if (_cursorIndexOfAge != -1) {
+ _entity.age = cursor.getInt(_cursorIndexOfAge);
+ }
+ return _entity;
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/DeletionDao.java
new file mode 100644
index 0000000..e287d17
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/DeletionDao.java
@@ -0,0 +1,417 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.room.EntityDeleteOrUpdateAdapter;
+import androidx.room.EntityDeletionOrUpdateAdapter;
+import androidx.room.RoomDatabase;
+import androidx.room.util.DBUtil;
+import androidx.room.util.SQLiteConnectionUtil;
+import androidx.room.util.StringUtil;
+import androidx.sqlite.SQLiteConnection;
+import androidx.sqlite.SQLiteStatement;
+import androidx.sqlite.db.SupportSQLiteStatement;
+import io.reactivex.Completable;
+import io.reactivex.Maybe;
+import io.reactivex.Single;
+import java.lang.Class;
+import java.lang.Exception;
+import java.lang.Integer;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.StringBuilder;
+import java.lang.SuppressWarnings;
+import java.lang.Void;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import javax.annotation.processing.Generated;
+import kotlin.jvm.functions.Function1;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class DeletionDao_Impl implements DeletionDao {
+ private final RoomDatabase __db;
+
+ private final EntityDeleteOrUpdateAdapter<User> __deleteAdapterOfUser;
+
+ private final EntityDeletionOrUpdateAdapter<User> __deleteCompatAdapterOfUser;
+
+ private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __deleteAdapterOfMultiPKeyEntity;
+
+ private final EntityDeleteOrUpdateAdapter<Book> __deleteAdapterOfBook;
+
+ public DeletionDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__deleteAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `User` WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ }
+ };
+ this.__deleteCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `User` WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ }
+ };
+ this.__deleteAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final MultiPKeyEntity entity) {
+ if (entity.name == null) {
+ statement.bindNull(1);
+ } else {
+ statement.bindText(1, entity.name);
+ }
+ if (entity.lastName == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.lastName);
+ }
+ }
+ };
+ this.__deleteAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `Book` WHERE `bookId` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ }
+ };
+ }
+
+ @Override
+ public void deleteUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __deleteAdapterOfUser.handle(_connection, user);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void deleteUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __deleteAdapterOfUser.handle(_connection, user1);
+ __deleteAdapterOfUser.handleMultiple(_connection, others);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void deleteArrayOfUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __deleteAdapterOfUser.handleMultiple(_connection, users);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public Integer deleteUserAndReturnCountObject(final User user) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handle(_connection, user);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public int deleteUserAndReturnCount(final User user) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handle(_connection, user);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public int deleteUserAndReturnCount(final User user1, final List<User> others) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handle(_connection, user1);
+ _result += __deleteAdapterOfUser.handleMultiple(_connection, others);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public int deleteUserAndReturnCount(final User[] users) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handleMultiple(_connection, users);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public Completable deleteUserCompletable(final User user) {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ __db.beginTransaction();
+ try {
+ __deleteCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Single<Integer> deleteUserSingle(final User user) {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __deleteCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Maybe<Integer> deleteUserMaybe(final User user) {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __deleteCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public int multiPKey(final MultiPKeyEntity entity) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __deleteAdapterOfMultiPKeyEntity.handle(_connection, entity);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public void deleteUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __deleteAdapterOfUser.handle(_connection, user);
+ __deleteAdapterOfBook.handle(_connection, book);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public int deleteByUid(final int uid) {
+ final String _sql = "DELETE FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ _stmt.step();
+ return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Completable deleteByUidCompletable(final int uid) {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ final String _sql = "DELETE FROM user where uid = ?";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ __db.beginTransaction();
+ try {
+ _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Single<Integer> deleteByUidSingle(final int uid) {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "DELETE FROM user where uid = ?";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Maybe<Integer> deleteByUidMaybe(final int uid) {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "DELETE FROM user where uid = ?";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public int deleteByUidList(final int... uid) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("DELETE FROM user where uid IN(");
+ final int _inputSize = uid == null ? 1 : uid.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (uid == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : uid) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ _stmt.step();
+ return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public int deleteEverything() {
+ final String _sql = "DELETE FROM user";
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ _stmt.step();
+ return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpdateDao.java
new file mode 100644
index 0000000..f3ca3df
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpdateDao.java
@@ -0,0 +1,455 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.room.EntityDeleteOrUpdateAdapter;
+import androidx.room.EntityDeletionOrUpdateAdapter;
+import androidx.room.RoomDatabase;
+import androidx.room.util.DBUtil;
+import androidx.sqlite.SQLiteConnection;
+import androidx.sqlite.SQLiteStatement;
+import androidx.sqlite.db.SupportSQLiteStatement;
+import io.reactivex.Completable;
+import io.reactivex.Maybe;
+import io.reactivex.Single;
+import java.lang.Class;
+import java.lang.Exception;
+import java.lang.Integer;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.SuppressWarnings;
+import java.lang.Void;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import javax.annotation.processing.Generated;
+import kotlin.jvm.functions.Function1;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class UpdateDao_Impl implements UpdateDao {
+ private final RoomDatabase __db;
+
+ private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser;
+
+ private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser_1;
+
+ private final EntityDeletionOrUpdateAdapter<User> __updateCompatAdapterOfUser;
+
+ private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __updateAdapterOfMultiPKeyEntity;
+
+ private final EntityDeleteOrUpdateAdapter<Book> __updateAdapterOfBook;
+
+ public UpdateDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__updateAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ };
+ this.__updateAdapterOfUser_1 = new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ };
+ this.__updateCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindString(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindString(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ };
+ this.__updateAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final MultiPKeyEntity entity) {
+ if (entity.name == null) {
+ statement.bindNull(1);
+ } else {
+ statement.bindText(1, entity.name);
+ }
+ if (entity.lastName == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.lastName);
+ }
+ if (entity.name == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.name);
+ }
+ if (entity.lastName == null) {
+ statement.bindNull(4);
+ } else {
+ statement.bindText(4, entity.lastName);
+ }
+ }
+ };
+ this.__updateAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ statement.bindLong(3, entity.bookId);
+ }
+ };
+ }
+
+ @Override
+ public void updateUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __updateAdapterOfUser.handle(_connection, user);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void updateUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __updateAdapterOfUser.handle(_connection, user1);
+ __updateAdapterOfUser.handleMultiple(_connection, others);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void updateArrayOfUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __updateAdapterOfUser.handleMultiple(_connection, users);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void updateTwoUsers(final User userOne, final User userTwo) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __updateAdapterOfUser_1.handle(_connection, userOne);
+ __updateAdapterOfUser_1.handle(_connection, userTwo);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public int updateUserAndReturnCount(final User user) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handle(_connection, user);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public int updateUserAndReturnCount(final User user1, final List<User> others) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handle(_connection, user1);
+ _result += __updateAdapterOfUser.handleMultiple(_connection, others);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public int updateUserAndReturnCount(final User[] users) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handleMultiple(_connection, users);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public Integer updateUserAndReturnCountObject(final User user) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handle(_connection, user);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public Completable updateUserAndReturnCountCompletable(final User user) {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ __db.beginTransaction();
+ try {
+ __updateCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Single<Integer> updateUserAndReturnCountSingle(final User user) {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __updateCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Maybe<Integer> updateUserAndReturnCountMaybe(final User user) {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __updateCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public int multiPKey(final MultiPKeyEntity entity) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
+ @Override
+ @NonNull
+ public Integer invoke(@NonNull final SQLiteConnection _connection) {
+ int _result = 0;
+ _result += __updateAdapterOfMultiPKeyEntity.handle(_connection, entity);
+ return _result;
+ }
+ });
+ }
+
+ @Override
+ public void updateUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __updateAdapterOfUser.handle(_connection, user);
+ __updateAdapterOfBook.handle(_connection, book);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void ageUserByUid(final String uid) {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = ?";
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (uid == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindText(_argIndex, uid);
+ }
+ _stmt.step();
+ return null;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void ageUserAll() {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ _stmt.step();
+ return null;
+ } finally {
+ _stmt.close();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Completable ageUserAllCompletable() {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ __db.beginTransaction();
+ try {
+ _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Single<Integer> ageUserAllSingle() {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @Override
+ public Maybe<Integer> ageUserAllMaybe() {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpsertDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpsertDao.java
new file mode 100644
index 0000000..f6dbf33
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpsertDao.java
@@ -0,0 +1,196 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.room.EntityDeleteOrUpdateAdapter;
+import androidx.room.EntityInsertAdapter;
+import androidx.room.EntityUpsertAdapter;
+import androidx.room.RoomDatabase;
+import androidx.room.util.DBUtil;
+import androidx.sqlite.SQLiteConnection;
+import androidx.sqlite.SQLiteStatement;
+import java.lang.Class;
+import java.lang.Long;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.SuppressWarnings;
+import java.lang.Void;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.processing.Generated;
+import kotlin.jvm.functions.Function1;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class UpsertDao_Impl implements UpsertDao {
+ private final RoomDatabase __db;
+
+ private final EntityUpsertAdapter<User> __upsertAdapterOfUser;
+
+ private final EntityUpsertAdapter<Book> __upsertAdapterOfBook;
+
+ public UpsertDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__upsertAdapterOfUser = new EntityUpsertAdapter<User>(new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ }
+ }, new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ });
+ this.__upsertAdapterOfBook = new EntityUpsertAdapter<Book>(new EntityInsertAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ }
+ }, new EntityDeleteOrUpdateAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ statement.bindLong(3, entity.bookId);
+ }
+ });
+ }
+
+ @Override
+ public void upsertUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __upsertAdapterOfUser.upsert(_connection, user);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void upsertUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __upsertAdapterOfUser.upsert(_connection, user1);
+ __upsertAdapterOfUser.upsert(_connection, others);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void upsertUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __upsertAdapterOfUser.upsert(_connection, users);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void upsertTwoUsers(final User userOne, final User userTwo) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __upsertAdapterOfUser.upsert(_connection, userOne);
+ __upsertAdapterOfUser.upsert(_connection, userTwo);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void upsertUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __upsertAdapterOfUser.upsert(_connection, user);
+ __upsertAdapterOfBook.upsert(_connection, book);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public long upsertAndReturnId(final User user) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Long>() {
+ @Override
+ @NonNull
+ public Long invoke(@NonNull final SQLiteConnection _connection) {
+ return __upsertAdapterOfUser.upsertAndReturnId(_connection, user);
+ }
+ });
+ }
+
+ @Override
+ public long[] upsertAndReturnIdsArray(final User[] users) {
+ return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, long[]>() {
+ @Override
+ @NonNull
+ public long[] invoke(@NonNull final SQLiteConnection _connection) {
+ return __upsertAdapterOfUser.upsertAndReturnIdsArray(_connection, users);
+ }
+ });
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/WriterDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/WriterDao.java
new file mode 100644
index 0000000..6402763
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/WriterDao.java
@@ -0,0 +1,185 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.room.EntityInsertAdapter;
+import androidx.room.RoomDatabase;
+import androidx.room.util.DBUtil;
+import androidx.sqlite.SQLiteConnection;
+import androidx.sqlite.SQLiteStatement;
+import java.lang.Class;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.SuppressWarnings;
+import java.lang.Void;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.processing.Generated;
+import kotlin.jvm.functions.Function1;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation", "removal"})
+public final class WriterDao_Impl implements WriterDao {
+ private final RoomDatabase __db;
+
+ private final EntityInsertAdapter<User> __insertAdapterOfUser;
+
+ private final EntityInsertAdapter<User> __insertAdapterOfUser_1;
+
+ private final EntityInsertAdapter<User> __insertAdapterOfUser_2;
+
+ private final EntityInsertAdapter<Book> __insertAdapterOfBook;
+
+ public WriterDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__insertAdapterOfUser = new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT OR ABORT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ }
+ };
+ this.__insertAdapterOfUser_1 = new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT OR REPLACE INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ }
+ };
+ this.__insertAdapterOfUser_2 = new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final User entity) {
+ statement.bindLong(1, entity.uid);
+ if (entity.name == null) {
+ statement.bindNull(2);
+ } else {
+ statement.bindText(2, entity.name);
+ }
+ if (entity.getLastName() == null) {
+ statement.bindNull(3);
+ } else {
+ statement.bindText(3, entity.getLastName());
+ }
+ statement.bindLong(4, entity.age);
+ }
+ };
+ this.__insertAdapterOfBook = new EntityInsertAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT OR ABORT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
+ }
+
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ }
+ };
+ }
+
+ @Override
+ public void insertUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __insertAdapterOfUser.insert(_connection, user);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void insertUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __insertAdapterOfUser.insert(_connection, user1);
+ __insertAdapterOfUser.insert(_connection, others);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void insertUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __insertAdapterOfUser_1.insert(_connection, users);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void insertTwoUsers(final User userOne, final User userTwo) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __insertAdapterOfUser_2.insert(_connection, userOne);
+ __insertAdapterOfUser_2.insert(_connection, userTwo);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void insertUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
+ @Override
+ @NonNull
+ public Void invoke(@NonNull final SQLiteConnection _connection) {
+ __insertAdapterOfUser.insert(_connection, user);
+ __insertAdapterOfBook.insert(_connection, book);
+ return null;
+ }
+ });
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
index 275e55d..a9c2362 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
@@ -12,7 +12,6 @@
import androidx.room.util.DBUtil;
import androidx.room.util.SQLiteStatementUtil;
import androidx.room.util.StringUtil;
-import androidx.sqlite.SQLiteConnection;
import androidx.sqlite.SQLiteStatement;
import androidx.sqlite.db.SupportSQLiteQuery;
import com.google.common.util.concurrent.ListenableFuture;
@@ -28,640 +27,595 @@
import java.util.List;
import java.util.concurrent.Callable;
import javax.annotation.processing.Generated;
-import kotlin.jvm.functions.Function1;
@Generated("androidx.room.RoomProcessor")
@SuppressWarnings({"unchecked", "deprecation", "removal"})
public final class ComplexDao_Impl extends ComplexDao {
- private final RoomDatabase __db;
+ private final RoomDatabase __db;
- public ComplexDao_Impl(final ComplexDatabase __db) {
- super(__db);
- this.__db = __db;
- }
+ public ComplexDao_Impl(final ComplexDatabase __db) {
+ super(__db);
+ this.__db = __db;
+ }
- @Override
- public boolean transactionMethod(final int i, final String s, final long l) {
- return DBUtil.performBlocking(__db, false, true, (_connection) -> {
- return ComplexDao_Impl.super.transactionMethod(i, s, l);
- });
- }
+ @Override
+ public boolean transactionMethod(final int i, final String s, final long l) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ return ComplexDao_Impl.super.transactionMethod(i, s, l);
+ });
+ }
- @Override
- public List<ComplexDao.FullName> fullNames(final int id) {
- final String _sql = "SELECT name || lastName as fullName, uid as id FROM user where uid = ?";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<ComplexDao.FullName>>() {
- @Override
- @NonNull
- public List<ComplexDao.FullName> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, id);
- final int _cursorIndexOfFullName = 0;
- final int _cursorIndexOfId = 1;
- final List<ComplexDao.FullName> _result = new ArrayList<ComplexDao.FullName>();
- while (_stmt.step()) {
- final ComplexDao.FullName _item;
- _item = new ComplexDao.FullName();
- _item.fullName = _stmt.getText(_cursorIndexOfFullName);
- _item.id = (int) (_stmt.getLong(_cursorIndexOfId));
- _result.add(_item);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public User getById(final int id) {
- final String _sql = "SELECT * FROM user where uid = ?";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, User>() {
- @Override
- @NonNull
- public User invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, id);
- final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
- final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
- final User _result;
- if (_stmt.step()) {
- _result = new User();
- _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
- _result.name = _stmt.getText(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
- _result.setLastName(_tmpLastName);
- _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public User findByName(final String name, final String lastName) {
- final String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, User>() {
- @Override
- @NonNull
- public User invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindText(_argIndex, name);
- _argIndex = 2;
- _stmt.bindText(_argIndex, lastName);
- final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
- final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
- final User _result;
- if (_stmt.step()) {
- _result = new User();
- _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
- _result.name = _stmt.getText(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
- _result.setLastName(_tmpLastName);
- _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public List<User> loadAllByIds(final int... ids) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT * FROM user where uid IN (");
- final int _inputSize = ids == null ? 1 : ids.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<User>>() {
- @Override
- @NonNull
- public List<User> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (ids == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item : ids) {
- _stmt.bindLong(_argIndex, _item);
- _argIndex++;
- }
- }
- final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
- final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
- final List<User> _result = new ArrayList<User>();
- while (_stmt.step()) {
- final User _item_1;
- _item_1 = new User();
- _item_1.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
- _item_1.name = _stmt.getText(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
- _item_1.setLastName(_tmpLastName);
- _item_1.age = (int) (_stmt.getLong(_cursorIndexOfAge));
- _result.add(_item_1);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- int getAge(final int id) {
- final String _sql = "SELECT ageColumn FROM user where uid = ?";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, id);
- final int _result;
- if (_stmt.step()) {
- _result = (int) (_stmt.getLong(0));
- } else {
- _result = 0;
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public int[] getAllAges(final int... ids) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
- final int _inputSize = ids == null ? 1 : ids.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, int[]>() {
- @Override
- @NonNull
- public int[] invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (ids == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item : ids) {
- _stmt.bindLong(_argIndex, _item);
- _argIndex++;
- }
- }
- final List<Integer> _listResult = new ArrayList<Integer>();
- while (_stmt.step()) {
- final Integer _item_1;
- _item_1 = (int) (_stmt.getLong(0));
- _listResult.add(_item_1);
- }
- final int[] _tmpArrayResult = new int[_listResult.size()];
- int _index = 0;
- for (int _listItem : _listResult) {
- _tmpArrayResult[_index] = _listItem;
- _index++;
- }
- final int[] _result = _tmpArrayResult;
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public List<Integer> getAllAgesAsList(final List<Integer> ids) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
- final int _inputSize = ids == null ? 1 : ids.size();
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Integer>>() {
- @Override
- @NonNull
- public List<Integer> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (ids == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (Integer _item : ids) {
- if (_item == null) {
- _stmt.bindNull(_argIndex);
- } else {
- _stmt.bindLong(_argIndex, _item);
- }
- _argIndex++;
- }
- }
- final List<Integer> _result = new ArrayList<Integer>();
- while (_stmt.step()) {
- final Integer _item_1;
- if (_stmt.isNull(0)) {
- _item_1 = null;
- } else {
- _item_1 = (int) (_stmt.getLong(0));
- }
- _result.add(_item_1);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public List<Integer> getAllAgesAsList(final List<Integer> ids1, final int[] ids2,
- final int... ids3) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
- final int _inputSize = ids1 == null ? 1 : ids1.size();
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(") OR uid IN (");
- final int _inputSize_1 = ids2 == null ? 1 : ids2.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize_1);
- _stringBuilder.append(") OR uid IN (");
- final int _inputSize_2 = ids3 == null ? 1 : ids3.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize_2);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Integer>>() {
- @Override
- @NonNull
- public List<Integer> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (ids1 == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (Integer _item : ids1) {
- if (_item == null) {
- _stmt.bindNull(_argIndex);
- } else {
- _stmt.bindLong(_argIndex, _item);
- }
- _argIndex++;
- }
- }
- _argIndex = 1 + _inputSize;
- if (ids2 == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item_1 : ids2) {
- _stmt.bindLong(_argIndex, _item_1);
- _argIndex++;
- }
- }
- _argIndex = 1 + _inputSize + _inputSize_1;
- if (ids3 == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item_2 : ids3) {
- _stmt.bindLong(_argIndex, _item_2);
- _argIndex++;
- }
- }
- final List<Integer> _result = new ArrayList<Integer>();
- while (_stmt.step()) {
- final Integer _item_3;
- if (_stmt.isNull(0)) {
- _item_3 = null;
- } else {
- _item_3 = (int) (_stmt.getLong(0));
- }
- _result.add(_item_3);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public LiveData<User> getByIdLive(final int id) {
- final String _sql = "SELECT * FROM user where uid = ?";
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
+ @Override
+ public List<ComplexDao.FullName> fullNames(final int id) {
+ final String _sql = "SELECT name || lastName as fullName, uid as id FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
int _argIndex = 1;
- _statement.bindLong(_argIndex, id);
- return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<User>() {
- @Override
- @Nullable
- public User call() throws Exception {
- final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
- try {
- final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
- final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
- final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
- final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
- final User _result;
- if (_cursor.moveToFirst()) {
- _result = new User();
- _result.uid = _cursor.getInt(_cursorIndexOfUid);
- _result.name = _cursor.getString(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
- _result.setLastName(_tmpLastName);
- _result.age = _cursor.getInt(_cursorIndexOfAge);
- } else {
- _result = null;
- }
- return _result;
- } finally {
- _cursor.close();
- }
- }
+ _stmt.bindLong(_argIndex, id);
+ final int _cursorIndexOfFullName = 0;
+ final int _cursorIndexOfId = 1;
+ final List<ComplexDao.FullName> _result = new ArrayList<ComplexDao.FullName>();
+ while (_stmt.step()) {
+ final ComplexDao.FullName _item;
+ _item = new ComplexDao.FullName();
+ _item.fullName = _stmt.getText(_cursorIndexOfFullName);
+ _item.id = (int) (_stmt.getLong(_cursorIndexOfId));
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @Override
- protected void finalize() {
- _statement.release();
- }
- });
- }
+ @Override
+ public User getById(final int id) {
+ final String _sql = "SELECT * FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, id);
+ final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
+ final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
+ final User _result;
+ if (_stmt.step()) {
+ _result = new User();
+ _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ _result.name = _stmt.getText(_cursorIndexOfName);
+ final String _tmpLastName;
+ _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
+ _result.setLastName(_tmpLastName);
+ _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @Override
- public LiveData<List<User>> loadUsersByIdsLive(final int... ids) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("SELECT * FROM user where uid IN (");
- final int _inputSize = ids == null ? 1 : ids.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- final int _argCount = 0 + _inputSize;
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
+ @Override
+ public User findByName(final String name, final String lastName) {
+ final String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindText(_argIndex, name);
+ _argIndex = 2;
+ _stmt.bindText(_argIndex, lastName);
+ final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
+ final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
+ final User _result;
+ if (_stmt.step()) {
+ _result = new User();
+ _result.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ _result.name = _stmt.getText(_cursorIndexOfName);
+ final String _tmpLastName;
+ _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
+ _result.setLastName(_tmpLastName);
+ _result.age = (int) (_stmt.getLong(_cursorIndexOfAge));
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public List<User> loadAllByIds(final int... ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT * FROM user where uid IN (");
+ final int _inputSize = ids == null ? 1 : ids.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
int _argIndex = 1;
if (ids == null) {
- _statement.bindNull(_argIndex);
+ _stmt.bindNull(_argIndex);
} else {
- for (int _item : ids) {
- _statement.bindLong(_argIndex, _item);
- _argIndex++;
- }
+ for (int _item : ids) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
}
- return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<List<User>>() {
- @Override
- @Nullable
- public List<User> call() throws Exception {
- final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
- try {
- final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
- final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
- final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
- final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
- final List<User> _result = new ArrayList<User>();
- while (_cursor.moveToNext()) {
- final User _item_1;
- _item_1 = new User();
- _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
- _item_1.name = _cursor.getString(_cursorIndexOfName);
- final String _tmpLastName;
- _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
- _item_1.setLastName(_tmpLastName);
- _item_1.age = _cursor.getInt(_cursorIndexOfAge);
- _result.add(_item_1);
- }
- return _result;
- } finally {
- _cursor.close();
- }
- }
+ final int _cursorIndexOfUid = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "uid");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfLastName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "lastName");
+ final int _cursorIndexOfAge = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "ageColumn");
+ final List<User> _result = new ArrayList<User>();
+ while (_stmt.step()) {
+ final User _item_1;
+ _item_1 = new User();
+ _item_1.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ _item_1.name = _stmt.getText(_cursorIndexOfName);
+ final String _tmpLastName;
+ _tmpLastName = _stmt.getText(_cursorIndexOfLastName);
+ _item_1.setLastName(_tmpLastName);
+ _item_1.age = (int) (_stmt.getLong(_cursorIndexOfAge));
+ _result.add(_item_1);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @Override
- protected void finalize() {
- _statement.release();
- }
- });
- }
+ @Override
+ int getAge(final int id) {
+ final String _sql = "SELECT ageColumn FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, id);
+ final int _result;
+ if (_stmt.step()) {
+ _result = (int) (_stmt.getLong(0));
+ } else {
+ _result = 0;
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @Override
- public List<Child1> getChild1List() {
- final String _sql = "SELECT * FROM Child1";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Child1>>() {
- @Override
- @NonNull
- public List<Child1> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
- final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
- final List<Child1> _result = new ArrayList<Child1>();
- while (_stmt.step()) {
- final Child1 _item;
- final int _tmpId;
- _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
- final String _tmpName;
- _tmpName = _stmt.getText(_cursorIndexOfName);
- final Info _tmpInfo;
- if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
- _tmpInfo = new Info();
- _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
- _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
- } else {
- _tmpInfo = null;
- }
- _item = new Child1(_tmpId,_tmpName,_tmpInfo);
- _result.add(_item);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
+ @Override
+ public int[] getAllAges(final int... ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+ final int _inputSize = ids == null ? 1 : ids.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ final List<Integer> _listResult = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_1;
+ _item_1 = (int) (_stmt.getLong(0));
+ _listResult.add(_item_1);
+ }
+ final int[] _tmpArrayResult = new int[_listResult.size()];
+ int _index = 0;
+ for (int _listItem : _listResult) {
+ _tmpArrayResult[_index] = _listItem;
+ _index++;
+ }
+ final int[] _result = _tmpArrayResult;
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @Override
- public List<Child2> getChild2List() {
- final String _sql = "SELECT * FROM Child2";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<Child2>>() {
- @Override
- @NonNull
- public List<Child2> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
- final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
- final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
- final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
- final List<Child2> _result = new ArrayList<Child2>();
- while (_stmt.step()) {
- final Child2 _item;
- final int _tmpId;
- _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
- final String _tmpName;
- _tmpName = _stmt.getText(_cursorIndexOfName);
- final Info _tmpInfo;
- if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
- _tmpInfo = new Info();
- _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
- _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
- } else {
- _tmpInfo = null;
- }
- _item = new Child2(_tmpId,_tmpName,_tmpInfo);
- _result.add(_item);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public ListenableFuture<List<Child1>> getChild1ListListenableFuture() {
- final String _sql = "SELECT * FROM Child1";
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
- final CancellationSignal _cancellationSignal = new CancellationSignal();
- return GuavaRoom.createListenableFuture(__db, false, new Callable<List<Child1>>() {
- @Override
- public List<Child1> call() throws Exception {
- final Cursor _cursor = DBUtil.query(__db, _statement, false, _cancellationSignal);
- try {
- final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
- final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
- final int _cursorIndexOfSerial = CursorUtil.getColumnIndexOrThrow(_cursor, "serial");
- final int _cursorIndexOfCode = CursorUtil.getColumnIndexOrThrow(_cursor, "code");
- final List<Child1> _result = new ArrayList<Child1>();
- while (_cursor.moveToNext()) {
- final Child1 _item;
- final int _tmpId;
- _tmpId = _cursor.getInt(_cursorIndexOfId);
- final String _tmpName;
- _tmpName = _cursor.getString(_cursorIndexOfName);
- final Info _tmpInfo;
- if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
- _tmpInfo = new Info();
- _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
- _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
- } else {
- _tmpInfo = null;
- }
- _item = new Child1(_tmpId,_tmpName,_tmpInfo);
- _result.add(_item);
- }
- return _result;
- } finally {
- _cursor.close();
- }
- }
- }, _statement, true, _cancellationSignal);
- }
-
- @Override
- public List<UserSummary> getUserNames() {
- final String _sql = "SELECT `uid`, `name` FROM (SELECT * FROM User)";
- return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, List<UserSummary>>() {
- @Override
- @NonNull
- public List<UserSummary> invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- final int _cursorIndexOfUid = 0;
- final int _cursorIndexOfName = 1;
- final List<UserSummary> _result = new ArrayList<UserSummary>();
- while (_stmt.step()) {
- final UserSummary _item;
- _item = new UserSummary();
- _item.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
- _item.name = _stmt.getText(_cursorIndexOfName);
- _result.add(_item);
- }
- return _result;
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @Override
- public User getUserViaRawQuery(final SupportSQLiteQuery rawQuery) {
- __db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, rawQuery, false, null);
- try {
- final User _result;
- if (_cursor.moveToFirst()) {
- _result = __entityCursorConverter_fooBarUser(_cursor);
+ @Override
+ public List<Integer> getAllAgesAsList(final List<Integer> ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+ final int _inputSize = ids == null ? 1 : ids.size();
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (Integer _item : ids) {
+ if (_item == null) {
+ _stmt.bindNull(_argIndex);
} else {
- _result = null;
+ _stmt.bindLong(_argIndex, _item);
}
- return _result;
- } finally {
- _cursor.close();
+ _argIndex++;
+ }
}
- }
+ final List<Integer> _result = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_1;
+ if (_stmt.isNull(0)) {
+ _item_1 = null;
+ } else {
+ _item_1 = (int) (_stmt.getLong(0));
+ }
+ _result.add(_item_1);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
+ @Override
+ public List<Integer> getAllAgesAsList(final List<Integer> ids1, final int[] ids2,
+ final int... ids3) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
+ final int _inputSize = ids1 == null ? 1 : ids1.size();
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(") OR uid IN (");
+ final int _inputSize_1 = ids2 == null ? 1 : ids2.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize_1);
+ _stringBuilder.append(") OR uid IN (");
+ final int _inputSize_2 = ids3 == null ? 1 : ids3.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize_2);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids1 == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (Integer _item : ids1) {
+ if (_item == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ _stmt.bindLong(_argIndex, _item);
+ }
+ _argIndex++;
+ }
+ }
+ _argIndex = 1 + _inputSize;
+ if (ids2 == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item_1 : ids2) {
+ _stmt.bindLong(_argIndex, _item_1);
+ _argIndex++;
+ }
+ }
+ _argIndex = 1 + _inputSize + _inputSize_1;
+ if (ids3 == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item_2 : ids3) {
+ _stmt.bindLong(_argIndex, _item_2);
+ _argIndex++;
+ }
+ }
+ final List<Integer> _result = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_3;
+ if (_stmt.isNull(0)) {
+ _item_3 = null;
+ } else {
+ _item_3 = (int) (_stmt.getLong(0));
+ }
+ _result.add(_item_3);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- private User __entityCursorConverter_fooBarUser(@NonNull final Cursor cursor) {
- final User _entity;
- final int _cursorIndexOfUid = CursorUtil.getColumnIndex(cursor, "uid");
- final int _cursorIndexOfName = CursorUtil.getColumnIndex(cursor, "name");
- final int _cursorIndexOfLastName = CursorUtil.getColumnIndex(cursor, "lastName");
- final int _cursorIndexOfAge = CursorUtil.getColumnIndex(cursor, "ageColumn");
- _entity = new User();
- if (_cursorIndexOfUid != -1) {
- _entity.uid = cursor.getInt(_cursorIndexOfUid);
- }
- if (_cursorIndexOfName != -1) {
- _entity.name = cursor.getString(_cursorIndexOfName);
- }
- if (_cursorIndexOfLastName != -1) {
+ @Override
+ public LiveData<User> getByIdLive(final int id) {
+ final String _sql = "SELECT * FROM user where uid = ?";
+ final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
+ int _argIndex = 1;
+ _statement.bindLong(_argIndex, id);
+ return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<User>() {
+ @Override
+ @Nullable
+ public User call() throws Exception {
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
+ try {
+ final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
+ final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
+ final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
+ final User _result;
+ if (_cursor.moveToFirst()) {
+ _result = new User();
+ _result.uid = _cursor.getInt(_cursorIndexOfUid);
+ _result.name = _cursor.getString(_cursorIndexOfName);
final String _tmpLastName;
- _tmpLastName = cursor.getString(_cursorIndexOfLastName);
- _entity.setLastName(_tmpLastName);
+ _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+ _result.setLastName(_tmpLastName);
+ _result.age = _cursor.getInt(_cursorIndexOfAge);
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _cursor.close();
}
- if (_cursorIndexOfAge != -1) {
- _entity.age = cursor.getInt(_cursorIndexOfAge);
- }
- return _entity;
+ }
+
+ @Override
+ protected void finalize() {
+ _statement.release();
+ }
+ });
+ }
+
+ @Override
+ public LiveData<List<User>> loadUsersByIdsLive(final int... ids) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("SELECT * FROM user where uid IN (");
+ final int _inputSize = ids == null ? 1 : ids.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ final int _argCount = 0 + _inputSize;
+ final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
+ int _argIndex = 1;
+ if (ids == null) {
+ _statement.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _statement.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
}
-}
\ No newline at end of file
+ return __db.getInvalidationTracker().createLiveData(new String[] {"user"}, false, new Callable<List<User>>() {
+ @Override
+ @Nullable
+ public List<User> call() throws Exception {
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
+ try {
+ final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
+ final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
+ final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
+ final List<User> _result = new ArrayList<User>();
+ while (_cursor.moveToNext()) {
+ final User _item_1;
+ _item_1 = new User();
+ _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
+ _item_1.name = _cursor.getString(_cursorIndexOfName);
+ final String _tmpLastName;
+ _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
+ _item_1.setLastName(_tmpLastName);
+ _item_1.age = _cursor.getInt(_cursorIndexOfAge);
+ _result.add(_item_1);
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ _statement.release();
+ }
+ });
+ }
+
+ @Override
+ public List<Child1> getChild1List() {
+ final String _sql = "SELECT * FROM Child1";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
+ final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
+ final List<Child1> _result = new ArrayList<Child1>();
+ while (_stmt.step()) {
+ final Child1 _item;
+ final int _tmpId;
+ _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
+ final String _tmpName;
+ _tmpName = _stmt.getText(_cursorIndexOfName);
+ final Info _tmpInfo;
+ if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
+ _tmpInfo = new Info();
+ _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
+ _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
+ } else {
+ _tmpInfo = null;
+ }
+ _item = new Child1(_tmpId,_tmpName,_tmpInfo);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public List<Child2> getChild2List() {
+ final String _sql = "SELECT * FROM Child2";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ final int _cursorIndexOfId = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "id");
+ final int _cursorIndexOfName = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "name");
+ final int _cursorIndexOfSerial = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "serial");
+ final int _cursorIndexOfCode = SQLiteStatementUtil.getColumnIndexOrThrow(_stmt, "code");
+ final List<Child2> _result = new ArrayList<Child2>();
+ while (_stmt.step()) {
+ final Child2 _item;
+ final int _tmpId;
+ _tmpId = (int) (_stmt.getLong(_cursorIndexOfId));
+ final String _tmpName;
+ _tmpName = _stmt.getText(_cursorIndexOfName);
+ final Info _tmpInfo;
+ if (!(_stmt.isNull(_cursorIndexOfSerial) && _stmt.isNull(_cursorIndexOfCode))) {
+ _tmpInfo = new Info();
+ _tmpInfo.serial = (int) (_stmt.getLong(_cursorIndexOfSerial));
+ _tmpInfo.code = _stmt.getText(_cursorIndexOfCode);
+ } else {
+ _tmpInfo = null;
+ }
+ _item = new Child2(_tmpId,_tmpName,_tmpInfo);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public ListenableFuture<List<Child1>> getChild1ListListenableFuture() {
+ final String _sql = "SELECT * FROM Child1";
+ final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
+ final CancellationSignal _cancellationSignal = new CancellationSignal();
+ return GuavaRoom.createListenableFuture(__db, false, new Callable<List<Child1>>() {
+ @Override
+ public List<Child1> call() throws Exception {
+ final Cursor _cursor = DBUtil.query(__db, _statement, false, _cancellationSignal);
+ try {
+ final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
+ final int _cursorIndexOfSerial = CursorUtil.getColumnIndexOrThrow(_cursor, "serial");
+ final int _cursorIndexOfCode = CursorUtil.getColumnIndexOrThrow(_cursor, "code");
+ final List<Child1> _result = new ArrayList<Child1>();
+ while (_cursor.moveToNext()) {
+ final Child1 _item;
+ final int _tmpId;
+ _tmpId = _cursor.getInt(_cursorIndexOfId);
+ final String _tmpName;
+ _tmpName = _cursor.getString(_cursorIndexOfName);
+ final Info _tmpInfo;
+ if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
+ _tmpInfo = new Info();
+ _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
+ _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
+ } else {
+ _tmpInfo = null;
+ }
+ _item = new Child1(_tmpId,_tmpName,_tmpInfo);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+ }, _statement, true, _cancellationSignal);
+ }
+
+ @Override
+ public List<UserSummary> getUserNames() {
+ final String _sql = "SELECT `uid`, `name` FROM (SELECT * FROM User)";
+ return DBUtil.performBlocking(__db, true, false, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ final int _cursorIndexOfUid = 0;
+ final int _cursorIndexOfName = 1;
+ final List<UserSummary> _result = new ArrayList<UserSummary>();
+ while (_stmt.step()) {
+ final UserSummary _item;
+ _item = new UserSummary();
+ _item.uid = (int) (_stmt.getLong(_cursorIndexOfUid));
+ _item.name = _stmt.getText(_cursorIndexOfName);
+ _result.add(_item);
+ }
+ return _result;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public User getUserViaRawQuery(final SupportSQLiteQuery rawQuery) {
+ __db.assertNotSuspendingTransaction();
+ final Cursor _cursor = DBUtil.query(__db, rawQuery, false, null);
+ try {
+ final User _result;
+ if (_cursor.moveToFirst()) {
+ _result = __entityCursorConverter_fooBarUser(_cursor);
+ } else {
+ _result = null;
+ }
+ return _result;
+ } finally {
+ _cursor.close();
+ }
+ }
+
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+
+ private User __entityCursorConverter_fooBarUser(@NonNull final Cursor cursor) {
+ final User _entity;
+ final int _cursorIndexOfUid = CursorUtil.getColumnIndex(cursor, "uid");
+ final int _cursorIndexOfName = CursorUtil.getColumnIndex(cursor, "name");
+ final int _cursorIndexOfLastName = CursorUtil.getColumnIndex(cursor, "lastName");
+ final int _cursorIndexOfAge = CursorUtil.getColumnIndex(cursor, "ageColumn");
+ _entity = new User();
+ if (_cursorIndexOfUid != -1) {
+ _entity.uid = cursor.getInt(_cursorIndexOfUid);
+ }
+ if (_cursorIndexOfName != -1) {
+ _entity.name = cursor.getString(_cursorIndexOfName);
+ }
+ if (_cursorIndexOfLastName != -1) {
+ final String _tmpLastName;
+ _tmpLastName = cursor.getString(_cursorIndexOfLastName);
+ _entity.setLastName(_tmpLastName);
+ }
+ if (_cursorIndexOfAge != -1) {
+ _entity.age = cursor.getInt(_cursorIndexOfAge);
+ }
+ return _entity;
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
index 6228fe0..4d0654d 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
@@ -8,7 +8,6 @@
import androidx.room.util.DBUtil;
import androidx.room.util.SQLiteConnectionUtil;
import androidx.room.util.StringUtil;
-import androidx.sqlite.SQLiteConnection;
import androidx.sqlite.SQLiteStatement;
import androidx.sqlite.db.SupportSQLiteStatement;
import io.reactivex.Completable;
@@ -26,386 +25,337 @@
import java.util.List;
import java.util.concurrent.Callable;
import javax.annotation.processing.Generated;
-import kotlin.jvm.functions.Function1;
@Generated("androidx.room.RoomProcessor")
@SuppressWarnings({"unchecked", "deprecation", "removal"})
public final class DeletionDao_Impl implements DeletionDao {
- private final RoomDatabase __db;
+ private final RoomDatabase __db;
- private final EntityDeleteOrUpdateAdapter<User> __deleteAdapterOfUser;
+ private final EntityDeleteOrUpdateAdapter<User> __deleteAdapterOfUser;
- private final EntityDeletionOrUpdateAdapter<User> __deleteCompatAdapterOfUser;
+ private final EntityDeletionOrUpdateAdapter<User> __deleteCompatAdapterOfUser;
- private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __deleteAdapterOfMultiPKeyEntity;
+ private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __deleteAdapterOfMultiPKeyEntity;
- private final EntityDeleteOrUpdateAdapter<Book> __deleteAdapterOfBook;
+ private final EntityDeleteOrUpdateAdapter<Book> __deleteAdapterOfBook;
- public DeletionDao_Impl(@NonNull final RoomDatabase __db) {
- this.__db = __db;
- this.__deleteAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "DELETE FROM `User` WHERE `uid` = ?";
- }
+ public DeletionDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__deleteAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `User` WHERE `uid` = ?";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- }
- };
- this.__deleteCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
- @Override
- @NonNull
- protected String createQuery() {
- return "DELETE FROM `User` WHERE `uid` = ?";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ }
+ };
+ this.__deleteCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `User` WHERE `uid` = ?";
+ }
- @Override
- protected void bind(@NonNull final SupportSQLiteStatement statement,
- @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- }
- };
- this.__deleteAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
- }
+ @Override
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
+ @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ }
+ };
+ this.__deleteAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement,
- @NonNull final MultiPKeyEntity entity) {
- statement.bindText(1, entity.name);
- statement.bindText(2, entity.lastName);
- }
- };
- this.__deleteAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "DELETE FROM `Book` WHERE `bookId` = ?";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement,
+ @NonNull final MultiPKeyEntity entity) {
+ statement.bindText(1, entity.name);
+ statement.bindText(2, entity.lastName);
+ }
+ };
+ this.__deleteAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "DELETE FROM `Book` WHERE `bookId` = ?";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
- statement.bindLong(1, entity.bookId);
- }
- };
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ }
+ };
+ }
- @Override
- public void deleteUser(final User user) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __deleteAdapterOfUser.handle(_connection, user);
- return null;
- }
- });
- }
+ @Override
+ public void deleteUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __deleteAdapterOfUser.handle(_connection, user);
+ return null;
+ });
+ }
- @Override
- public void deleteUsers(final User user1, final List<User> others) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __deleteAdapterOfUser.handle(_connection, user1);
- __deleteAdapterOfUser.handleMultiple(_connection, others);
- return null;
- }
- });
- }
+ @Override
+ public void deleteUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __deleteAdapterOfUser.handle(_connection, user1);
+ __deleteAdapterOfUser.handleMultiple(_connection, others);
+ return null;
+ });
+ }
- @Override
- public void deleteArrayOfUsers(final User[] users) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __deleteAdapterOfUser.handleMultiple(_connection, users);
- return null;
- }
- });
- }
+ @Override
+ public void deleteArrayOfUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __deleteAdapterOfUser.handleMultiple(_connection, users);
+ return null;
+ });
+ }
- @Override
- public Integer deleteUserAndReturnCountObject(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfUser.handle(_connection, user);
- return _result;
- }
- });
- }
+ @Override
+ public Integer deleteUserAndReturnCountObject(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handle(_connection, user);
+ return _result;
+ });
+ }
- @Override
- public int deleteUserAndReturnCount(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfUser.handle(_connection, user);
- return _result;
- }
- });
- }
+ @Override
+ public int deleteUserAndReturnCount(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handle(_connection, user);
+ return _result;
+ });
+ }
- @Override
- public int deleteUserAndReturnCount(final User user1, final List<User> others) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfUser.handle(_connection, user1);
- _result += __deleteAdapterOfUser.handleMultiple(_connection, others);
- return _result;
- }
- });
- }
+ @Override
+ public int deleteUserAndReturnCount(final User user1, final List<User> others) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handle(_connection, user1);
+ _result += __deleteAdapterOfUser.handleMultiple(_connection, others);
+ return _result;
+ });
+ }
- @Override
- public int deleteUserAndReturnCount(final User[] users) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfUser.handleMultiple(_connection, users);
- return _result;
- }
- });
- }
+ @Override
+ public int deleteUserAndReturnCount(final User[] users) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfUser.handleMultiple(_connection, users);
+ return _result;
+ });
+ }
- @Override
- public Completable deleteUserCompletable(final User user) {
- return Completable.fromCallable(new Callable<Void>() {
- @Override
- @Nullable
- public Void call() throws Exception {
- __db.beginTransaction();
- try {
- __deleteCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return null;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Completable deleteUserCompletable(final User user) {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ __db.beginTransaction();
+ try {
+ __deleteCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Single<Integer> deleteUserSingle(final User user) {
- return Single.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total += __deleteCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Single<Integer> deleteUserSingle(final User user) {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __deleteCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Maybe<Integer> deleteUserMaybe(final User user) {
- return Maybe.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total += __deleteCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Maybe<Integer> deleteUserMaybe(final User user) {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __deleteCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public int multiPKey(final MultiPKeyEntity entity) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __deleteAdapterOfMultiPKeyEntity.handle(_connection, entity);
- return _result;
- }
- });
- }
+ @Override
+ public int multiPKey(final MultiPKeyEntity entity) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __deleteAdapterOfMultiPKeyEntity.handle(_connection, entity);
+ return _result;
+ });
+ }
- @Override
- public void deleteUserAndBook(final User user, final Book book) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __deleteAdapterOfUser.handle(_connection, user);
- __deleteAdapterOfBook.handle(_connection, book);
- return null;
- }
- });
- }
+ @Override
+ public void deleteUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __deleteAdapterOfUser.handle(_connection, user);
+ __deleteAdapterOfBook.handle(_connection, book);
+ return null;
+ });
+ }
- @Override
- public int deleteByUid(final int uid) {
+ @Override
+ public int deleteByUid(final int uid) {
+ final String _sql = "DELETE FROM user where uid = ?";
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ _stmt.step();
+ return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
+
+ @Override
+ public Completable deleteByUidCompletable(final int uid) {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
final String _sql = "DELETE FROM user where uid = ?";
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, uid);
- _stmt.step();
- return SQLiteConnectionUtil.getTotalChangedRows(_connection);
- } finally {
- _stmt.close();
- }
- }
- });
- }
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ __db.beginTransaction();
+ try {
+ _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Completable deleteByUidCompletable(final int uid) {
- return Completable.fromCallable(new Callable<Void>() {
- @Override
- @Nullable
- public Void call() throws Exception {
- final String _sql = "DELETE FROM user where uid = ?";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, uid);
- __db.beginTransaction();
- try {
- _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return null;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Single<Integer> deleteByUidSingle(final int uid) {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "DELETE FROM user where uid = ?";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Single<Integer> deleteByUidSingle(final int uid) {
- return Single.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- final String _sql = "DELETE FROM user where uid = ?";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, uid);
- __db.beginTransaction();
- try {
- final Integer _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Maybe<Integer> deleteByUidMaybe(final int uid) {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "DELETE FROM user where uid = ?";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ int _argIndex = 1;
+ _stmt.bindLong(_argIndex, uid);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Maybe<Integer> deleteByUidMaybe(final int uid) {
- return Maybe.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- final String _sql = "DELETE FROM user where uid = ?";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- int _argIndex = 1;
- _stmt.bindLong(_argIndex, uid);
- __db.beginTransaction();
- try {
- final Integer _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public int deleteByUidList(final int... uid) {
+ final StringBuilder _stringBuilder = new StringBuilder();
+ _stringBuilder.append("DELETE FROM user where uid IN(");
+ final int _inputSize = uid == null ? 1 : uid.length;
+ StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
+ _stringBuilder.append(")");
+ final String _sql = _stringBuilder.toString();
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (uid == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : uid) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ _stmt.step();
+ return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @Override
- public int deleteByUidList(final int... uid) {
- final StringBuilder _stringBuilder = new StringBuilder();
- _stringBuilder.append("DELETE FROM user where uid IN(");
- final int _inputSize = uid == null ? 1 : uid.length;
- StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
- _stringBuilder.append(")");
- final String _sql = _stringBuilder.toString();
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- if (uid == null) {
- _stmt.bindNull(_argIndex);
- } else {
- for (int _item : uid) {
- _stmt.bindLong(_argIndex, _item);
- _argIndex++;
- }
- }
- _stmt.step();
- return SQLiteConnectionUtil.getTotalChangedRows(_connection);
- } finally {
- _stmt.close();
- }
- }
- });
- }
+ @Override
+ public int deleteEverything() {
+ final String _sql = "DELETE FROM user";
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ _stmt.step();
+ return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @Override
- public int deleteEverything() {
- final String _sql = "DELETE FROM user";
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- _stmt.step();
- return SQLiteConnectionUtil.getTotalChangedRows(_connection);
- } finally {
- _stmt.close();
- }
- }
- });
- }
-
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
-}
\ No newline at end of file
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
index afbf5da..49a5436 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
@@ -6,7 +6,6 @@
import androidx.room.EntityDeletionOrUpdateAdapter;
import androidx.room.RoomDatabase;
import androidx.room.util.DBUtil;
-import androidx.sqlite.SQLiteConnection;
import androidx.sqlite.SQLiteStatement;
import androidx.sqlite.db.SupportSQLiteStatement;
import io.reactivex.Completable;
@@ -23,400 +22,342 @@
import java.util.List;
import java.util.concurrent.Callable;
import javax.annotation.processing.Generated;
-import kotlin.Unit;
-import kotlin.jvm.functions.Function1;
@Generated("androidx.room.RoomProcessor")
@SuppressWarnings({"unchecked", "deprecation", "removal"})
public final class UpdateDao_Impl implements UpdateDao {
- private final RoomDatabase __db;
+ private final RoomDatabase __db;
- private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser;
+ private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser;
- private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser_1;
+ private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser_1;
- private final EntityDeletionOrUpdateAdapter<User> __updateCompatAdapterOfUser;
+ private final EntityDeletionOrUpdateAdapter<User> __updateCompatAdapterOfUser;
- private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __updateAdapterOfMultiPKeyEntity;
+ private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __updateAdapterOfMultiPKeyEntity;
- private final EntityDeleteOrUpdateAdapter<Book> __updateAdapterOfBook;
+ private final EntityDeleteOrUpdateAdapter<Book> __updateAdapterOfBook;
- public UpdateDao_Impl(@NonNull final RoomDatabase __db) {
- this.__db = __db;
- this.__updateAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
- }
+ public UpdateDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__updateAdapterOfUser = new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- statement.bindText(2, entity.name);
- statement.bindText(3, entity.getLastName());
- statement.bindLong(4, entity.age);
- statement.bindLong(5, entity.uid);
- }
- };
- this.__updateAdapterOfUser_1 = new EntityDeleteOrUpdateAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ statement.bindText(2, entity.name);
+ statement.bindText(3, entity.getLastName());
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ };
+ this.__updateAdapterOfUser_1 = new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- statement.bindText(2, entity.name);
- statement.bindText(3, entity.getLastName());
- statement.bindLong(4, entity.age);
- statement.bindLong(5, entity.uid);
- }
- };
- this.__updateCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ statement.bindText(2, entity.name);
+ statement.bindText(3, entity.getLastName());
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ };
+ this.__updateCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
- @Override
- protected void bind(@NonNull final SupportSQLiteStatement statement,
- @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- statement.bindString(2, entity.name);
- statement.bindString(3, entity.getLastName());
- statement.bindLong(4, entity.age);
- statement.bindLong(5, entity.uid);
- }
- };
- this.__updateAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
- }
+ @Override
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
+ @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ statement.bindString(2, entity.name);
+ statement.bindString(3, entity.getLastName());
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ };
+ this.__updateAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement,
- @NonNull final MultiPKeyEntity entity) {
- statement.bindText(1, entity.name);
- statement.bindText(2, entity.lastName);
- statement.bindText(3, entity.name);
- statement.bindText(4, entity.lastName);
- }
- };
- this.__updateAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement,
+ @NonNull final MultiPKeyEntity entity) {
+ statement.bindText(1, entity.name);
+ statement.bindText(2, entity.lastName);
+ statement.bindText(3, entity.name);
+ statement.bindText(4, entity.lastName);
+ }
+ };
+ this.__updateAdapterOfBook = new EntityDeleteOrUpdateAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
- statement.bindLong(1, entity.bookId);
- statement.bindLong(2, entity.uid);
- statement.bindLong(3, entity.bookId);
- }
- };
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ statement.bindLong(3, entity.bookId);
+ }
+ };
+ }
- @Override
- public void updateUser(final User user) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser.handle(_connection, user);
- return null;
- }
- });
- }
+ @Override
+ public void updateUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser.handle(_connection, user);
+ return null;
+ });
+ }
- @Override
- public void updateUsers(final User user1, final List<User> others) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser.handle(_connection, user1);
- __updateAdapterOfUser.handleMultiple(_connection, others);
- return null;
- }
- });
- }
+ @Override
+ public void updateUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser.handle(_connection, user1);
+ __updateAdapterOfUser.handleMultiple(_connection, others);
+ return null;
+ });
+ }
- @Override
- public void updateArrayOfUsers(final User[] users) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser.handleMultiple(_connection, users);
- return null;
- }
- });
- }
+ @Override
+ public void updateArrayOfUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser.handleMultiple(_connection, users);
+ return null;
+ });
+ }
- @Override
- public void updateTwoUsers(final User userOne, final User userTwo) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser_1.handle(_connection, userOne);
- __updateAdapterOfUser_1.handle(_connection, userTwo);
- return null;
- }
- });
- }
+ @Override
+ public void updateTwoUsers(final User userOne, final User userTwo) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser_1.handle(_connection, userOne);
+ __updateAdapterOfUser_1.handle(_connection, userTwo);
+ return null;
+ });
+ }
- @Override
- public int updateUserAndReturnCount(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfUser.handle(_connection, user);
- return _result;
- }
- });
- }
+ @Override
+ public int updateUserAndReturnCount(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handle(_connection, user);
+ return _result;
+ });
+ }
- @Override
- public int updateUserAndReturnCount(final User user1, final List<User> others) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfUser.handle(_connection, user1);
- _result += __updateAdapterOfUser.handleMultiple(_connection, others);
- return _result;
- }
- });
- }
+ @Override
+ public int updateUserAndReturnCount(final User user1, final List<User> others) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handle(_connection, user1);
+ _result += __updateAdapterOfUser.handleMultiple(_connection, others);
+ return _result;
+ });
+ }
- @Override
- public int updateUserAndReturnCount(final User[] users) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfUser.handleMultiple(_connection, users);
- return _result;
- }
- });
- }
+ @Override
+ public int updateUserAndReturnCount(final User[] users) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handleMultiple(_connection, users);
+ return _result;
+ });
+ }
- @Override
- public Integer updateUserAndReturnCountObject(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfUser.handle(_connection, user);
- return _result;
- }
- });
- }
+ @Override
+ public Integer updateUserAndReturnCountObject(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfUser.handle(_connection, user);
+ return _result;
+ });
+ }
- @Override
- public Completable updateUserAndReturnCountCompletable(final User user) {
- return Completable.fromCallable(new Callable<Void>() {
- @Override
- @Nullable
- public Void call() throws Exception {
- __db.beginTransaction();
- try {
- __updateCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return null;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Completable updateUserAndReturnCountCompletable(final User user) {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
+ __db.beginTransaction();
+ try {
+ __updateCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Single<Integer> updateUserAndReturnCountSingle(final User user) {
- return Single.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total += __updateCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Single<Integer> updateUserAndReturnCountSingle(final User user) {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __updateCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Maybe<Integer> updateUserAndReturnCountMaybe(final User user) {
- return Maybe.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- int _total = 0;
- __db.beginTransaction();
- try {
- _total += __updateCompatAdapterOfUser.handle(user);
- __db.setTransactionSuccessful();
- return _total;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Maybe<Integer> updateUserAndReturnCountMaybe(final User user) {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ int _total = 0;
+ __db.beginTransaction();
+ try {
+ _total += __updateCompatAdapterOfUser.handle(user);
+ __db.setTransactionSuccessful();
+ return _total;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public int multiPKey(final MultiPKeyEntity entity) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Integer>() {
- @Override
- @NonNull
- public Integer invoke(@NonNull final SQLiteConnection _connection) {
- int _result = 0;
- _result += __updateAdapterOfMultiPKeyEntity.handle(_connection, entity);
- return _result;
- }
- });
- }
+ @Override
+ public int multiPKey(final MultiPKeyEntity entity) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ int _result = 0;
+ _result += __updateAdapterOfMultiPKeyEntity.handle(_connection, entity);
+ return _result;
+ });
+ }
- @Override
- public void updateUserAndBook(final User user, final Book book) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __updateAdapterOfUser.handle(_connection, user);
- __updateAdapterOfBook.handle(_connection, book);
- return null;
- }
- });
- }
+ @Override
+ public void updateUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __updateAdapterOfUser.handle(_connection, user);
+ __updateAdapterOfBook.handle(_connection, book);
+ return null;
+ });
+ }
- @Override
- public void updateAndAge(final User user) {
- DBUtil.performBlocking(__db, false, true, (_connection) -> {
- UpdateDao.super.updateAndAge(user);
- return Unit.INSTANCE;
- });
- }
+ @Override
+ public void ageUserByUid(final String uid) {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = ?";
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ _stmt.bindText(_argIndex, uid);
+ _stmt.step();
+ return null;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @Override
- public void ageUserByUid(final String uid) {
- final String _sql = "UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = ?";
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- int _argIndex = 1;
- _stmt.bindText(_argIndex, uid);
- _stmt.step();
- return null;
- } finally {
- _stmt.close();
- }
- }
- });
- }
+ @Override
+ public void ageUserAll() {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ _stmt.step();
+ return null;
+ } finally {
+ _stmt.close();
+ }
+ });
+ }
- @Override
- public void ageUserAll() {
+ @Override
+ public Completable ageUserAllCompletable() {
+ return Completable.fromCallable(new Callable<Void>() {
+ @Override
+ @Nullable
+ public Void call() throws Exception {
final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- final SQLiteStatement _stmt = _connection.prepare(_sql);
- try {
- _stmt.step();
- return null;
- } finally {
- _stmt.close();
- }
- }
- });
- }
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ __db.beginTransaction();
+ try {
+ _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return null;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Completable ageUserAllCompletable() {
- return Completable.fromCallable(new Callable<Void>() {
- @Override
- @Nullable
- public Void call() throws Exception {
- final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- __db.beginTransaction();
- try {
- _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return null;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Single<Integer> ageUserAllSingle() {
+ return Single.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Single<Integer> ageUserAllSingle() {
- return Single.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- __db.beginTransaction();
- try {
- final Integer _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
+ @Override
+ public Maybe<Integer> ageUserAllMaybe() {
+ return Maybe.fromCallable(new Callable<Integer>() {
+ @Override
+ @Nullable
+ public Integer call() throws Exception {
+ final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+ final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+ __db.beginTransaction();
+ try {
+ final Integer _result = _stmt.executeUpdateDelete();
+ __db.setTransactionSuccessful();
+ return _result;
+ } finally {
+ __db.endTransaction();
+ }
+ }
+ });
+ }
- @Override
- public Maybe<Integer> ageUserAllMaybe() {
- return Maybe.fromCallable(new Callable<Integer>() {
- @Override
- @Nullable
- public Integer call() throws Exception {
- final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
- final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
- __db.beginTransaction();
- try {
- final Integer _result = _stmt.executeUpdateDelete();
- __db.setTransactionSuccessful();
- return _result;
- } finally {
- __db.endTransaction();
- }
- }
- });
- }
-
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
-}
\ No newline at end of file
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java
index 0858d17..a3f22bb 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java
@@ -6,175 +6,143 @@
import androidx.room.EntityUpsertAdapter;
import androidx.room.RoomDatabase;
import androidx.room.util.DBUtil;
-import androidx.sqlite.SQLiteConnection;
import androidx.sqlite.SQLiteStatement;
import java.lang.Class;
-import java.lang.Long;
import java.lang.Override;
import java.lang.String;
import java.lang.SuppressWarnings;
-import java.lang.Void;
import java.util.Collections;
import java.util.List;
import javax.annotation.processing.Generated;
-import kotlin.jvm.functions.Function1;
@Generated("androidx.room.RoomProcessor")
@SuppressWarnings({"unchecked", "deprecation", "removal"})
public final class UpsertDao_Impl implements UpsertDao {
- private final RoomDatabase __db;
+ private final RoomDatabase __db;
- private final EntityUpsertAdapter<User> __upsertAdapterOfUser;
+ private final EntityUpsertAdapter<User> __upsertAdapterOfUser;
- private final EntityUpsertAdapter<Book> __upsertAdapterOfBook;
+ private final EntityUpsertAdapter<Book> __upsertAdapterOfBook;
- public UpsertDao_Impl(@NonNull final RoomDatabase __db) {
- this.__db = __db;
- this.__upsertAdapterOfUser = new EntityUpsertAdapter<User>(new EntityInsertAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
- }
+ public UpsertDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__upsertAdapterOfUser = new EntityUpsertAdapter<User>(new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- statement.bindText(2, entity.name);
- statement.bindText(3, entity.getLastName());
- statement.bindLong(4, entity.age);
- }
- }, new EntityDeleteOrUpdateAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ statement.bindText(2, entity.name);
+ statement.bindText(3, entity.getLastName());
+ statement.bindLong(4, entity.age);
+ }
+ }, new EntityDeleteOrUpdateAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- statement.bindText(2, entity.name);
- statement.bindText(3, entity.getLastName());
- statement.bindLong(4, entity.age);
- statement.bindLong(5, entity.uid);
- }
- });
- this.__upsertAdapterOfBook = new EntityUpsertAdapter<Book>(new EntityInsertAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ statement.bindText(2, entity.name);
+ statement.bindText(3, entity.getLastName());
+ statement.bindLong(4, entity.age);
+ statement.bindLong(5, entity.uid);
+ }
+ });
+ this.__upsertAdapterOfBook = new EntityUpsertAdapter<Book>(new EntityInsertAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
- statement.bindLong(1, entity.bookId);
- statement.bindLong(2, entity.uid);
- }
- }, new EntityDeleteOrUpdateAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ }
+ }, new EntityDeleteOrUpdateAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
- statement.bindLong(1, entity.bookId);
- statement.bindLong(2, entity.uid);
- statement.bindLong(3, entity.bookId);
- }
- });
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ statement.bindLong(3, entity.bookId);
+ }
+ });
+ }
- @Override
- public void upsertUser(final User user) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, user);
- return null;
- }
- });
- }
+ @Override
+ public void upsertUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, user);
+ return null;
+ });
+ }
- @Override
- public void upsertUsers(final User user1, final List<User> others) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, user1);
- __upsertAdapterOfUser.upsert(_connection, others);
- return null;
- }
- });
- }
+ @Override
+ public void upsertUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, user1);
+ __upsertAdapterOfUser.upsert(_connection, others);
+ return null;
+ });
+ }
- @Override
- public void upsertUsers(final User[] users) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, users);
- return null;
- }
- });
- }
+ @Override
+ public void upsertUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, users);
+ return null;
+ });
+ }
- @Override
- public void upsertTwoUsers(final User userOne, final User userTwo) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, userOne);
- __upsertAdapterOfUser.upsert(_connection, userTwo);
- return null;
- }
- });
- }
+ @Override
+ public void upsertTwoUsers(final User userOne, final User userTwo) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, userOne);
+ __upsertAdapterOfUser.upsert(_connection, userTwo);
+ return null;
+ });
+ }
- @Override
- public void upsertUserAndBook(final User user, final Book book) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __upsertAdapterOfUser.upsert(_connection, user);
- __upsertAdapterOfBook.upsert(_connection, book);
- return null;
- }
- });
- }
+ @Override
+ public void upsertUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __upsertAdapterOfUser.upsert(_connection, user);
+ __upsertAdapterOfBook.upsert(_connection, book);
+ return null;
+ });
+ }
- @Override
- public long upsertAndReturnId(final User user) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Long>() {
- @Override
- @NonNull
- public Long invoke(@NonNull final SQLiteConnection _connection) {
- return __upsertAdapterOfUser.upsertAndReturnId(_connection, user);
- }
- });
- }
+ @Override
+ public long upsertAndReturnId(final User user) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ return __upsertAdapterOfUser.upsertAndReturnId(_connection, user);
+ });
+ }
- @Override
- public long[] upsertAndReturnIdsArray(final User[] users) {
- return DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, long[]>() {
- @Override
- @NonNull
- public long[] invoke(@NonNull final SQLiteConnection _connection) {
- return __upsertAdapterOfUser.upsertAndReturnIdsArray(_connection, users);
- }
- });
- }
+ @Override
+ public long[] upsertAndReturnIdsArray(final User[] users) {
+ return DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ return __upsertAdapterOfUser.upsertAndReturnIdsArray(_connection, users);
+ });
+ }
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
-}
\ No newline at end of file
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java
index 80b3ed3..2a4d60d 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java
@@ -4,158 +4,135 @@
import androidx.room.EntityInsertAdapter;
import androidx.room.RoomDatabase;
import androidx.room.util.DBUtil;
-import androidx.sqlite.SQLiteConnection;
import androidx.sqlite.SQLiteStatement;
import java.lang.Class;
import java.lang.Override;
import java.lang.String;
import java.lang.SuppressWarnings;
-import java.lang.Void;
import java.util.Collections;
import java.util.List;
import javax.annotation.processing.Generated;
-import kotlin.jvm.functions.Function1;
@Generated("androidx.room.RoomProcessor")
@SuppressWarnings({"unchecked", "deprecation", "removal"})
public final class WriterDao_Impl implements WriterDao {
- private final RoomDatabase __db;
+ private final RoomDatabase __db;
- private final EntityInsertAdapter<User> __insertAdapterOfUser;
+ private final EntityInsertAdapter<User> __insertAdapterOfUser;
- private final EntityInsertAdapter<User> __insertAdapterOfUser_1;
+ private final EntityInsertAdapter<User> __insertAdapterOfUser_1;
- private final EntityInsertAdapter<User> __insertAdapterOfUser_2;
+ private final EntityInsertAdapter<User> __insertAdapterOfUser_2;
- private final EntityInsertAdapter<Book> __insertAdapterOfBook;
+ private final EntityInsertAdapter<Book> __insertAdapterOfBook;
- public WriterDao_Impl(@NonNull final RoomDatabase __db) {
- this.__db = __db;
- this.__insertAdapterOfUser = new EntityInsertAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT OR ABORT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
- }
+ public WriterDao_Impl(@NonNull final RoomDatabase __db) {
+ this.__db = __db;
+ this.__insertAdapterOfUser = new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT OR ABORT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- statement.bindText(2, entity.name);
- statement.bindText(3, entity.getLastName());
- statement.bindLong(4, entity.age);
- }
- };
- this.__insertAdapterOfUser_1 = new EntityInsertAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT OR REPLACE INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ statement.bindText(2, entity.name);
+ statement.bindText(3, entity.getLastName());
+ statement.bindLong(4, entity.age);
+ }
+ };
+ this.__insertAdapterOfUser_1 = new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT OR REPLACE INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- statement.bindText(2, entity.name);
- statement.bindText(3, entity.getLastName());
- statement.bindLong(4, entity.age);
- }
- };
- this.__insertAdapterOfUser_2 = new EntityInsertAdapter<User>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ statement.bindText(2, entity.name);
+ statement.bindText(3, entity.getLastName());
+ statement.bindLong(4, entity.age);
+ }
+ };
+ this.__insertAdapterOfUser_2 = new EntityInsertAdapter<User>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
- statement.bindLong(1, entity.uid);
- statement.bindText(2, entity.name);
- statement.bindText(3, entity.getLastName());
- statement.bindLong(4, entity.age);
- }
- };
- this.__insertAdapterOfBook = new EntityInsertAdapter<Book>() {
- @Override
- @NonNull
- protected String createQuery() {
- return "INSERT OR ABORT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final User entity) {
+ statement.bindLong(1, entity.uid);
+ statement.bindText(2, entity.name);
+ statement.bindText(3, entity.getLastName());
+ statement.bindLong(4, entity.age);
+ }
+ };
+ this.__insertAdapterOfBook = new EntityInsertAdapter<Book>() {
+ @Override
+ @NonNull
+ protected String createQuery() {
+ return "INSERT OR ABORT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
+ }
- @Override
- protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
- statement.bindLong(1, entity.bookId);
- statement.bindLong(2, entity.uid);
- }
- };
- }
+ @Override
+ protected void bind(@NonNull final SQLiteStatement statement, @NonNull final Book entity) {
+ statement.bindLong(1, entity.bookId);
+ statement.bindLong(2, entity.uid);
+ }
+ };
+ }
- @Override
- public void insertUser(final User user) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser.insert(_connection, user);
- return null;
- }
- });
- }
+ @Override
+ public void insertUser(final User user) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser.insert(_connection, user);
+ return null;
+ });
+ }
- @Override
- public void insertUsers(final User user1, final List<User> others) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser.insert(_connection, user1);
- __insertAdapterOfUser.insert(_connection, others);
- return null;
- }
- });
- }
+ @Override
+ public void insertUsers(final User user1, final List<User> others) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser.insert(_connection, user1);
+ __insertAdapterOfUser.insert(_connection, others);
+ return null;
+ });
+ }
- @Override
- public void insertUsers(final User[] users) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser_1.insert(_connection, users);
- return null;
- }
- });
- }
+ @Override
+ public void insertUsers(final User[] users) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser_1.insert(_connection, users);
+ return null;
+ });
+ }
- @Override
- public void insertTwoUsers(final User userOne, final User userTwo) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser_2.insert(_connection, userOne);
- __insertAdapterOfUser_2.insert(_connection, userTwo);
- return null;
- }
- });
- }
+ @Override
+ public void insertTwoUsers(final User userOne, final User userTwo) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser_2.insert(_connection, userOne);
+ __insertAdapterOfUser_2.insert(_connection, userTwo);
+ return null;
+ });
+ }
- @Override
- public void insertUserAndBook(final User user, final Book book) {
- DBUtil.performBlocking(__db, false, true, new Function1<SQLiteConnection, Void>() {
- @Override
- @NonNull
- public Void invoke(@NonNull final SQLiteConnection _connection) {
- __insertAdapterOfUser.insert(_connection, user);
- __insertAdapterOfBook.insert(_connection, book);
- return null;
- }
- });
- }
+ @Override
+ public void insertUserAndBook(final User user, final Book book) {
+ DBUtil.performBlocking(__db, false, true, (_connection) -> {
+ __insertAdapterOfUser.insert(_connection, user);
+ __insertAdapterOfBook.insert(_connection, book);
+ return null;
+ });
+ }
- @NonNull
- public static List<Class<?>> getRequiredConverters() {
- return Collections.emptyList();
- }
-}
\ No newline at end of file
+ @NonNull
+ public static List<Class<?>> getRequiredConverters() {
+ return Collections.emptyList();
+ }
+}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt
index 57c2e48..c03a592 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt
@@ -1,21 +1,22 @@
-import android.database.Cursor
import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
import androidx.room.util.appendPlaceholders
import androidx.room.util.getColumnIndex
import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
-import androidx.room.util.recursiveFetchHashMap
-import java.util.ArrayList
-import java.util.HashMap
+import androidx.room.util.performBlocking
+import androidx.room.util.recursiveFetchMap
+import androidx.sqlite.SQLiteConnection
+import androidx.sqlite.SQLiteStatement
import javax.`annotation`.processing.Generated
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.Suppress
import kotlin.collections.List
+import kotlin.collections.MutableList
+import kotlin.collections.MutableMap
import kotlin.collections.Set
+import kotlin.collections.mutableListOf
+import kotlin.collections.mutableMapOf
import kotlin.reflect.KClass
import kotlin.text.StringBuilder
@@ -31,130 +32,128 @@
public override fun getSongsWithArtist(): SongWithArtist {
val _sql: String = "SELECT * FROM Song"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_cursor, "songId")
- val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_cursor, "artistKey")
- val _collectionArtist: HashMap<Long, Artist?> = HashMap<Long, Artist?>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfArtistKey)
- _collectionArtist.put(_tmpKey, null)
- }
- _cursor.moveToPosition(-1)
- __fetchRelationshipArtistAsArtist(_collectionArtist)
- val _result: SongWithArtist
- if (_cursor.moveToFirst()) {
- val _tmpSong: Song
- val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
- val _tmpArtistKey: Long
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
- _tmpSong = Song(_tmpSongId,_tmpArtistKey)
- val _tmpArtist: Artist?
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistKey)
- _tmpArtist = _collectionArtist.get(_tmpKey_1)
- if (_tmpArtist == null) {
- error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_stmt, "songId")
+ val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_stmt, "artistKey")
+ val _collectionArtist: MutableMap<Long, Artist?> = mutableMapOf()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ _collectionArtist.put(_tmpKey, null)
}
- _result = SongWithArtist(_tmpSong,_tmpArtist)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
+ _stmt.reset()
+ __fetchRelationshipArtistAsArtist(_connection, _collectionArtist)
+ val _result: SongWithArtist
+ if (_stmt.step()) {
+ val _tmpSong: Song
+ val _tmpSongId: Long
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
+ val _tmpArtistKey: Long
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ _tmpSong = Song(_tmpSongId,_tmpArtistKey)
+ val _tmpArtist: Artist?
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfArtistKey)
+ _tmpArtist = _collectionArtist.get(_tmpKey_1)
+ if (_tmpArtist == null) {
+ error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
+ }
+ _result = SongWithArtist(_tmpSong,_tmpArtist)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
public override fun getArtistAndSongs(): ArtistAndSongs {
val _sql: String = "SELECT * FROM Artist"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_cursor, "artistId")
- val _collectionSongs: HashMap<Long, ArrayList<Song>> = HashMap<Long, ArrayList<Song>>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfArtistId)
- if (!_collectionSongs.containsKey(_tmpKey)) {
- _collectionSongs.put(_tmpKey, ArrayList<Song>())
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_stmt, "artistId")
+ val _collectionSongs: MutableMap<Long, MutableList<Song>> = mutableMapOf()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfArtistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
}
+ _stmt.reset()
+ __fetchRelationshipSongAsSong(_connection, _collectionSongs)
+ val _result: ArtistAndSongs
+ if (_stmt.step()) {
+ val _tmpArtist: Artist
+ val _tmpArtistId: Long
+ _tmpArtistId = _stmt.getLong(_cursorIndexOfArtistId)
+ _tmpArtist = Artist(_tmpArtistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfArtistId)
+ _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
+ _result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- _cursor.moveToPosition(-1)
- __fetchRelationshipSongAsSong(_collectionSongs)
- val _result: ArtistAndSongs
- if (_cursor.moveToFirst()) {
- val _tmpArtist: Artist
- val _tmpArtistId: Long
- _tmpArtistId = _cursor.getLong(_cursorIndexOfArtistId)
- _tmpArtist = Artist(_tmpArtistId)
- val _tmpSongsCollection: ArrayList<Song>
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistId)
- _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
- _result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
- }
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
public override fun getPlaylistAndSongs(): PlaylistAndSongs {
val _sql: String = "SELECT * FROM Playlist"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfPlaylistId: Int = getColumnIndexOrThrow(_cursor, "playlistId")
- val _collectionSongs: HashMap<Long, ArrayList<Song>> = HashMap<Long, ArrayList<Song>>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfPlaylistId)
- if (!_collectionSongs.containsKey(_tmpKey)) {
- _collectionSongs.put(_tmpKey, ArrayList<Song>())
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfPlaylistId: Int = getColumnIndexOrThrow(_stmt, "playlistId")
+ val _collectionSongs: MutableMap<Long, MutableList<Song>> = mutableMapOf()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfPlaylistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
}
+ _stmt.reset()
+ __fetchRelationshipSongAsSong_1(_connection, _collectionSongs)
+ val _result: PlaylistAndSongs
+ if (_stmt.step()) {
+ val _tmpPlaylist: Playlist
+ val _tmpPlaylistId: Long
+ _tmpPlaylistId = _stmt.getLong(_cursorIndexOfPlaylistId)
+ _tmpPlaylist = Playlist(_tmpPlaylistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfPlaylistId)
+ _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
+ _result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- _cursor.moveToPosition(-1)
- __fetchRelationshipSongAsSong_1(_collectionSongs)
- val _result: PlaylistAndSongs
- if (_cursor.moveToFirst()) {
- val _tmpPlaylist: Playlist
- val _tmpPlaylistId: Long
- _tmpPlaylistId = _cursor.getLong(_cursorIndexOfPlaylistId)
- _tmpPlaylist = Playlist(_tmpPlaylistId)
- val _tmpSongsCollection: ArrayList<Song>
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfPlaylistId)
- _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
- _result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
- }
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
- private fun __fetchRelationshipArtistAsArtist(_map: HashMap<Long, Artist?>) {
+ private fun __fetchRelationshipArtistAsArtist(_connection: SQLiteConnection,
+ _map: MutableMap<Long, Artist?>) {
val __mapKeySet: Set<Long> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchHashMap(_map, false) {
- __fetchRelationshipArtistAsArtist(it)
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, false) { _tmpMap ->
+ __fetchRelationshipArtistAsArtist(_connection, _tmpMap)
}
return
}
@@ -164,44 +163,43 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: Long in __mapKeySet) {
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
- val _itemKeyIndex: Int = getColumnIndex(_cursor, "artistId")
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistId")
if (_itemKeyIndex == -1) {
return
}
val _cursorIndexOfArtistId: Int = 0
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
if (_map.containsKey(_tmpKey)) {
val _item_1: Artist
val _tmpArtistId: Long
- _tmpArtistId = _cursor.getLong(_cursorIndexOfArtistId)
+ _tmpArtistId = _stmt.getLong(_cursorIndexOfArtistId)
_item_1 = Artist(_tmpArtistId)
_map.put(_tmpKey, _item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
- private fun __fetchRelationshipSongAsSong(_map: HashMap<Long, ArrayList<Song>>) {
+ private fun __fetchRelationshipSongAsSong(_connection: SQLiteConnection,
+ _map: MutableMap<Long, MutableList<Song>>) {
val __mapKeySet: Set<Long> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchHashMap(_map, true) {
- __fetchRelationshipSongAsSong(it)
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong(_connection, _tmpMap)
}
return
}
@@ -211,48 +209,47 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: Long in __mapKeySet) {
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
- val _itemKeyIndex: Int = getColumnIndex(_cursor, "artistKey")
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistKey")
if (_itemKeyIndex == -1) {
return
}
val _cursorIndexOfSongId: Int = 0
val _cursorIndexOfArtistKey: Int = 1
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
- val _tmpRelation: ArrayList<Song>? = _map.get(_tmpKey)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
if (_tmpRelation != null) {
val _item_1: Song
val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
val _tmpArtistKey: Long
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
_item_1 = Song(_tmpSongId,_tmpArtistKey)
_tmpRelation.add(_item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
- private fun __fetchRelationshipSongAsSong_1(_map: HashMap<Long, ArrayList<Song>>) {
+ private fun __fetchRelationshipSongAsSong_1(_connection: SQLiteConnection,
+ _map: MutableMap<Long, MutableList<Song>>) {
val __mapKeySet: Set<Long> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchHashMap(_map, true) {
- __fetchRelationshipSongAsSong_1(it)
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong_1(_connection, _tmpMap)
}
return
}
@@ -262,14 +259,12 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: Long in __mapKeySet) {
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
// _junction.playlistKey
val _itemKeyIndex: Int = 2
@@ -278,22 +273,22 @@
}
val _cursorIndexOfSongId: Int = 0
val _cursorIndexOfArtistKey: Int = 1
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
- val _tmpRelation: ArrayList<Song>? = _map.get(_tmpKey)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
if (_tmpRelation != null) {
val _item_1: Song
val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
val _tmpArtistKey: Long
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
_item_1 = Song(_tmpSongId,_tmpArtistKey)
_tmpRelation.add(_item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt
index 0c5dcc4..6a397c8 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt
@@ -1,21 +1,21 @@
-import android.database.Cursor
import androidx.collection.ArrayMap
import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
import androidx.room.util.appendPlaceholders
import androidx.room.util.getColumnIndex
import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.room.util.performBlocking
import androidx.room.util.recursiveFetchArrayMap
-import java.util.ArrayList
+import androidx.sqlite.SQLiteConnection
+import androidx.sqlite.SQLiteStatement
import javax.`annotation`.processing.Generated
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.Suppress
import kotlin.collections.List
+import kotlin.collections.MutableList
import kotlin.collections.Set
+import kotlin.collections.mutableListOf
import kotlin.reflect.KClass
import kotlin.text.StringBuilder
@@ -31,130 +31,130 @@
public override fun getSongsWithArtist(): SongWithArtist {
val _sql: String = "SELECT * FROM Song"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_cursor, "songId")
- val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_cursor, "artistKey")
- val _collectionArtist: ArrayMap<Long, Artist?> = ArrayMap<Long, Artist?>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfArtistKey)
- _collectionArtist.put(_tmpKey, null)
- }
- _cursor.moveToPosition(-1)
- __fetchRelationshipArtistAsArtist(_collectionArtist)
- val _result: SongWithArtist
- if (_cursor.moveToFirst()) {
- val _tmpSong: Song
- val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
- val _tmpArtistKey: Long
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
- _tmpSong = Song(_tmpSongId,_tmpArtistKey)
- val _tmpArtist: Artist?
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistKey)
- _tmpArtist = _collectionArtist.get(_tmpKey_1)
- if (_tmpArtist == null) {
- error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_stmt, "songId")
+ val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_stmt, "artistKey")
+ val _collectionArtist: ArrayMap<Long, Artist?> = ArrayMap<Long, Artist?>()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ _collectionArtist.put(_tmpKey, null)
}
- _result = SongWithArtist(_tmpSong,_tmpArtist)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
+ _stmt.reset()
+ __fetchRelationshipArtistAsArtist(_connection, _collectionArtist)
+ val _result: SongWithArtist
+ if (_stmt.step()) {
+ val _tmpSong: Song
+ val _tmpSongId: Long
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
+ val _tmpArtistKey: Long
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ _tmpSong = Song(_tmpSongId,_tmpArtistKey)
+ val _tmpArtist: Artist?
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfArtistKey)
+ _tmpArtist = _collectionArtist.get(_tmpKey_1)
+ if (_tmpArtist == null) {
+ error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
+ }
+ _result = SongWithArtist(_tmpSong,_tmpArtist)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
public override fun getArtistAndSongs(): ArtistAndSongs {
val _sql: String = "SELECT * FROM Artist"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_cursor, "artistId")
- val _collectionSongs: ArrayMap<Long, ArrayList<Song>> = ArrayMap<Long, ArrayList<Song>>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfArtistId)
- if (!_collectionSongs.containsKey(_tmpKey)) {
- _collectionSongs.put(_tmpKey, ArrayList<Song>())
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_stmt, "artistId")
+ val _collectionSongs: ArrayMap<Long, MutableList<Song>> =
+ ArrayMap<Long, MutableList<Song>>()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfArtistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
}
+ _stmt.reset()
+ __fetchRelationshipSongAsSong(_connection, _collectionSongs)
+ val _result: ArtistAndSongs
+ if (_stmt.step()) {
+ val _tmpArtist: Artist
+ val _tmpArtistId: Long
+ _tmpArtistId = _stmt.getLong(_cursorIndexOfArtistId)
+ _tmpArtist = Artist(_tmpArtistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfArtistId)
+ _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
+ _result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- _cursor.moveToPosition(-1)
- __fetchRelationshipSongAsSong(_collectionSongs)
- val _result: ArtistAndSongs
- if (_cursor.moveToFirst()) {
- val _tmpArtist: Artist
- val _tmpArtistId: Long
- _tmpArtistId = _cursor.getLong(_cursorIndexOfArtistId)
- _tmpArtist = Artist(_tmpArtistId)
- val _tmpSongsCollection: ArrayList<Song>
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistId)
- _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
- _result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
- }
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
public override fun getPlaylistAndSongs(): PlaylistAndSongs {
val _sql: String = "SELECT * FROM Playlist"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfPlaylistId: Int = getColumnIndexOrThrow(_cursor, "playlistId")
- val _collectionSongs: ArrayMap<Long, ArrayList<Song>> = ArrayMap<Long, ArrayList<Song>>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfPlaylistId)
- if (!_collectionSongs.containsKey(_tmpKey)) {
- _collectionSongs.put(_tmpKey, ArrayList<Song>())
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfPlaylistId: Int = getColumnIndexOrThrow(_stmt, "playlistId")
+ val _collectionSongs: ArrayMap<Long, MutableList<Song>> =
+ ArrayMap<Long, MutableList<Song>>()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfPlaylistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
}
+ _stmt.reset()
+ __fetchRelationshipSongAsSong_1(_connection, _collectionSongs)
+ val _result: PlaylistAndSongs
+ if (_stmt.step()) {
+ val _tmpPlaylist: Playlist
+ val _tmpPlaylistId: Long
+ _tmpPlaylistId = _stmt.getLong(_cursorIndexOfPlaylistId)
+ _tmpPlaylist = Playlist(_tmpPlaylistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfPlaylistId)
+ _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
+ _result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- _cursor.moveToPosition(-1)
- __fetchRelationshipSongAsSong_1(_collectionSongs)
- val _result: PlaylistAndSongs
- if (_cursor.moveToFirst()) {
- val _tmpPlaylist: Playlist
- val _tmpPlaylistId: Long
- _tmpPlaylistId = _cursor.getLong(_cursorIndexOfPlaylistId)
- _tmpPlaylist = Playlist(_tmpPlaylistId)
- val _tmpSongsCollection: ArrayList<Song>
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfPlaylistId)
- _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
- _result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
- }
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
- private fun __fetchRelationshipArtistAsArtist(_map: ArrayMap<Long, Artist?>) {
+ private fun __fetchRelationshipArtistAsArtist(_connection: SQLiteConnection,
+ _map: ArrayMap<Long, Artist?>) {
val __mapKeySet: Set<Long> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchArrayMap(_map, false) {
- __fetchRelationshipArtistAsArtist(it)
+ if (_map.size > 999) {
+ recursiveFetchArrayMap(_map, false) { _tmpMap ->
+ __fetchRelationshipArtistAsArtist(_connection, _tmpMap)
}
return
}
@@ -164,44 +164,43 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: Long in __mapKeySet) {
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
- val _itemKeyIndex: Int = getColumnIndex(_cursor, "artistId")
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistId")
if (_itemKeyIndex == -1) {
return
}
val _cursorIndexOfArtistId: Int = 0
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
if (_map.containsKey(_tmpKey)) {
val _item_1: Artist
val _tmpArtistId: Long
- _tmpArtistId = _cursor.getLong(_cursorIndexOfArtistId)
+ _tmpArtistId = _stmt.getLong(_cursorIndexOfArtistId)
_item_1 = Artist(_tmpArtistId)
_map.put(_tmpKey, _item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
- private fun __fetchRelationshipSongAsSong(_map: ArrayMap<Long, ArrayList<Song>>) {
+ private fun __fetchRelationshipSongAsSong(_connection: SQLiteConnection,
+ _map: ArrayMap<Long, MutableList<Song>>) {
val __mapKeySet: Set<Long> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchArrayMap(_map, true) {
- __fetchRelationshipSongAsSong(it)
+ if (_map.size > 999) {
+ recursiveFetchArrayMap(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong(_connection, _tmpMap)
}
return
}
@@ -211,48 +210,47 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: Long in __mapKeySet) {
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
- val _itemKeyIndex: Int = getColumnIndex(_cursor, "artistKey")
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistKey")
if (_itemKeyIndex == -1) {
return
}
val _cursorIndexOfSongId: Int = 0
val _cursorIndexOfArtistKey: Int = 1
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
- val _tmpRelation: ArrayList<Song>? = _map.get(_tmpKey)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
if (_tmpRelation != null) {
val _item_1: Song
val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
val _tmpArtistKey: Long
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
_item_1 = Song(_tmpSongId,_tmpArtistKey)
_tmpRelation.add(_item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
- private fun __fetchRelationshipSongAsSong_1(_map: ArrayMap<Long, ArrayList<Song>>) {
+ private fun __fetchRelationshipSongAsSong_1(_connection: SQLiteConnection,
+ _map: ArrayMap<Long, MutableList<Song>>) {
val __mapKeySet: Set<Long> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchArrayMap(_map, true) {
- __fetchRelationshipSongAsSong_1(it)
+ if (_map.size > 999) {
+ recursiveFetchArrayMap(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong_1(_connection, _tmpMap)
}
return
}
@@ -262,14 +260,12 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: Long in __mapKeySet) {
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
// _junction.playlistKey
val _itemKeyIndex: Int = 2
@@ -278,22 +274,22 @@
}
val _cursorIndexOfSongId: Int = 0
val _cursorIndexOfArtistKey: Int = 1
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
- val _tmpRelation: ArrayList<Song>? = _map.get(_tmpKey)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
if (_tmpRelation != null) {
val _item_1: Song
val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
val _tmpArtistKey: Long
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
_item_1 = Song(_tmpSongId,_tmpArtistKey)
_tmpRelation.add(_item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt
index f6480e9c..c843aef 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt
@@ -1,14 +1,12 @@
-import android.database.Cursor
import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
import androidx.room.util.appendPlaceholders
import androidx.room.util.getColumnIndex
import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
-import androidx.room.util.recursiveFetchHashMap
+import androidx.room.util.performBlocking
+import androidx.room.util.recursiveFetchMap
+import androidx.sqlite.SQLiteConnection
+import androidx.sqlite.SQLiteStatement
import java.nio.ByteBuffer
-import java.util.HashMap
import javax.`annotation`.processing.Generated
import kotlin.ByteArray
import kotlin.Int
@@ -16,7 +14,9 @@
import kotlin.String
import kotlin.Suppress
import kotlin.collections.List
+import kotlin.collections.MutableMap
import kotlin.collections.Set
+import kotlin.collections.mutableMapOf
import kotlin.reflect.KClass
import kotlin.text.StringBuilder
@@ -32,54 +32,54 @@
public override fun getSongsWithArtist(): SongWithArtist {
val _sql: String = "SELECT * FROM Song"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_cursor, "songId")
- val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_cursor, "artistKey")
- val _collectionArtist: HashMap<ByteBuffer, Artist?> = HashMap<ByteBuffer, Artist?>()
- while (_cursor.moveToNext()) {
- val _tmpKey: ByteBuffer
- _tmpKey = ByteBuffer.wrap(_cursor.getBlob(_cursorIndexOfArtistKey))
- _collectionArtist.put(_tmpKey, null)
- }
- _cursor.moveToPosition(-1)
- __fetchRelationshipArtistAsArtist(_collectionArtist)
- val _result: SongWithArtist
- if (_cursor.moveToFirst()) {
- val _tmpSong: Song
- val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
- val _tmpArtistKey: ByteArray
- _tmpArtistKey = _cursor.getBlob(_cursorIndexOfArtistKey)
- _tmpSong = Song(_tmpSongId,_tmpArtistKey)
- val _tmpArtist: Artist?
- val _tmpKey_1: ByteBuffer
- _tmpKey_1 = ByteBuffer.wrap(_cursor.getBlob(_cursorIndexOfArtistKey))
- _tmpArtist = _collectionArtist.get(_tmpKey_1)
- if (_tmpArtist == null) {
- error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_stmt, "songId")
+ val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_stmt, "artistKey")
+ val _collectionArtist: MutableMap<ByteBuffer, Artist?> = mutableMapOf()
+ while (_stmt.step()) {
+ val _tmpKey: ByteBuffer
+ _tmpKey = ByteBuffer.wrap(_stmt.getBlob(_cursorIndexOfArtistKey))
+ _collectionArtist.put(_tmpKey, null)
}
- _result = SongWithArtist(_tmpSong,_tmpArtist)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
+ _stmt.reset()
+ __fetchRelationshipArtistAsArtist(_connection, _collectionArtist)
+ val _result: SongWithArtist
+ if (_stmt.step()) {
+ val _tmpSong: Song
+ val _tmpSongId: Long
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
+ val _tmpArtistKey: ByteArray
+ _tmpArtistKey = _stmt.getBlob(_cursorIndexOfArtistKey)
+ _tmpSong = Song(_tmpSongId,_tmpArtistKey)
+ val _tmpArtist: Artist?
+ val _tmpKey_1: ByteBuffer
+ _tmpKey_1 = ByteBuffer.wrap(_stmt.getBlob(_cursorIndexOfArtistKey))
+ _tmpArtist = _collectionArtist.get(_tmpKey_1)
+ if (_tmpArtist == null) {
+ error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
+ }
+ _result = SongWithArtist(_tmpSong,_tmpArtist)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
- private fun __fetchRelationshipArtistAsArtist(_map: HashMap<ByteBuffer, Artist?>) {
+ private fun __fetchRelationshipArtistAsArtist(_connection: SQLiteConnection,
+ _map: MutableMap<ByteBuffer, Artist?>) {
val __mapKeySet: Set<ByteBuffer> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchHashMap(_map, false) {
- __fetchRelationshipArtistAsArtist(it)
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, false) { _tmpMap ->
+ __fetchRelationshipArtistAsArtist(_connection, _tmpMap)
}
return
}
@@ -89,33 +89,31 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: ByteBuffer in __mapKeySet) {
_stmt.bindBlob(_argIndex, _item.array())
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
- val _itemKeyIndex: Int = getColumnIndex(_cursor, "artistId")
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistId")
if (_itemKeyIndex == -1) {
return
}
val _cursorIndexOfArtistId: Int = 0
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: ByteBuffer
- _tmpKey = ByteBuffer.wrap(_cursor.getBlob(_itemKeyIndex))
+ _tmpKey = ByteBuffer.wrap(_stmt.getBlob(_itemKeyIndex))
if (_map.containsKey(_tmpKey)) {
val _item_1: Artist
val _tmpArtistId: ByteArray
- _tmpArtistId = _cursor.getBlob(_cursorIndexOfArtistId)
+ _tmpArtistId = _stmt.getBlob(_cursorIndexOfArtistId)
_item_1 = Artist(_tmpArtistId)
_map.put(_tmpKey, _item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt
index 2c0afeb..5096b2b 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt
@@ -1,20 +1,20 @@
-import android.database.Cursor
import androidx.collection.LongSparseArray
import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
import androidx.room.util.appendPlaceholders
import androidx.room.util.getColumnIndex
import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.room.util.performBlocking
import androidx.room.util.recursiveFetchLongSparseArray
-import java.util.ArrayList
+import androidx.sqlite.SQLiteConnection
+import androidx.sqlite.SQLiteStatement
import javax.`annotation`.processing.Generated
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.Suppress
import kotlin.collections.List
+import kotlin.collections.MutableList
+import kotlin.collections.mutableListOf
import kotlin.reflect.KClass
import kotlin.text.StringBuilder
@@ -30,129 +30,129 @@
public override fun getSongsWithArtist(): SongWithArtist {
val _sql: String = "SELECT * FROM Song"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_cursor, "songId")
- val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_cursor, "artistKey")
- val _collectionArtist: LongSparseArray<Artist?> = LongSparseArray<Artist?>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfArtistKey)
- _collectionArtist.put(_tmpKey, null)
- }
- _cursor.moveToPosition(-1)
- __fetchRelationshipArtistAsArtist(_collectionArtist)
- val _result: SongWithArtist
- if (_cursor.moveToFirst()) {
- val _tmpSong: Song
- val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
- val _tmpArtistKey: Long
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
- _tmpSong = Song(_tmpSongId,_tmpArtistKey)
- val _tmpArtist: Artist?
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistKey)
- _tmpArtist = _collectionArtist.get(_tmpKey_1)
- if (_tmpArtist == null) {
- error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_stmt, "songId")
+ val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_stmt, "artistKey")
+ val _collectionArtist: LongSparseArray<Artist?> = LongSparseArray<Artist?>()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ _collectionArtist.put(_tmpKey, null)
}
- _result = SongWithArtist(_tmpSong,_tmpArtist)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
+ _stmt.reset()
+ __fetchRelationshipArtistAsArtist(_connection, _collectionArtist)
+ val _result: SongWithArtist
+ if (_stmt.step()) {
+ val _tmpSong: Song
+ val _tmpSongId: Long
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
+ val _tmpArtistKey: Long
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ _tmpSong = Song(_tmpSongId,_tmpArtistKey)
+ val _tmpArtist: Artist?
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfArtistKey)
+ _tmpArtist = _collectionArtist.get(_tmpKey_1)
+ if (_tmpArtist == null) {
+ error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
+ }
+ _result = SongWithArtist(_tmpSong,_tmpArtist)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
public override fun getArtistAndSongs(): ArtistAndSongs {
val _sql: String = "SELECT * FROM Artist"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_cursor, "artistId")
- val _collectionSongs: LongSparseArray<ArrayList<Song>> = LongSparseArray<ArrayList<Song>>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfArtistId)
- if (!_collectionSongs.containsKey(_tmpKey)) {
- _collectionSongs.put(_tmpKey, ArrayList<Song>())
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_stmt, "artistId")
+ val _collectionSongs: LongSparseArray<MutableList<Song>> =
+ LongSparseArray<MutableList<Song>>()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfArtistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
}
+ _stmt.reset()
+ __fetchRelationshipSongAsSong(_connection, _collectionSongs)
+ val _result: ArtistAndSongs
+ if (_stmt.step()) {
+ val _tmpArtist: Artist
+ val _tmpArtistId: Long
+ _tmpArtistId = _stmt.getLong(_cursorIndexOfArtistId)
+ _tmpArtist = Artist(_tmpArtistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfArtistId)
+ _tmpSongsCollection = checkNotNull(_collectionSongs.get(_tmpKey_1))
+ _result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- _cursor.moveToPosition(-1)
- __fetchRelationshipSongAsSong(_collectionSongs)
- val _result: ArtistAndSongs
- if (_cursor.moveToFirst()) {
- val _tmpArtist: Artist
- val _tmpArtistId: Long
- _tmpArtistId = _cursor.getLong(_cursorIndexOfArtistId)
- _tmpArtist = Artist(_tmpArtistId)
- val _tmpSongsCollection: ArrayList<Song>
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistId)
- _tmpSongsCollection = checkNotNull(_collectionSongs.get(_tmpKey_1))
- _result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
- }
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
public override fun getPlaylistAndSongs(): PlaylistAndSongs {
val _sql: String = "SELECT * FROM Playlist"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfPlaylistId: Int = getColumnIndexOrThrow(_cursor, "playlistId")
- val _collectionSongs: LongSparseArray<ArrayList<Song>> = LongSparseArray<ArrayList<Song>>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfPlaylistId)
- if (!_collectionSongs.containsKey(_tmpKey)) {
- _collectionSongs.put(_tmpKey, ArrayList<Song>())
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfPlaylistId: Int = getColumnIndexOrThrow(_stmt, "playlistId")
+ val _collectionSongs: LongSparseArray<MutableList<Song>> =
+ LongSparseArray<MutableList<Song>>()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfPlaylistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
}
+ _stmt.reset()
+ __fetchRelationshipSongAsSong_1(_connection, _collectionSongs)
+ val _result: PlaylistAndSongs
+ if (_stmt.step()) {
+ val _tmpPlaylist: Playlist
+ val _tmpPlaylistId: Long
+ _tmpPlaylistId = _stmt.getLong(_cursorIndexOfPlaylistId)
+ _tmpPlaylist = Playlist(_tmpPlaylistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfPlaylistId)
+ _tmpSongsCollection = checkNotNull(_collectionSongs.get(_tmpKey_1))
+ _result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- _cursor.moveToPosition(-1)
- __fetchRelationshipSongAsSong_1(_collectionSongs)
- val _result: PlaylistAndSongs
- if (_cursor.moveToFirst()) {
- val _tmpPlaylist: Playlist
- val _tmpPlaylistId: Long
- _tmpPlaylistId = _cursor.getLong(_cursorIndexOfPlaylistId)
- _tmpPlaylist = Playlist(_tmpPlaylistId)
- val _tmpSongsCollection: ArrayList<Song>
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfPlaylistId)
- _tmpSongsCollection = checkNotNull(_collectionSongs.get(_tmpKey_1))
- _result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
- }
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
- private fun __fetchRelationshipArtistAsArtist(_map: LongSparseArray<Artist?>) {
+ private fun __fetchRelationshipArtistAsArtist(_connection: SQLiteConnection,
+ _map: LongSparseArray<Artist?>) {
if (_map.isEmpty()) {
return
}
- if (_map.size() > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchLongSparseArray(_map, false) {
- __fetchRelationshipArtistAsArtist(it)
+ if (_map.size() > 999) {
+ recursiveFetchLongSparseArray(_map, false) { _tmpMap ->
+ __fetchRelationshipArtistAsArtist(_connection, _tmpMap)
}
return
}
@@ -162,44 +162,43 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (i in 0 until _map.size()) {
val _item: Long = _map.keyAt(i)
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
- val _itemKeyIndex: Int = getColumnIndex(_cursor, "artistId")
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistId")
if (_itemKeyIndex == -1) {
return
}
val _cursorIndexOfArtistId: Int = 0
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
if (_map.containsKey(_tmpKey)) {
val _item_1: Artist
val _tmpArtistId: Long
- _tmpArtistId = _cursor.getLong(_cursorIndexOfArtistId)
+ _tmpArtistId = _stmt.getLong(_cursorIndexOfArtistId)
_item_1 = Artist(_tmpArtistId)
_map.put(_tmpKey, _item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
- private fun __fetchRelationshipSongAsSong(_map: LongSparseArray<ArrayList<Song>>) {
+ private fun __fetchRelationshipSongAsSong(_connection: SQLiteConnection,
+ _map: LongSparseArray<MutableList<Song>>) {
if (_map.isEmpty()) {
return
}
- if (_map.size() > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchLongSparseArray(_map, true) {
- __fetchRelationshipSongAsSong(it)
+ if (_map.size() > 999) {
+ recursiveFetchLongSparseArray(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong(_connection, _tmpMap)
}
return
}
@@ -209,48 +208,47 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (i in 0 until _map.size()) {
val _item: Long = _map.keyAt(i)
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
- val _itemKeyIndex: Int = getColumnIndex(_cursor, "artistKey")
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistKey")
if (_itemKeyIndex == -1) {
return
}
val _cursorIndexOfSongId: Int = 0
val _cursorIndexOfArtistKey: Int = 1
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
- val _tmpRelation: ArrayList<Song>? = _map.get(_tmpKey)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
if (_tmpRelation != null) {
val _item_1: Song
val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
val _tmpArtistKey: Long
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
_item_1 = Song(_tmpSongId,_tmpArtistKey)
_tmpRelation.add(_item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
- private fun __fetchRelationshipSongAsSong_1(_map: LongSparseArray<ArrayList<Song>>) {
+ private fun __fetchRelationshipSongAsSong_1(_connection: SQLiteConnection,
+ _map: LongSparseArray<MutableList<Song>>) {
if (_map.isEmpty()) {
return
}
- if (_map.size() > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchLongSparseArray(_map, true) {
- __fetchRelationshipSongAsSong_1(it)
+ if (_map.size() > 999) {
+ recursiveFetchLongSparseArray(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong_1(_connection, _tmpMap)
}
return
}
@@ -260,15 +258,13 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (i in 0 until _map.size()) {
val _item: Long = _map.keyAt(i)
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
// _junction.playlistKey
val _itemKeyIndex: Int = 2
@@ -277,22 +273,22 @@
}
val _cursorIndexOfSongId: Int = 0
val _cursorIndexOfArtistKey: Int = 1
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
- val _tmpRelation: ArrayList<Song>? = _map.get(_tmpKey)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
if (_tmpRelation != null) {
val _item_1: Song
val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
val _tmpArtistKey: Long
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
_item_1 = Song(_tmpSongId,_tmpArtistKey)
_tmpRelation.add(_item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt
index 37123cf2..015fc74 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt
@@ -1,21 +1,22 @@
-import android.database.Cursor
import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
import androidx.room.util.appendPlaceholders
import androidx.room.util.getColumnIndex
import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
-import androidx.room.util.recursiveFetchHashMap
-import java.util.ArrayList
-import java.util.HashMap
+import androidx.room.util.performBlocking
+import androidx.room.util.recursiveFetchMap
+import androidx.sqlite.SQLiteConnection
+import androidx.sqlite.SQLiteStatement
import javax.`annotation`.processing.Generated
import kotlin.Int
import kotlin.Long
import kotlin.String
import kotlin.Suppress
import kotlin.collections.List
+import kotlin.collections.MutableList
+import kotlin.collections.MutableMap
import kotlin.collections.Set
+import kotlin.collections.mutableListOf
+import kotlin.collections.mutableMapOf
import kotlin.reflect.KClass
import kotlin.text.StringBuilder
@@ -31,145 +32,143 @@
public override fun getSongsWithArtist(): SongWithArtist {
val _sql: String = "SELECT * FROM Song"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_cursor, "songId")
- val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_cursor, "artistKey")
- val _collectionArtist: HashMap<Long, Artist?> = HashMap<Long, Artist?>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long?
- if (_cursor.isNull(_cursorIndexOfArtistKey)) {
- _tmpKey = null
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_stmt, "songId")
+ val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_stmt, "artistKey")
+ val _collectionArtist: MutableMap<Long, Artist?> = mutableMapOf()
+ while (_stmt.step()) {
+ val _tmpKey: Long?
+ if (_stmt.isNull(_cursorIndexOfArtistKey)) {
+ _tmpKey = null
+ } else {
+ _tmpKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ }
+ if (_tmpKey != null) {
+ _collectionArtist.put(_tmpKey, null)
+ }
+ }
+ _stmt.reset()
+ __fetchRelationshipArtistAsArtist(_connection, _collectionArtist)
+ val _result: SongWithArtist
+ if (_stmt.step()) {
+ val _tmpSong: Song
+ val _tmpSongId: Long
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
+ val _tmpArtistKey: Long?
+ if (_stmt.isNull(_cursorIndexOfArtistKey)) {
+ _tmpArtistKey = null
+ } else {
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
+ }
+ _tmpSong = Song(_tmpSongId,_tmpArtistKey)
+ val _tmpArtist: Artist?
+ val _tmpKey_1: Long?
+ if (_stmt.isNull(_cursorIndexOfArtistKey)) {
+ _tmpKey_1 = null
+ } else {
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfArtistKey)
+ }
+ if (_tmpKey_1 != null) {
+ _tmpArtist = _collectionArtist.get(_tmpKey_1)
+ } else {
+ _tmpArtist = null
+ }
+ _result = SongWithArtist(_tmpSong,_tmpArtist)
} else {
- _tmpKey = _cursor.getLong(_cursorIndexOfArtistKey)
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
}
- if (_tmpKey != null) {
- _collectionArtist.put(_tmpKey, null)
- }
+ _result
+ } finally {
+ _stmt.close()
}
- _cursor.moveToPosition(-1)
- __fetchRelationshipArtistAsArtist(_collectionArtist)
- val _result: SongWithArtist
- if (_cursor.moveToFirst()) {
- val _tmpSong: Song
- val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
- val _tmpArtistKey: Long?
- if (_cursor.isNull(_cursorIndexOfArtistKey)) {
- _tmpArtistKey = null
- } else {
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
- }
- _tmpSong = Song(_tmpSongId,_tmpArtistKey)
- val _tmpArtist: Artist?
- val _tmpKey_1: Long?
- if (_cursor.isNull(_cursorIndexOfArtistKey)) {
- _tmpKey_1 = null
- } else {
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistKey)
- }
- if (_tmpKey_1 != null) {
- _tmpArtist = _collectionArtist.get(_tmpKey_1)
- } else {
- _tmpArtist = null
- }
- _result = SongWithArtist(_tmpSong,_tmpArtist)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
- }
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
public override fun getArtistAndSongs(): ArtistAndSongs {
val _sql: String = "SELECT * FROM Artist"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_cursor, "artistId")
- val _collectionSongs: HashMap<Long, ArrayList<Song>> = HashMap<Long, ArrayList<Song>>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfArtistId)
- if (!_collectionSongs.containsKey(_tmpKey)) {
- _collectionSongs.put(_tmpKey, ArrayList<Song>())
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_stmt, "artistId")
+ val _collectionSongs: MutableMap<Long, MutableList<Song>> = mutableMapOf()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfArtistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
}
+ _stmt.reset()
+ __fetchRelationshipSongAsSong(_connection, _collectionSongs)
+ val _result: ArtistAndSongs
+ if (_stmt.step()) {
+ val _tmpArtist: Artist
+ val _tmpArtistId: Long
+ _tmpArtistId = _stmt.getLong(_cursorIndexOfArtistId)
+ _tmpArtist = Artist(_tmpArtistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfArtistId)
+ _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
+ _result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- _cursor.moveToPosition(-1)
- __fetchRelationshipSongAsSong(_collectionSongs)
- val _result: ArtistAndSongs
- if (_cursor.moveToFirst()) {
- val _tmpArtist: Artist
- val _tmpArtistId: Long
- _tmpArtistId = _cursor.getLong(_cursorIndexOfArtistId)
- _tmpArtist = Artist(_tmpArtistId)
- val _tmpSongsCollection: ArrayList<Song>
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistId)
- _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
- _result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
- }
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
public override fun getPlaylistAndSongs(): PlaylistAndSongs {
val _sql: String = "SELECT * FROM Playlist"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, true, null)
- try {
- val _cursorIndexOfPlaylistId: Int = getColumnIndexOrThrow(_cursor, "playlistId")
- val _collectionSongs: HashMap<Long, ArrayList<Song>> = HashMap<Long, ArrayList<Song>>()
- while (_cursor.moveToNext()) {
- val _tmpKey: Long
- _tmpKey = _cursor.getLong(_cursorIndexOfPlaylistId)
- if (!_collectionSongs.containsKey(_tmpKey)) {
- _collectionSongs.put(_tmpKey, ArrayList<Song>())
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfPlaylistId: Int = getColumnIndexOrThrow(_stmt, "playlistId")
+ val _collectionSongs: MutableMap<Long, MutableList<Song>> = mutableMapOf()
+ while (_stmt.step()) {
+ val _tmpKey: Long
+ _tmpKey = _stmt.getLong(_cursorIndexOfPlaylistId)
+ if (!_collectionSongs.containsKey(_tmpKey)) {
+ _collectionSongs.put(_tmpKey, mutableListOf())
+ }
}
+ _stmt.reset()
+ __fetchRelationshipSongAsSong_1(_connection, _collectionSongs)
+ val _result: PlaylistAndSongs
+ if (_stmt.step()) {
+ val _tmpPlaylist: Playlist
+ val _tmpPlaylistId: Long
+ _tmpPlaylistId = _stmt.getLong(_cursorIndexOfPlaylistId)
+ _tmpPlaylist = Playlist(_tmpPlaylistId)
+ val _tmpSongsCollection: MutableList<Song>
+ val _tmpKey_1: Long
+ _tmpKey_1 = _stmt.getLong(_cursorIndexOfPlaylistId)
+ _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
+ _result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
+ } else {
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
+ }
+ _result
+ } finally {
+ _stmt.close()
}
- _cursor.moveToPosition(-1)
- __fetchRelationshipSongAsSong_1(_collectionSongs)
- val _result: PlaylistAndSongs
- if (_cursor.moveToFirst()) {
- val _tmpPlaylist: Playlist
- val _tmpPlaylistId: Long
- _tmpPlaylistId = _cursor.getLong(_cursorIndexOfPlaylistId)
- _tmpPlaylist = Playlist(_tmpPlaylistId)
- val _tmpSongsCollection: ArrayList<Song>
- val _tmpKey_1: Long
- _tmpKey_1 = _cursor.getLong(_cursorIndexOfPlaylistId)
- _tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
- _result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
- } else {
- error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
- }
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
- private fun __fetchRelationshipArtistAsArtist(_map: HashMap<Long, Artist?>) {
+ private fun __fetchRelationshipArtistAsArtist(_connection: SQLiteConnection,
+ _map: MutableMap<Long, Artist?>) {
val __mapKeySet: Set<Long> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchHashMap(_map, false) {
- __fetchRelationshipArtistAsArtist(it)
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, false) { _tmpMap ->
+ __fetchRelationshipArtistAsArtist(_connection, _tmpMap)
}
return
}
@@ -179,44 +178,43 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: Long in __mapKeySet) {
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
- val _itemKeyIndex: Int = getColumnIndex(_cursor, "artistId")
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistId")
if (_itemKeyIndex == -1) {
return
}
val _cursorIndexOfArtistId: Int = 0
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
if (_map.containsKey(_tmpKey)) {
val _item_1: Artist?
val _tmpArtistId: Long
- _tmpArtistId = _cursor.getLong(_cursorIndexOfArtistId)
+ _tmpArtistId = _stmt.getLong(_cursorIndexOfArtistId)
_item_1 = Artist(_tmpArtistId)
_map.put(_tmpKey, _item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
- private fun __fetchRelationshipSongAsSong(_map: HashMap<Long, ArrayList<Song>>) {
+ private fun __fetchRelationshipSongAsSong(_connection: SQLiteConnection,
+ _map: MutableMap<Long, MutableList<Song>>) {
val __mapKeySet: Set<Long> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchHashMap(_map, true) {
- __fetchRelationshipSongAsSong(it)
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong(_connection, _tmpMap)
}
return
}
@@ -226,39 +224,37 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: Long in __mapKeySet) {
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
- val _itemKeyIndex: Int = getColumnIndex(_cursor, "artistKey")
+ val _itemKeyIndex: Int = getColumnIndex(_stmt, "artistKey")
if (_itemKeyIndex == -1) {
return
}
val _cursorIndexOfSongId: Int = 0
val _cursorIndexOfArtistKey: Int = 1
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long?
- if (_cursor.isNull(_itemKeyIndex)) {
+ if (_stmt.isNull(_itemKeyIndex)) {
_tmpKey = null
} else {
- _tmpKey = _cursor.getLong(_itemKeyIndex)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
}
if (_tmpKey != null) {
- val _tmpRelation: ArrayList<Song>? = _map.get(_tmpKey)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
if (_tmpRelation != null) {
val _item_1: Song
val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
val _tmpArtistKey: Long?
- if (_cursor.isNull(_cursorIndexOfArtistKey)) {
+ if (_stmt.isNull(_cursorIndexOfArtistKey)) {
_tmpArtistKey = null
} else {
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
}
_item_1 = Song(_tmpSongId,_tmpArtistKey)
_tmpRelation.add(_item_1)
@@ -266,18 +262,19 @@
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
- private fun __fetchRelationshipSongAsSong_1(_map: HashMap<Long, ArrayList<Song>>) {
+ private fun __fetchRelationshipSongAsSong_1(_connection: SQLiteConnection,
+ _map: MutableMap<Long, MutableList<Song>>) {
val __mapKeySet: Set<Long> = _map.keys
if (__mapKeySet.isEmpty()) {
return
}
- if (_map.size > RoomDatabase.MAX_BIND_PARAMETER_CNT) {
- recursiveFetchHashMap(_map, true) {
- __fetchRelationshipSongAsSong_1(it)
+ if (_map.size > 999) {
+ recursiveFetchMap(_map, true) { _tmpMap ->
+ __fetchRelationshipSongAsSong_1(_connection, _tmpMap)
}
return
}
@@ -287,14 +284,12 @@
appendPlaceholders(_stringBuilder, _inputSize)
_stringBuilder.append(")")
val _sql: String = _stringBuilder.toString()
- val _argCount: Int = 0 + _inputSize
- val _stmt: RoomSQLiteQuery = acquire(_sql, _argCount)
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
var _argIndex: Int = 1
for (_item: Long in __mapKeySet) {
_stmt.bindLong(_argIndex, _item)
_argIndex++
}
- val _cursor: Cursor = query(__db, _stmt, false, null)
try {
// _junction.playlistKey
val _itemKeyIndex: Int = 2
@@ -303,26 +298,26 @@
}
val _cursorIndexOfSongId: Int = 0
val _cursorIndexOfArtistKey: Int = 1
- while (_cursor.moveToNext()) {
+ while (_stmt.step()) {
val _tmpKey: Long
- _tmpKey = _cursor.getLong(_itemKeyIndex)
- val _tmpRelation: ArrayList<Song>? = _map.get(_tmpKey)
+ _tmpKey = _stmt.getLong(_itemKeyIndex)
+ val _tmpRelation: MutableList<Song>? = _map.get(_tmpKey)
if (_tmpRelation != null) {
val _item_1: Song
val _tmpSongId: Long
- _tmpSongId = _cursor.getLong(_cursorIndexOfSongId)
+ _tmpSongId = _stmt.getLong(_cursorIndexOfSongId)
val _tmpArtistKey: Long?
- if (_cursor.isNull(_cursorIndexOfArtistKey)) {
+ if (_stmt.isNull(_cursorIndexOfArtistKey)) {
_tmpArtistKey = null
} else {
- _tmpArtistKey = _cursor.getLong(_cursorIndexOfArtistKey)
+ _tmpArtistKey = _stmt.getLong(_cursorIndexOfArtistKey)
}
_item_1 = Song(_tmpSongId,_tmpArtistKey)
_tmpRelation.add(_item_1)
}
}
} finally {
- _cursor.close()
+ _stmt.close()
}
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_abstractClass.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_abstractClass.kt
index 0a7097a..37885f5 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_abstractClass.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_abstractClass.kt
@@ -18,7 +18,7 @@
this.__db = __db
}
- public override fun baseConcrete(): Unit = performBlocking(__db, false, true) {
+ public override fun baseConcrete(): Unit = performBlocking(__db, false, true) { _ ->
super@MyDao_Impl.baseConcrete()
}
@@ -26,11 +26,11 @@
super@MyDao_Impl.baseSuspendConcrete()
}
- public override fun concrete(): Unit = performBlocking(__db, false, true) {
+ public override fun concrete(): Unit = performBlocking(__db, false, true) { _ ->
super@MyDao_Impl.concrete()
}
- internal override fun concreteInternal(): Unit = performBlocking(__db, false, true) {
+ internal override fun concreteInternal(): Unit = performBlocking(__db, false, true) { _ ->
super@MyDao_Impl.concreteInternal()
}
@@ -39,7 +39,7 @@
}
public override fun concreteWithVararg(vararg arr: Long): Unit = performBlocking(__db, false,
- true) {
+ true) { _ ->
super@MyDao_Impl.concreteWithVararg(*arr)
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_interface.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_interface.kt
index c4590e2..95deee1 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_interface.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_interface.kt
@@ -21,7 +21,7 @@
this.__db = __db
}
- public override fun baseConcrete(): Unit = performBlocking(__db, false, true) {
+ public override fun baseConcrete(): Unit = performBlocking(__db, false, true) { _ ->
super@MyDao_Impl.baseConcrete()
}
@@ -29,21 +29,21 @@
super@MyDao_Impl.baseSuspendConcrete()
}
- public override fun concrete(): Unit = performBlocking(__db, false, true) {
+ public override fun concrete(): Unit = performBlocking(__db, false, true) { _ ->
super@MyDao_Impl.concrete()
}
- public override fun concreteWithReturn(): String = performBlocking(__db, false, true) {
+ public override fun concreteWithReturn(): String = performBlocking(__db, false, true) { _ ->
super@MyDao_Impl.concreteWithReturn()
}
public override fun concreteWithParamsAndReturn(text: String, num: Long): String =
- performBlocking(__db, false, true) {
+ performBlocking(__db, false, true) { _ ->
super@MyDao_Impl.concreteWithParamsAndReturn(text, num)
}
public override fun concreteWithFunctionalParam(block: Function0<Unit>): Unit =
- performBlocking(__db, false, true) {
+ performBlocking(__db, false, true) { _ ->
super@MyDao_Impl.concreteWithFunctionalParam(block)
}
diff --git a/room/room-ktx/build.gradle b/room/room-ktx/build.gradle
index 58a0f23..9dafedb 100644
--- a/room/room-ktx/build.gradle
+++ b/room/room-ktx/build.gradle
@@ -21,8 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -49,7 +48,7 @@
androidx {
name = "Room Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Android Room Kotlin Extensions"
metalavaK2UastEnabled = true
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index 7039636..5634a6c 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -487,10 +487,11 @@
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T, C> T findAndInstantiateDatabaseImpl(Class<C> klass, optional String suffix);
}
- @RestrictTo({androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public final class RelationUtil {
- method public static <K, V> void recursiveFetchArrayMap(androidx.collection.ArrayMap<K,V> map, boolean isRelationCollection, kotlin.jvm.functions.Function1<? super androidx.collection.ArrayMap<K,V>,kotlin.Unit> fetchBlock);
- method public static <K, V> void recursiveFetchHashMap(java.util.HashMap<K,V> map, boolean isRelationCollection, kotlin.jvm.functions.Function1<? super java.util.HashMap<K,V>,kotlin.Unit> fetchBlock);
- method public static <V> void recursiveFetchLongSparseArray(androidx.collection.LongSparseArray<V> map, boolean isRelationCollection, kotlin.jvm.functions.Function1<? super androidx.collection.LongSparseArray<V>,kotlin.Unit> fetchBlock);
+ public final class RelationUtil {
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <K, V> void recursiveFetchArrayMap(androidx.collection.ArrayMap<K,V> map, boolean isRelationCollection, kotlin.jvm.functions.Function1<? super androidx.collection.ArrayMap<K,V>,kotlin.Unit> fetchBlock);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <K, V> void recursiveFetchHashMap(java.util.HashMap<K,V> map, boolean isRelationCollection, kotlin.jvm.functions.Function1<? super java.util.HashMap<K,V>,kotlin.Unit> fetchBlock);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <V> void recursiveFetchLongSparseArray(androidx.collection.LongSparseArray<V> map, boolean isRelationCollection, kotlin.jvm.functions.Function1<? super androidx.collection.LongSparseArray<V>,kotlin.Unit> fetchBlock);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <K, V> void recursiveFetchMap(java.util.Map<K,V> map, boolean isRelationCollection, kotlin.jvm.functions.Function1<? super java.util.Map<K,V>,kotlin.Unit> fetchBlock);
}
public final class SQLiteConnectionUtil {
@@ -499,6 +500,7 @@
}
public final class SQLiteStatementUtil {
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static int getColumnIndex(androidx.sqlite.SQLiteStatement stmt, String name);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static int getColumnIndexOrThrow(androidx.sqlite.SQLiteStatement stmt, String name);
}
diff --git a/room/room-runtime/build.gradle b/room/room-runtime/build.gradle
index 9b0a135..c230427 100644
--- a/room/room-runtime/build.gradle
+++ b/room/room-runtime/build.gradle
@@ -146,7 +146,7 @@
compileOnly("androidx.collection:collection:1.2.0")
compileOnly("androidx.lifecycle:lifecycle-livedata-core:2.0.0")
compileOnly("androidx.paging:paging-common:2.0.0")
- implementation(projectOrArtifact(":annotation:annotation-experimental"))
+ implementation("androidx.annotation:annotation-experimental:1.4.1")
}
}
androidUnitTest {
diff --git a/room/room-runtime/lint-baseline.xml b/room/room-runtime/lint-baseline.xml
index 5a234f8..814e6a9 100644
--- a/room/room-runtime/lint-baseline.xml
+++ b/room/room-runtime/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha10" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha10)" variant="all" version="8.3.0-alpha10">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="RestrictedApiAndroidX"
@@ -127,4 +127,22 @@
file="src/androidMain/kotlin/androidx/room/support/PrePackagedCopyOpenHelper.android.kt"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 20) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidMain/kotlin/androidx/room/util/TableInfo.android.kt"/>
+ </issue>
+
</issues>
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/RelationUtil.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/RelationUtil.android.kt
index cefa581..e4f7a9f 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/RelationUtil.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/RelationUtil.android.kt
@@ -14,8 +14,8 @@
* limitations under the License.
*/
+@file:JvmMultifileClass
@file:JvmName("RelationUtil")
-@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
package androidx.room.util
@@ -32,6 +32,7 @@
* @param isRelationCollection - True if [V] is a [Collection] which means it is non null.
* @param fetchBlock - A lambda for calling the generated _fetchRelationship function.
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
fun <K : Any, V> recursiveFetchHashMap(
map: HashMap<K, V>,
isRelationCollection: Boolean,
@@ -73,6 +74,7 @@
/**
* Same as [recursiveFetchHashMap] but for [LongSparseArray].
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
fun <V> recursiveFetchLongSparseArray(
map: LongSparseArray<V>,
isRelationCollection: Boolean,
@@ -112,6 +114,7 @@
/**
* Same as [recursiveFetchHashMap] but for [ArrayMap].
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
fun <K : Any, V> recursiveFetchArrayMap(
map: ArrayMap<K, V>,
isRelationCollection: Boolean,
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/StatementUtil.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/StatementUtil.android.kt
index 6b4d5d2..587b3fd 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/StatementUtil.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/StatementUtil.android.kt
@@ -27,12 +27,12 @@
*
* The implementation also contains Android-specific patches to workaround issues on older devices.
*/
-internal actual fun SQLiteStatement.getColumnIndex(name: String): Int {
- var index = this.columnIndexOf(name)
+internal actual fun SQLiteStatement.columnIndexOf(name: String): Int {
+ var index = this.columnIndexOfCommon(name)
if (index >= 0) {
return index
}
- index = this.columnIndexOf("`$name`")
+ index = this.columnIndexOfCommon("`$name`")
return if (index >= 0) {
index
} else {
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/RelationUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/RelationUtil.kt
new file mode 100644
index 0000000..3a11b73
--- /dev/null
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/RelationUtil.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmMultifileClass
+@file:JvmName("RelationUtil")
+
+package androidx.room.util
+
+import androidx.annotation.RestrictTo
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
+
+/**
+ * Utility function used in generated code to recursively fetch relationships when the amount of
+ * keys exceed [MAX_BIND_PARAMETER_CNT].
+ *
+ * @param map - The map containing the relationship keys to fill-in.
+ * @param isRelationCollection - True if [V] is a [Collection] which means it is non null.
+ * @param fetchBlock - A lambda for calling the generated _fetchRelationship function.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun <K : Any, V> recursiveFetchMap(
+ map: MutableMap<K, V>,
+ isRelationCollection: Boolean,
+ fetchBlock: (MutableMap<K, V>) -> Unit
+) {
+ val tmpMap = mutableMapOf<K, V>()
+ var count = 0
+ for (key in map.keys) {
+ // Safe because `V` is a nullable type arg when isRelationCollection == false and vice versa
+ @Suppress("UNCHECKED_CAST")
+ if (isRelationCollection) {
+ tmpMap[key] = map[key] as V
+ } else {
+ tmpMap[key] = null as V
+ }
+ count++
+ if (count == MAX_BIND_PARAMETER_CNT) {
+ // recursively load that batch
+ fetchBlock(tmpMap)
+ // for non collection relation, put the loaded batch in the original map,
+ // not needed when dealing with collections since references are passed
+ if (!isRelationCollection) {
+ map.putAll(tmpMap)
+ }
+ tmpMap.clear()
+ count = 0
+ }
+ }
+ if (count > 0) {
+ // load the last batch
+ fetchBlock(tmpMap)
+ // for non collection relation, put the last batch in the original map
+ if (!isRelationCollection) {
+ map.putAll(tmpMap)
+ }
+ }
+}
+
+/**
+ * Unfortunately, we cannot read this value so we are only setting it to the SQLite default.
+ */
+internal const val MAX_BIND_PARAMETER_CNT = 999
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/SchemaInfoUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/SchemaInfoUtil.kt
index d4ebdd5..2d68ae8 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/SchemaInfoUtil.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/SchemaInfoUtil.kt
@@ -69,11 +69,11 @@
): Set<TableInfo.ForeignKey> {
// this seems to return everything in order but it is not documented so better be safe
connection.prepare("PRAGMA foreign_key_list(`$tableName`)").use { stmt ->
- val idColumnIndex = stmt.getColumnIndex("id")
- val seqColumnIndex = stmt.getColumnIndex("seq")
- val tableColumnIndex = stmt.getColumnIndex("table")
- val onDeleteColumnIndex = stmt.getColumnIndex("on_delete")
- val onUpdateColumnIndex = stmt.getColumnIndex("on_update")
+ val idColumnIndex = stmt.columnIndexOf("id")
+ val seqColumnIndex = stmt.columnIndexOf("seq")
+ val tableColumnIndex = stmt.columnIndexOf("table")
+ val onDeleteColumnIndex = stmt.columnIndexOf("on_delete")
+ val onUpdateColumnIndex = stmt.columnIndexOf("on_update")
val ordered = readForeignKeyFieldMappings(stmt)
// Reset cursor as readForeignKeyFieldMappings has moved it
@@ -132,10 +132,10 @@
private fun readForeignKeyFieldMappings(
stmt: SQLiteStatement
): List<ForeignKeyWithSequence> {
- val idColumnIndex = stmt.getColumnIndex("id")
- val seqColumnIndex = stmt.getColumnIndex("seq")
- val fromColumnIndex = stmt.getColumnIndex("from")
- val toColumnIndex = stmt.getColumnIndex("to")
+ val idColumnIndex = stmt.columnIndexOf("id")
+ val seqColumnIndex = stmt.columnIndexOf("seq")
+ val fromColumnIndex = stmt.columnIndexOf("from")
+ val toColumnIndex = stmt.columnIndexOf("to")
return buildList {
while (stmt.step()) {
@@ -160,11 +160,11 @@
return emptyMap()
}
- val nameIndex = stmt.getColumnIndex("name")
- val typeIndex = stmt.getColumnIndex("type")
- val notNullIndex = stmt.getColumnIndex("notnull")
- val pkIndex = stmt.getColumnIndex("pk")
- val defaultValueIndex = stmt.getColumnIndex("dflt_value")
+ val nameIndex = stmt.columnIndexOf("name")
+ val typeIndex = stmt.columnIndexOf("type")
+ val notNullIndex = stmt.columnIndexOf("notnull")
+ val pkIndex = stmt.columnIndexOf("pk")
+ val defaultValueIndex = stmt.columnIndexOf("dflt_value")
return buildMap {
do {
@@ -195,9 +195,9 @@
*/
private fun readIndices(connection: SQLiteConnection, tableName: String): Set<TableInfo.Index>? {
connection.prepare("PRAGMA index_list(`$tableName`)").use { stmt ->
- val nameColumnIndex = stmt.getColumnIndex("name")
- val originColumnIndex = stmt.getColumnIndex("origin")
- val uniqueIndex = stmt.getColumnIndex("unique")
+ val nameColumnIndex = stmt.columnIndexOf("name")
+ val originColumnIndex = stmt.columnIndexOf("origin")
+ val uniqueIndex = stmt.columnIndexOf("unique")
if (nameColumnIndex == -1 || originColumnIndex == -1 || uniqueIndex == -1) {
// we cannot read them so better not validate any index.
return null
@@ -228,10 +228,10 @@
unique: Boolean
): TableInfo.Index? {
return connection.prepare("PRAGMA index_xinfo(`$name`)").use { stmt ->
- val seqnoColumnIndex = stmt.getColumnIndex("seqno")
- val cidColumnIndex = stmt.getColumnIndex("cid")
- val nameColumnIndex = stmt.getColumnIndex("name")
- val descColumnIndex = stmt.getColumnIndex("desc")
+ val seqnoColumnIndex = stmt.columnIndexOf("seqno")
+ val cidColumnIndex = stmt.columnIndexOf("cid")
+ val nameColumnIndex = stmt.columnIndexOf("name")
+ val descColumnIndex = stmt.columnIndexOf("desc")
if (
seqnoColumnIndex == -1 ||
cidColumnIndex == -1 ||
@@ -265,7 +265,7 @@
return buildSet {
connection.prepare("PRAGMA table_info(`$tableName`)").use { stmt ->
if (!stmt.step()) return@use
- val nameIndex = stmt.getColumnIndex("name")
+ val nameIndex = stmt.columnIndexOf("name")
do {
add(stmt.getText(nameIndex))
} while (stmt.step())
@@ -278,7 +278,7 @@
"SELECT * FROM sqlite_master WHERE `name` = '$tableName'"
).use { stmt ->
if (stmt.step()) {
- stmt.getText(stmt.getColumnIndex("sql"))
+ stmt.getText(stmt.columnIndexOf("sql"))
} else {
""
}
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt
index cebd289..d45b94f 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt
@@ -30,7 +30,7 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
fun getColumnIndexOrThrow(stmt: SQLiteStatement, name: String): Int {
- val index: Int = stmt.getColumnIndex(name)
+ val index: Int = stmt.columnIndexOf(name)
if (index >= 0) {
return index
}
@@ -43,13 +43,21 @@
/**
* Returns the zero-based index for the given column name, or -1 if the column doesn't exist.
*/
-internal expect fun SQLiteStatement.getColumnIndex(name: String): Int
+internal expect fun SQLiteStatement.columnIndexOf(name: String): Int
// TODO(b/322183292): Consider optimizing by creating a String->Int map, similar to Android
-internal fun SQLiteStatement.columnIndexOf(name: String): Int {
+internal fun SQLiteStatement.columnIndexOfCommon(name: String): Int {
val columnCount = getColumnCount()
for (i in 0 until columnCount) {
if (name == getColumnName(i)) return i
}
return -1
}
+
+/**
+ * Returns the zero-based index for the given column name, or -1 if the column doesn't exist.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun getColumnIndex(stmt: SQLiteStatement, name: String): Int {
+ return stmt.columnIndexOf(name)
+}
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/StatementUtil.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/StatementUtil.jvmNative.kt
index 437fe5c..96c4bae 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/StatementUtil.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/StatementUtil.jvmNative.kt
@@ -26,4 +26,4 @@
/**
* Returns the zero-based index for the given column name, or -1 if the column doesn't exist.
*/
-internal actual fun SQLiteStatement.getColumnIndex(name: String): Int = columnIndexOf(name)
+internal actual fun SQLiteStatement.columnIndexOf(name: String): Int = columnIndexOfCommon(name)
diff --git a/samples/AndroidXDemos/lint-baseline.xml b/samples/AndroidXDemos/lint-baseline.xml
index ffcc193..5bcc968 100644
--- a/samples/AndroidXDemos/lint-baseline.xml
+++ b/samples/AndroidXDemos/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha07)" variant="all" version="8.4.0-alpha07">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="OnClick"
@@ -51,7 +51,7 @@
<issue
id="NewApi"
- message="Call requires API level 29 (current min is 19): `android.os.StrictMode.VmPolicy.Builder#detectImplicitDirectBoot`"
+ message="Call requires API level 29 (current min is 21): `android.os.StrictMode.VmPolicy.Builder#detectImplicitDirectBoot`"
errorLine1=" .detectImplicitDirectBoot()"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -60,7 +60,7 @@
<issue
id="NewApi"
- message="Custom drawables requires API level 24 (current min is 19)"
+ message="Custom drawables requires API level 24 (current min is 21)"
errorLine1=" class="com.example.androidx.drawable.MyDrawable""
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -213,19 +213,26 @@
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v11`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `layout`.">
+ message="This folder configuration (`v11`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `layout`.">
<location
file="src/main/res/layout-v11"/>
</issue>
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v18`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `values`.">
+ message="This folder configuration (`v18`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
<location
file="src/main/res/values-v18"/>
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="src/main/res/values-v21"/>
+ </issue>
+
+ <issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" protected void onCreate(Bundle savedInstanceState) {"
diff --git a/samples/MediaRoutingDemo/lint-baseline.xml b/samples/MediaRoutingDemo/lint-baseline.xml
index b32d7d5..b1d0a01 100644
--- a/samples/MediaRoutingDemo/lint-baseline.xml
+++ b/samples/MediaRoutingDemo/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="RestrictedApiAndroidX"
@@ -120,7 +120,7 @@
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v18`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `values`.">
+ message="This folder configuration (`v18`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
<location
file="src/main/res/values-v18"/>
</issue>
diff --git a/samples/Support4Demos/lint-baseline.xml b/samples/Support4Demos/lint-baseline.xml
index 13a47b0..3dba431 100644
--- a/samples/Support4Demos/lint-baseline.xml
+++ b/samples/Support4Demos/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="MissingPermission"
@@ -137,24 +137,6 @@
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class com.example.android.supportv4.view.WindowInsetsPlayground is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" getWindow().setStatusBarColor(0x80000000);"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/example/android/supportv4/view/WindowInsetsPlayground.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class com.example.android.supportv4.view.WindowInsetsPlayground is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" getWindow().setNavigationBarColor(0x80000000);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/example/android/supportv4/view/WindowInsetsPlayground.java"/>
- </issue>
-
- <issue
id="ForegroundServiceType"
message="To call `Service.startForeground()`, the `<service>` element of manifest file must have the `foregroundServiceType` attribute specified"
errorLine1=" mService.startForeground(NOTIFICATION_ID, notification);"
@@ -201,12 +183,46 @@
<issue
id="ObsoleteSdkInt"
- message="This folder configuration (`v11`) is unnecessary; `minSdkVersion` is 19. Merge all the resources in this folder into `values`.">
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/android/supportv4/view/WindowInsetsControllerPlayground.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/android/supportv4/view/WindowInsetsPlayground.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" tools:targetApi="lollipop">"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/layout/activity_insets_controller.xml"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v11`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
<location
file="src/main/res/values-v11"/>
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="src/main/res/values-v21"/>
+ </issue>
+
+ <issue
id="LambdaLast"
message="Functional interface parameters (such as parameter 1, "queue", in com.example.android.supportv4.media.utils.QueueHelper.getMusicIndexOnQueue) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
errorLine1=" String mediaId) {"
diff --git a/samples/SupportEmojiDemos/lint-baseline.xml b/samples/SupportEmojiDemos/lint-baseline.xml
index 4292157..b5a4d29 100644
--- a/samples/SupportEmojiDemos/lint-baseline.xml
+++ b/samples/SupportEmojiDemos/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanSynchronizedMethods"
@@ -20,15 +20,6 @@
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class com.example.android.support.text.emoji.ConfigLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/com/example/android/support/text/emoji/ConfigLayout.java"/>
- </issue>
-
- <issue
id="RestrictedApiAndroidX"
message="EmojiCompat.reset can only be called from within the same library (androidx.emoji2:emoji2)"
errorLine1=" EmojiCompat.reset(config);"
@@ -38,6 +29,15 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/android/support/text/emoji/ConfigLayout.java"/>
+ </issue>
+
+ <issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public ConfigLayout(Context context) {"
diff --git a/samples/SupportPreferenceDemos/lint-baseline.xml b/samples/SupportPreferenceDemos/lint-baseline.xml
index a3353c4..e37b30d 100644
--- a/samples/SupportPreferenceDemos/lint-baseline.xml
+++ b/samples/SupportPreferenceDemos/lint-baseline.xml
@@ -1,5 +1,5 @@
<?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">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="MissingTvBanner"
@@ -11,6 +11,22 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/preference/LeanbackPreferences.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="This folder configuration (`v21`) is unnecessary; `minSdkVersion` is 21. Merge all the resources in this folder into `values`.">
+ <location
+ file="src/main/res/values-v21"/>
+ </issue>
+
+ <issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" protected void onCreate(Bundle savedInstanceState) {"
diff --git a/samples/SupportRemoteCallbackDemos/build.gradle b/samples/SupportRemoteCallbackDemos/build.gradle
index 4c0746b..5864d41 100644
--- a/samples/SupportRemoteCallbackDemos/build.gradle
+++ b/samples/SupportRemoteCallbackDemos/build.gradle
@@ -28,7 +28,6 @@
android {
defaultConfig {
- minSdkVersion 19
}
namespace "com.example.androidx.remotecallback.demos"
}
diff --git a/samples/SupportSliceDemos/build.gradle b/samples/SupportSliceDemos/build.gradle
index c928b0b..007adcb 100644
--- a/samples/SupportSliceDemos/build.gradle
+++ b/samples/SupportSliceDemos/build.gradle
@@ -33,7 +33,6 @@
android {
defaultConfig {
- minSdkVersion 19
}
namespace "com.example.androidx.slice.demos"
}
diff --git a/savedstate/savedstate-ktx/build.gradle b/savedstate/savedstate-ktx/build.gradle
index 915bed4..b54f221 100644
--- a/savedstate/savedstate-ktx/build.gradle
+++ b/savedstate/savedstate-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -41,7 +41,7 @@
androidx {
name = "SavedState Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Kotlin extensions for 'savedstate' artifact"
metalavaK2UastEnabled = true
diff --git a/security/security-crypto-ktx/build.gradle b/security/security-crypto-ktx/build.gradle
index 3ad3bb3..d78c899 100644
--- a/security/security-crypto-ktx/build.gradle
+++ b/security/security-crypto-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -50,7 +50,7 @@
androidx {
name = "Security Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.SECURITY
inceptionYear = "2020"
description = "Kotlin Extensions for the androidx.security:Security-crypto artifact"
diff --git a/settings.gradle b/settings.gradle
index 20624d7..84fb587 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -28,8 +28,8 @@
classpath("com.google.protobuf:protobuf-java:3.22.3")
// upgrade okio for gcpbuildcache that is compatible with the wire plugin used by androidx
classpath("com.squareup.okio:okio:3.3.0")
- classpath("com.gradle:gradle-enterprise-gradle-plugin:3.16")
- classpath("com.gradle:common-custom-user-data-gradle-plugin:1.12")
+ classpath("com.gradle:develocity-gradle-plugin:3.17.2")
+ classpath("com.gradle:common-custom-user-data-gradle-plugin:2.0.1")
classpath("androidx.build.gradle.gcpbuildcache:gcpbuildcache:1.0.0-beta07")
}
}
@@ -72,17 +72,17 @@
)
}
-apply(plugin: "com.gradle.enterprise")
+apply(plugin: "com.gradle.develocity")
apply(plugin: "com.gradle.common-custom-user-data-gradle-plugin")
apply(plugin: "androidx.build.gradle.gcpbuildcache")
def BUILD_NUMBER = System.getenv("BUILD_NUMBER")
-gradleEnterprise {
+develocity {
server = "https://ge.androidx.dev"
buildScan {
capture {
- taskInputFiles = true
+ fileFingerprints.set(true)
}
obfuscation {
hostname { host -> "unset" }
@@ -95,9 +95,7 @@
value("androidx.projects", getRequestedProjectSubsetName() ?: "Unset")
value("androidx.useMaxDepVersions", providers.gradleProperty("androidx.useMaxDepVersions").isPresent().toString())
- // Publish scan for androidx-main
- publishAlways()
- publishIfAuthenticated()
+ publishing.onlyIf { it.authenticated }
}
}
@@ -405,6 +403,7 @@
includeProject(":benchmark:integration-tests:macrobenchmark", [BuildType.MAIN])
includeProject(":benchmark:integration-tests:macrobenchmark-target", [BuildType.MAIN])
includeProject(":benchmark:integration-tests:startup-benchmark", [BuildType.MAIN])
+includeProject(":binarycompatibilityvalidator:binarycompatibilityvalidator", [BuildType.MAIN])
includeProject(":biometric:biometric", [BuildType.MAIN])
includeProject(":biometric:biometric-ktx", [BuildType.MAIN])
includeProject(":biometric:biometric-ktx-samples", "biometric/biometric-ktx/samples", [BuildType.MAIN])
@@ -657,6 +656,7 @@
includeProject(":datastore:datastore-compose-samples", [BuildType.COMPOSE])
includeProject(":datastore:datastore-preferences", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":datastore:datastore-preferences-core", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:datastore-preferences-external-protobuf", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":datastore:datastore-preferences-proto", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":datastore:datastore-preferences-rxjava2", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":datastore:datastore-preferences-rxjava3", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
diff --git a/slice/slice-builders-ktx/build.gradle b/slice/slice-builders-ktx/build.gradle
index da1bc13..3e68849 100644
--- a/slice/slice-builders-ktx/build.gradle
+++ b/slice/slice-builders-ktx/build.gradle
@@ -32,7 +32,6 @@
android {
defaultConfig {
- minSdkVersion 19
}
namespace "androidx.slice.builders.ktx"
}
diff --git a/slice/slice-builders/lint-baseline.xml b/slice/slice-builders/lint-baseline.xml
index 53333a6..e6c36b0 100644
--- a/slice/slice-builders/lint-baseline.xml
+++ b/slice/slice-builders/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha10" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha10)" variant="all" version="8.3.0-alpha10">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="WrongConstant"
@@ -28,4 +28,58 @@
file="src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/builders/impl/ListBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/builders/ListBuilder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java"/>
+ </issue>
+
</issues>
diff --git a/slice/slice-core/lint-baseline.xml b/slice/slice-core/lint-baseline.xml
index 01e7b79..2b22a3d 100644
--- a/slice/slice-core/lint-baseline.xml
+++ b/slice/slice-core/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha10" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha10)" variant="all" version="8.3.0-alpha10">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanUncheckedReflection"
@@ -10,4 +10,13 @@
file="src/main/java/androidx/slice/SliceManagerWrapper.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 20"
+ errorLine1=" @RequiresApi(20)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/SliceItem.java"/>
+ </issue>
+
</issues>
diff --git a/slice/slice-test/lint-baseline.xml b/slice/slice-test/lint-baseline.xml
index 55bf586..ccb6139 100644
--- a/slice/slice-test/lint-baseline.xml
+++ b/slice/slice-test/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="UnspecifiedImmutableFlag"
@@ -11,6 +11,24 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/test/SampleSliceProvider.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/test/SampleSliceProvider.java"/>
+ </issue>
+
+ <issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public static Uri getUri(String path, Context context) {"
diff --git a/slice/slice-view/lint-baseline.xml b/slice/slice-view/lint-baseline.xml
index d198219..6f2e88b 100644
--- a/slice/slice-view/lint-baseline.xml
+++ b/slice/slice-view/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanSynchronizedMethods"
@@ -12,33 +12,6 @@
<issue
id="ClassVerificationFailure"
- message="This call references a method added in API level 20; however, the containing class androidx.slice.widget.ActionRow is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" if (input.getRemoteInput().getAllowFreeFormInput()) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/slice/widget/ActionRow.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.slice.widget.GridRowView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mForeground.getBackground().setHotspot(x, y);"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/slice/widget/GridRowView.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.slice.widget.LocationBasedViewTracker is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" if (child.isAccessibilityFocused()) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/slice/widget/LocationBasedViewTracker.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
message="This call references a method added in API level 24; however, the containing class androidx.slice.widget.RemoteInputView.RemoteEditText is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" return isTemporarilyDetached();"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
@@ -47,19 +20,127 @@
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.slice.widget.SliceView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" super(context, attrs, defStyleAttr, defStyleRes);"
- errorLine2=" ~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/ActionRow.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/ActionRow.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/ActionRow.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/ActionRow.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/ActionRow.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/GridRowView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/GridRowView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/GridRowView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/LocationBasedViewTracker.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/LocationBasedViewTracker.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/RemoteInputView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slice/widget/SliceActionView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/slice/widget/SliceView.java"/>
</issue>
<issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 21; however, the containing class androidx.slice.widget.TemplateView is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" mForeground.getBackground().setHotspot(x, y);"
- errorLine2=" ~~~~~~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/slice/widget/TemplateView.java"/>
</issue>
diff --git a/slidingpanelayout/slidingpanelayout/lint-baseline.xml b/slidingpanelayout/slidingpanelayout/lint-baseline.xml
new file mode 100644
index 0000000..6bff2c6
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.kt"/>
+ </issue>
+
+</issues>
diff --git a/sqlite/sqlite-bundled/build.gradle b/sqlite/sqlite-bundled/build.gradle
index 8df8903..1f3d1d3 100644
--- a/sqlite/sqlite-bundled/build.gradle
+++ b/sqlite/sqlite-bundled/build.gradle
@@ -106,6 +106,7 @@
KonanTarget.ANDROID_X86,
KonanTarget.MACOS_ARM64,
KonanTarget.MACOS_X64,
+ KonanTarget.MINGW_X64,
KonanTarget.LINUX_X64,
].collect { it.INSTANCE } // Use INSTANCE to get object class instance from kotlin
@@ -144,7 +145,7 @@
)
// Define C++ compilation of JNI
- def jvmArtJniImplementation = createNativeCompilation("jvmArtJniImplementation") {
+ def jvmArtJniImplementation = createNativeCompilation("sqliteJni") {
configureEachTarget { nativeCompilation ->
// add JNI headers as sources
nativeCompilation.addJniHeaders()
diff --git a/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt b/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt
index 4b0ec4b..22b981a 100644
--- a/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt
+++ b/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt
@@ -33,7 +33,7 @@
private companion object {
init {
- NativeLibraryLoader.loadLibrary("jvmArtJniImplementation")
+ NativeLibraryLoader.loadLibrary("sqliteJni")
}
}
}
diff --git a/sqlite/sqlite-bundled/src/androidMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.android.kt b/sqlite/sqlite-bundled/src/androidMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.android.kt
index c54b9a6..9edb7a3 100644
--- a/sqlite/sqlite-bundled/src/androidMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.android.kt
+++ b/sqlite/sqlite-bundled/src/androidMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.android.kt
@@ -16,8 +16,11 @@
package androidx.sqlite.driver.bundled
+/**
+ * Helper class to load native libraries based on the host platform.
+ */
internal actual object NativeLibraryLoader {
- actual fun loadLibrary(name: String) {
+ actual fun loadLibrary(name: String): Unit = synchronized(this) {
System.loadLibrary(name)
}
}
diff --git a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.kt b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.kt
index 6930b5f..5482ca3 100644
--- a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.kt
+++ b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.kt
@@ -16,6 +16,9 @@
package androidx.sqlite.driver.bundled
+/**
+ * Helper class to load native libraries based on the host platform.
+ */
internal expect object NativeLibraryLoader {
/**
* Loads the given native library via JNI (if running on JVM or Android).
diff --git a/sqlite/sqlite-bundled/src/jvmMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.jvm.kt b/sqlite/sqlite-bundled/src/jvmMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.jvm.kt
index cae82a8..79eb81a 100644
--- a/sqlite/sqlite-bundled/src/jvmMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.jvm.kt
+++ b/sqlite/sqlite-bundled/src/jvmMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.jvm.kt
@@ -20,16 +20,40 @@
import java.nio.file.StandardCopyOption
import java.util.Locale
+/**
+ * Helper class to load native libraries based on the host platform.
+ */
internal actual object NativeLibraryLoader {
// TODO(b/304281116): Generate this via Gradle so it is consistent.
- actual fun loadLibrary(name: String) {
+ actual fun loadLibrary(name: String): Unit = synchronized(this) {
try {
System.loadLibrary(name)
return
- } catch (error: Throwable) {
- // looks like we are not on Android, continue
+ } catch (error: UnsatisfiedLinkError) {
+ // Likely not on Android, continue...
}
- // TODO(b/304281116): Temporary loading implementation
+ // TODO(b/304281116): Improve loading implementation
+ val libResourceName = getResourceName(name)
+ val libTempCopy = Files.createTempFile("androidx_$name", null)
+ .apply { toFile().deleteOnExit() }
+ NativeLibraryLoader::class.java.classLoader!!.getResourceAsStream(
+ libResourceName
+ ).use { resourceStream ->
+ checkNotNull(resourceStream) {
+ "Cannot find a suitable SQLite binary for ${System.getProperty("os.name")} | " +
+ "${System.getProperty("os.arch")}. Please file a bug at " +
+ "https://issuetracker.google.com/issues/new?component=460784"
+ }
+ Files.copy(resourceStream, libTempCopy, StandardCopyOption.REPLACE_EXISTING)
+ }
+ @Suppress("UnsafeDynamicallyLoadedCode")
+ System.load(libTempCopy.toFile().canonicalPath)
+ }
+
+ /**
+ * Gets the JAR's resource file path to the native library.
+ */
+ private fun getResourceName(name: String): String {
val osName =
System.getProperty("os.name")?.lowercase(Locale.US) ?: error("Cannot read osName")
val osArch =
@@ -37,6 +61,7 @@
val osPrefix = when {
osName.contains("linux") -> "linux"
osName.contains("mac") || osName.contains("osx") -> "osx"
+ osName.contains("windows") -> "windows"
else -> error("Unsupported operating system: $osName")
}
val archSuffix = when {
@@ -50,19 +75,12 @@
else -> error("Unsupported architecture: $osArch")
}
val resourceFolder = "${osPrefix}_$archSuffix"
- val ext = if (osPrefix == "linux") { "so" } else { "dylib" }
- val resourceName = "$resourceFolder/lib$name.$ext"
- val nativeLibCopy = Files.createTempFile("androidx_$name", null)
- nativeLibCopy.toFile().deleteOnExit()
- NativeLibraryLoader::class.java.classLoader!!.getResourceAsStream(
- resourceName
- ).use { resourceStream ->
- checkNotNull(resourceStream) {
- "Cannot find resource $resourceName"
- }
- Files.copy(resourceStream, nativeLibCopy, StandardCopyOption.REPLACE_EXISTING)
+ val extension = when (osPrefix) {
+ "linux" -> "so"
+ "osx" -> "dylib"
+ "windows" -> "dll"
+ else -> error("Unsupported operating system: $osName")
}
- @Suppress("UnsafeDynamicallyLoadedCode")
- System.load(nativeLibCopy.toFile().canonicalPath)
+ return "natives/$resourceFolder/lib$name.$extension"
}
}
diff --git a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.nativeCommon.kt b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.nativeCommon.kt
index 1ae2bf8..c1cf339 100644
--- a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.nativeCommon.kt
+++ b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.nativeCommon.kt
@@ -16,6 +16,9 @@
package androidx.sqlite.driver.bundled
+/**
+ * Helper class to load native libraries based on the host platform.
+ */
internal actual object NativeLibraryLoader {
actual fun loadLibrary(name: String) {
// no-op, we are already in native code
diff --git a/sqlite/sqlite-ktx/build.gradle b/sqlite/sqlite-ktx/build.gradle
index ac587d8..fd9573a 100644
--- a/sqlite/sqlite-ktx/build.gradle
+++ b/sqlite/sqlite-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -39,7 +39,7 @@
androidx {
name = "SQLite Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Kotlin extensions for DB"
metalavaK2UastEnabled = true
diff --git a/sqlite/sqlite/lint-baseline.xml b/sqlite/sqlite/lint-baseline.xml
new file mode 100644
index 0000000..13a7fa6
--- /dev/null
+++ b/sqlite/sqlite/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteCompat.android.kt"/>
+ </issue>
+
+</issues>
diff --git a/swiperefreshlayout/swiperefreshlayout/lint-baseline.xml b/swiperefreshlayout/swiperefreshlayout/lint-baseline.xml
index be24f80..05b8458 100644
--- a/swiperefreshlayout/swiperefreshlayout/lint-baseline.xml
+++ b/swiperefreshlayout/swiperefreshlayout/lint-baseline.xml
@@ -1,5 +1,23 @@
<?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">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" return android.os.Build.VERSION.SDK_INT >= 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/swiperefreshlayout/widget/CircleImageView.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" && ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayout.java"/>
+ </issue>
<issue
id="KotlinPropertyAccess"
diff --git a/test/ext/junit-gtest/src/main/cpp/CMakeLists.txt b/test/ext/junit-gtest/src/main/cpp/CMakeLists.txt
index c563898..5b4f8d7 100644
--- a/test/ext/junit-gtest/src/main/cpp/CMakeLists.txt
+++ b/test/ext/junit-gtest/src/main/cpp/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10.2)
+cmake_minimum_required(VERSION 3.22.1)
project(junit-gtest LANGUAGES CXX)
diff --git a/test/integration-tests/junit-gtest-test/src/main/cpp/CMakeLists.txt b/test/integration-tests/junit-gtest-test/src/main/cpp/CMakeLists.txt
index cd8a95e..d3898fb 100644
--- a/test/integration-tests/junit-gtest-test/src/main/cpp/CMakeLists.txt
+++ b/test/integration-tests/junit-gtest-test/src/main/cpp/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10.2)
+cmake_minimum_required(VERSION 3.22.1)
project(junit-gtest-test LANGUAGES CXX)
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiWindowTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiWindowTest.java
index bff0f577..37a9d00 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiWindowTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiWindowTest.java
@@ -35,7 +35,6 @@
private static final BySelector STATUS_BAR = By.res("com.android.systemui", "status_bar");
@Test
- @SdkSuppress(minSdkVersion = 21)
public void testMultiWindow_statusBar() {
// Can locate objects outside of current context.
assertTrue(mDevice.hasObject(STATUS_BAR));
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
index 0ecbc8d..4192492 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
@@ -278,7 +278,6 @@
}
@Test
- @SdkSuppress(minSdkVersion = 21) // Quick settings menu might not be present prior to API 21.
public void testOpenQuickSettings() {
mDevice.openQuickSettings();
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/ComposeTestActivity.kt b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/ComposeTestActivity.kt
index bba1130..bdf6446 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/ComposeTestActivity.kt
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/ComposeTestActivity.kt
@@ -22,7 +22,6 @@
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
@@ -69,14 +68,14 @@
) {
Text("Top", modifier = Modifier.padding(top = 20.dp).testTag("top-text"))
Spacer(modifier = Modifier.size(scrollHeight.dp))
- Row(
+ Column(
modifier = Modifier.padding(bottom = 20.dp),
- verticalAlignment = Alignment.CenterVertically,
+ horizontalAlignment = Alignment.CenterHorizontally,
) {
+ Text(text)
Button(onClick = { text = "Updated" }) {
Text("Update")
}
- Text(text)
}
}
}
diff --git a/test/uiautomator/uiautomator/api/current.txt b/test/uiautomator/uiautomator/api/current.txt
index 89cf12f6..3bfcf3f 100644
--- a/test/uiautomator/uiautomator/api/current.txt
+++ b/test/uiautomator/uiautomator/api/current.txt
@@ -169,7 +169,7 @@
method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
method @Deprecated public void dumpWindowHierarchy(String);
- method @Discouraged(message="Can be useful for simple commands, but lacks support for proper error handling, input data, or complex commands (quotes, pipes) that can be obtained from UiAutomation#executeShellCommandRwe or similar utilities.") @RequiresApi(21) public String executeShellCommand(String) throws java.io.IOException;
+ method @Discouraged(message="Can be useful for simple commands, but lacks support for proper error handling, input data, or complex commands (quotes, pipes) that can be obtained from UiAutomation#executeShellCommandRwe or similar utilities.") public String executeShellCommand(String) throws java.io.IOException;
method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
diff --git a/test/uiautomator/uiautomator/api/restricted_current.txt b/test/uiautomator/uiautomator/api/restricted_current.txt
index 89cf12f6..3bfcf3f 100644
--- a/test/uiautomator/uiautomator/api/restricted_current.txt
+++ b/test/uiautomator/uiautomator/api/restricted_current.txt
@@ -169,7 +169,7 @@
method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
method @Deprecated public void dumpWindowHierarchy(String);
- method @Discouraged(message="Can be useful for simple commands, but lacks support for proper error handling, input data, or complex commands (quotes, pipes) that can be obtained from UiAutomation#executeShellCommandRwe or similar utilities.") @RequiresApi(21) public String executeShellCommand(String) throws java.io.IOException;
+ method @Discouraged(message="Can be useful for simple commands, but lacks support for proper error handling, input data, or complex commands (quotes, pipes) that can be obtained from UiAutomation#executeShellCommandRwe or similar utilities.") public String executeShellCommand(String) throws java.io.IOException;
method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
diff --git a/test/uiautomator/uiautomator/lint-baseline.xml b/test/uiautomator/uiautomator/lint-baseline.xml
index 2a29920..eb168df 100644
--- a/test/uiautomator/uiautomator/lint-baseline.xml
+++ b/test/uiautomator/uiautomator/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha10" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha10)" variant="all" version="8.3.0-alpha10">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanUncheckedReflection"
diff --git a/test/uiautomator/uiautomator/src/androidTest/java/androidx/test/uiautomator/UiDeviceTest.java b/test/uiautomator/uiautomator/src/androidTest/java/androidx/test/uiautomator/UiDeviceTest.java
index 0286b1b..172a19f 100644
--- a/test/uiautomator/uiautomator/src/androidTest/java/androidx/test/uiautomator/UiDeviceTest.java
+++ b/test/uiautomator/uiautomator/src/androidTest/java/androidx/test/uiautomator/UiDeviceTest.java
@@ -88,7 +88,6 @@
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
public void testGetDisplayMetrics() throws IOException {
String densityCmdOutput = mDevice.executeShellCommand("wm density");
Pattern densityPattern = Pattern.compile("^Physical\\sdensity:\\s(\\d+)\\D+.*");
@@ -202,7 +201,6 @@
}
@Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
public void testExecuteShellCommand() throws IOException {
String output = mDevice.executeShellCommand("pm list packages");
assertTrue(output.contains("package:androidx.test.uiautomator.test"));
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/AccessibilityNodeInfoHelper.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/AccessibilityNodeInfoHelper.java
index 8e3523a..ce4fd2f 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/AccessibilityNodeInfoHelper.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/AccessibilityNodeInfoHelper.java
@@ -17,14 +17,10 @@
package androidx.test.uiautomator;
import android.graphics.Rect;
-import android.os.Build;
import android.util.Log;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
-import androidx.annotation.DoNotInline;
-import androidx.annotation.RequiresApi;
-
/**
* This class contains static helper methods to work with
* {@link AccessibilityNodeInfo}
@@ -68,15 +64,12 @@
}
intersectOrWarn(nodeRect, displayRect);
- // On platforms that give us access to the node's window
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- // Trim any portion of the bounds that are outside the window
- Rect bounds = new Rect();
- AccessibilityWindowInfo window = Api21Impl.getWindow(node);
- if (window != null) {
- Api21Impl.getBoundsInScreen(window, bounds);
- intersectOrWarn(nodeRect, bounds);
- }
+ // Trim any portion of the bounds that are outside the window
+ Rect bounds = new Rect();
+ AccessibilityWindowInfo window = node.getWindow();
+ if (window != null) {
+ window.getBoundsInScreen(bounds);
+ intersectOrWarn(nodeRect, bounds);
}
// Trim the bounds into any scrollable ancestor, if required.
@@ -106,21 +99,4 @@
Log.v(TAG, String.format("No overlap between %s and %s. Ignoring.", target, bounds));
}
}
-
- @RequiresApi(21)
- static class Api21Impl {
- private Api21Impl() {
- }
-
- @DoNotInline
- static void getBoundsInScreen(AccessibilityWindowInfo accessibilityWindowInfo,
- Rect outBounds) {
- accessibilityWindowInfo.getBoundsInScreen(outBounds);
- }
-
- @DoNotInline
- static AccessibilityWindowInfo getWindow(AccessibilityNodeInfo accessibilityNodeInfo) {
- return accessibilityNodeInfo.getWindow();
- }
- }
}
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java
index 6366d96..6f9863c 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/GestureController.java
@@ -25,9 +25,7 @@
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
-import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -136,15 +134,9 @@
try {
int displayId = pending.peek().displayId();
Display display = mDevice.getDisplayById(displayId);
- float displayRefreshRate;
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
- float[] refreshRates = Api21Impl.getSupportedRefreshRates(display);
- Arrays.sort(refreshRates);
- displayRefreshRate = refreshRates[refreshRates.length - 1];
- } else {
- // Set to current refresh rate if API version is lower than 21.
- displayRefreshRate = display.getRefreshRate();
- }
+ float[] refreshRates = display.getSupportedRefreshRates();
+ Arrays.sort(refreshRates);
+ float displayRefreshRate = refreshRates[refreshRates.length - 1];
injectionDelay = (long) (500 / displayRefreshRate);
} catch (Exception e) {
Log.e(TAG, "Fail to update motion event delay", e);
@@ -305,15 +297,4 @@
UiDevice getDevice() {
return mDevice;
}
-
- @RequiresApi(21)
- private static class Api21Impl {
- private Api21Impl() {
- }
-
- @DoNotInline
- static float[] getSupportedRefreshRates(Display display) {
- return display.getSupportedRefreshRates();
- }
- }
}
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
index 58d2d0f..efb05c4 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
@@ -20,7 +20,6 @@
import android.app.UiAutomation;
import android.app.UiAutomation.AccessibilityEventFilter;
import android.graphics.Point;
-import android.os.Build;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -457,8 +456,7 @@
*/
public boolean wakeDevice() throws RemoteException {
if(!isScreenOn()) {
- boolean supportsWakeButton = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH;
- sendKey(supportsWakeButton ? KeyEvent.KEYCODE_WAKEUP : KeyEvent.KEYCODE_POWER, 0);
+ sendKey(KeyEvent.KEYCODE_WAKEUP, 0);
return true;
}
return false;
@@ -473,8 +471,7 @@
*/
public boolean sleepDevice() throws RemoteException {
if(isScreenOn()) {
- boolean supportsSleepButton = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH;
- sendKey(supportsSleepButton ? KeyEvent.KEYCODE_SLEEP : KeyEvent.KEYCODE_POWER, 0);
+ sendKey(KeyEvent.KEYCODE_SLEEP , 0);
return true;
}
return false;
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
index a70fb2e..a105be1 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
@@ -1177,10 +1177,10 @@
}
/**
- * Helper method used for debugging to dump the current window's layout hierarchy.
- * Relative file paths are stored the application's internal private storage location.
+ * Dumps every window's layout hierarchy to a file in XML format.
*
- * @param fileName
+ * @param fileName The file path in which to store the window hierarchy information. Relative
+ * file paths are stored the application's internal private storage location.
* @deprecated Use {@link UiDevice#dumpWindowHierarchy(File)} or
* {@link UiDevice#dumpWindowHierarchy(OutputStream)} instead.
*/
@@ -1198,10 +1198,10 @@
}
/**
- * Dump the current window hierarchy to a {@link java.io.File}.
+ * Dumps every window's layout hierarchy to a {@link java.io.File} in XML format.
*
* @param dest The file in which to store the window hierarchy information.
- * @throws IOException
+ * @throws IOException if an I/O error occurs
*/
public void dumpWindowHierarchy(@NonNull File dest) throws IOException {
Log.d(TAG, String.format("Dumping window hierarchy to %s.", dest));
@@ -1211,10 +1211,10 @@
}
/**
- * Dump the current window hierarchy to an {@link java.io.OutputStream}.
+ * Dumps every window's layout hierarchy to an {@link java.io.OutputStream} in XML format.
*
* @param out The output stream that the window hierarchy information is written to.
- * @throws IOException
+ * @throws IOException if an I/O error occurs
*/
public void dumpWindowHierarchy(@NonNull OutputStream out) throws IOException {
Log.d(TAG, String.format("Dumping window hierarchy to %s.", out));
@@ -1351,11 +1351,10 @@
@Discouraged(message = "Can be useful for simple commands, but lacks support for proper error"
+ " handling, input data, or complex commands (quotes, pipes) that can be obtained "
+ "from UiAutomation#executeShellCommandRwe or similar utilities.")
- @RequiresApi(21)
@NonNull
public String executeShellCommand(@NonNull String cmd) throws IOException {
Log.d(TAG, String.format("Executing shell command: %s", cmd));
- try (ParcelFileDescriptor pfd = Api21Impl.executeShellCommand(getUiAutomation(), cmd);
+ try (ParcelFileDescriptor pfd = getUiAutomation().executeShellCommand(cmd);
FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
byte[] buf = new byte[512];
int bytesRead;
@@ -1394,7 +1393,6 @@
return p;
}
- @RequiresApi(21)
private List<AccessibilityWindowInfo> getWindows(UiAutomation uiAutomation) {
// Support multi-display searches for API level 30 and up.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@@ -1406,7 +1404,7 @@
}
return windowList;
}
- return Api21Impl.getWindows(uiAutomation);
+ return uiAutomation.getWindows();
}
/** Returns a list containing the root {@link AccessibilityNodeInfo}s for each active window */
@@ -1423,16 +1421,14 @@
} else {
Log.w(TAG, "Active window root not found.");
}
- // Support multi-window searches for API level 21 and up.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- for (final AccessibilityWindowInfo window : getWindows(uiAutomation)) {
- final AccessibilityNodeInfo root = Api21Impl.getRoot(window);
- if (root == null) {
- Log.w(TAG, "Skipping null root node for window: " + window);
- continue;
- }
- roots.add(root);
+ // Add all windows to support multi-window/display searches.
+ for (final AccessibilityWindowInfo window : getWindows(uiAutomation)) {
+ final AccessibilityNodeInfo root = window.getRoot();
+ if (root == null) {
+ Log.w(TAG, "Skipping null root node for window: " + window);
+ continue;
}
+ roots.add(root);
}
return roots.toArray(new AccessibilityNodeInfo[0]);
}
@@ -1489,10 +1485,8 @@
boolean serviceFlagsChanged = serviceInfo.flags != mCachedServiceFlags;
if (serviceFlagsChanged
|| SystemClock.uptimeMillis() - mLastServiceFlagsTime > SERVICE_FLAGS_TIMEOUT) {
- // Enable multi-window support for API 21+.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- serviceInfo.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
- }
+ // Enable multi-window support.
+ serviceInfo.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
// Enable or disable hierarchy compression.
if (mCompressed) {
serviceInfo.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
@@ -1520,27 +1514,6 @@
return mInteractionController;
}
- @RequiresApi(21)
- static class Api21Impl {
- private Api21Impl() {
- }
-
- @DoNotInline
- static ParcelFileDescriptor executeShellCommand(UiAutomation uiAutomation, String command) {
- return uiAutomation.executeShellCommand(command);
- }
-
- @DoNotInline
- static List<AccessibilityWindowInfo> getWindows(UiAutomation uiAutomation) {
- return uiAutomation.getWindows();
- }
-
- @DoNotInline
- static AccessibilityNodeInfo getRoot(AccessibilityWindowInfo accessibilityWindowInfo) {
- return accessibilityWindowInfo.getRoot();
- }
- }
-
@RequiresApi(24)
static class Api24Impl {
private Api24Impl() {
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
index 6be203f..d42a8f2 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
@@ -18,11 +18,9 @@
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
-import android.view.KeyEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -581,22 +579,14 @@
if (text == null) {
text = "";
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- // ACTION_SET_TEXT is added in API 21.
- AccessibilityNodeInfo node = findAccessibilityNodeInfo(
- mConfig.getWaitForSelectorTimeout());
- if (node == null) {
- throw new UiObjectNotFoundException(getSelector().toString());
- }
- Log.d(TAG, String.format("Setting text to '%s'.", text));
- Bundle args = new Bundle();
- args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
- return node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
- } else {
- clearTextField();
- Log.d(TAG, String.format("Setting text to '%s'.", text));
- return getInteractionController().sendText(text);
+ AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
+ if (node == null) {
+ throw new UiObjectNotFoundException(getSelector().toString());
}
+ Log.d(TAG, String.format("Setting text to '%s'.", text));
+ Bundle args = new Bundle();
+ args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
+ return node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
}
/**
@@ -618,26 +608,7 @@
CharSequence text = node.getText();
// do nothing if already empty
if (text != null && text.length() > 0) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- setText("");
- } else {
- Log.d(TAG, "Setting text to ''.");
- Bundle selectionArgs = new Bundle();
- // select all of the existing text
- selectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
- selectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,
- text.length());
- boolean ret = node.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
- if (!ret) {
- Log.w(TAG, "ACTION_FOCUS on text field failed.");
- }
- ret = node.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, selectionArgs);
- if (!ret) {
- Log.w(TAG, "ACTION_SET_SELECTION on text field failed.");
- }
- // now delete all
- getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
- }
+ setText("");
}
}
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
index 1fa068b..2da3b2a 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
@@ -25,7 +25,6 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
-import android.view.KeyEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
@@ -97,7 +96,7 @@
// Fetch and cache display information. This is safe as moving the underlying view to
// another display would invalidate the cached node and require recreating this UiObject2.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- AccessibilityWindowInfo window = Api21Impl.getWindow(cachedNode);
+ AccessibilityWindowInfo window = cachedNode.getWindow();
mDisplayId = window == null ? Display.DEFAULT_DISPLAY : Api30Impl.getDisplayId(window);
} else {
mDisplayId = Display.DEFAULT_DISPLAY;
@@ -969,37 +968,10 @@
}
Log.d(TAG, String.format("Setting text to '%s'.", text));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- // ACTION_SET_TEXT is added in API 21.
- Bundle args = new Bundle();
- args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
- if (!node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args)) {
- // TODO: Decide if we should throw here
- Log.w(TAG, "AccessibilityNodeInfo#performAction(ACTION_SET_TEXT) failed");
- }
- } else {
- CharSequence currentText = node.getText();
- if (currentText == null || !text.contentEquals(currentText)) {
- // Give focus to the object. Expect this to fail if the object already has focus.
- if (!node.performAction(AccessibilityNodeInfo.ACTION_FOCUS) && !node.isFocused()) {
- // TODO: Decide if we should throw here
- Log.w(TAG, "AccessibilityNodeInfo#performAction(ACTION_FOCUS) failed");
- }
- // Select the existing text. Expect this to fail if there is no existing text.
- Bundle args = new Bundle();
- args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
- args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,
- currentText == null ? 0 : currentText.length());
- if (!node.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, args) &&
- currentText != null && currentText.length() > 0) {
- // TODO: Decide if we should throw here
- Log.w(TAG, "AccessibilityNodeInfo#performAction(ACTION_SET_SELECTION) failed");
- }
- // Send the delete key to clear the existing text, then send the new text
- InteractionController ic = getDevice().getInteractionController();
- ic.sendKey(KeyEvent.KEYCODE_DEL, 0);
- ic.sendText(text);
- }
+ Bundle args = new Bundle();
+ args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
+ if (!node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args)) {
+ Log.w(TAG, "AccessibilityNodeInfo#performAction(ACTION_SET_TEXT) failed");
}
}
@@ -1055,23 +1027,6 @@
return mDevice;
}
- @RequiresApi(21)
- static class Api21Impl {
- private Api21Impl() {
- }
-
- @DoNotInline
- static AccessibilityWindowInfo getWindow(AccessibilityNodeInfo accessibilityNodeInfo) {
- return accessibilityNodeInfo.getWindow();
- }
-
- @DoNotInline
- static void getBoundsInScreen(AccessibilityWindowInfo accessibilityWindowInfo,
- Rect outBounds) {
- accessibilityWindowInfo.getBoundsInScreen(outBounds);
- }
- }
-
@RequiresApi(24)
static class Api24Impl {
private Api24Impl() {
diff --git a/tracing/tracing-ktx/build.gradle b/tracing/tracing-ktx/build.gradle
index b764032..4a33a5e 100644
--- a/tracing/tracing-ktx/build.gradle
+++ b/tracing/tracing-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -30,7 +30,7 @@
}
dependencies {
- api project(":tracing:tracing")
+ api(project(":tracing:tracing"))
api(libs.kotlinStdlib)
androidTestImplementation(libs.testExtJunit)
@@ -42,7 +42,7 @@
androidx {
name = "Tracing Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2020"
description = "Android Tracing"
metalavaK2UastEnabled = true
diff --git a/tracing/tracing-perfetto-binary/src/main/cpp/CMakeLists.txt b/tracing/tracing-perfetto-binary/src/main/cpp/CMakeLists.txt
index 4c98782..c5cbe48 100644
--- a/tracing/tracing-perfetto-binary/src/main/cpp/CMakeLists.txt
+++ b/tracing/tracing-perfetto-binary/src/main/cpp/CMakeLists.txt
@@ -14,7 +14,7 @@
# the License.
#
-cmake_minimum_required(VERSION 3.22)
+cmake_minimum_required(VERSION 3.22.1)
project(tracing_perfetto)
diff --git a/tracing/tracing-perfetto/lint-baseline.xml b/tracing/tracing-perfetto/lint-baseline.xml
new file mode 100644
index 0000000..42ea188
--- /dev/null
+++ b/tracing/tracing-perfetto/lint-baseline.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> Build.SUPPORTED_ABIS.first()"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/tracing/perfetto/security/SafeLibLoader.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) Impl21.getCodeCacheDir(context)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/tracing/perfetto/security/SafeLibLoader.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/tracing/perfetto/security/SafeLibLoader.kt"/>
+ </issue>
+
+</issues>
diff --git a/transition/transition-ktx/build.gradle b/transition/transition-ktx/build.gradle
index 9ca355f..3b67de1b 100644
--- a/transition/transition-ktx/build.gradle
+++ b/transition/transition-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -42,7 +42,7 @@
androidx {
name = "Transition Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2019"
description = "Kotlin extensions for 'transition' artifact"
metalavaK2UastEnabled = true
diff --git a/transition/transition/lint-baseline.xml b/transition/transition/lint-baseline.xml
index 0b4e446..c1d4ce8 100644
--- a/transition/transition/lint-baseline.xml
+++ b/transition/transition/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="ResourceType"
@@ -217,4 +217,121 @@
file="src/main/java/androidx/transition/Styleable.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/CanvasUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" private static final boolean SUPPORTS_VIEW_REMOVAL_SUPPRESSION = Build.VERSION.SDK_INT >= 21;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/ChangeTransform.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/GhostViewHolder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/GhostViewHolder.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21) // This method is only used on exactly API 28"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/GhostViewPlatform.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/ImageViewUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/ImageViewUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/ObjectAnimatorUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/ObjectAnimatorUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/PropertyValuesHolderUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/PropertyValuesHolderUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/ViewUtils.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/transition/ViewUtilsApi21.java"/>
+ </issue>
+
</issues>
diff --git a/tv/tv-foundation/build.gradle b/tv/tv-foundation/build.gradle
index 5ec0bb0..1e83ff7 100644
--- a/tv/tv-foundation/build.gradle
+++ b/tv/tv-foundation/build.gradle
@@ -67,7 +67,7 @@
androidx {
name = "TV Foundation"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.TV
inceptionYear = "2022"
description = "This library makes it easier for developers" +
diff --git a/tv/tv-material/api/current.ignore b/tv/tv-material/api/current.ignore
deleted file mode 100644
index 3bdd09b..0000000
--- a/tv/tv-material/api/current.ignore
+++ /dev/null
@@ -1,57 +0,0 @@
-// Baseline format: 1.0
-AddedClass: androidx.tv.material3.CardContainerColors:
- Added class androidx.tv.material3.CardContainerColors
-AddedClass: androidx.tv.material3.CardContainerDefaults:
- Added class androidx.tv.material3.CardContainerDefaults
-AddedClass: androidx.tv.material3.CardContainerKt:
- Added class androidx.tv.material3.CardContainerKt
-AddedClass: androidx.tv.material3.SurfaceColors:
- Added class androidx.tv.material3.SurfaceColors
-AddedClass: androidx.tv.material3.SurfaceDefaults:
- Added class androidx.tv.material3.SurfaceDefaults
-
-
-AddedMethod: androidx.tv.material3.CardDefaults#getScrimBrush():
- Added method androidx.tv.material3.CardDefaults.getScrimBrush()
-AddedMethod: androidx.tv.material3.ListItemDefaults#getTonalElevation():
- Added method androidx.tv.material3.ListItemDefaults.getTonalElevation()
-AddedMethod: androidx.tv.material3.ListItemKt#DenseListItem(boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.ui.Modifier, boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, float, androidx.tv.material3.ListItemShape, androidx.tv.material3.ListItemColors, androidx.tv.material3.ListItemScale, androidx.tv.material3.ListItemBorder, androidx.tv.material3.ListItemGlow, androidx.compose.foundation.interaction.MutableInteractionSource):
- Added method androidx.tv.material3.ListItemKt.DenseListItem(boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.ui.Modifier,boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,float,androidx.tv.material3.ListItemShape,androidx.tv.material3.ListItemColors,androidx.tv.material3.ListItemScale,androidx.tv.material3.ListItemBorder,androidx.tv.material3.ListItemGlow,androidx.compose.foundation.interaction.MutableInteractionSource)
-AddedMethod: androidx.tv.material3.ListItemKt#ListItem(boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.ui.Modifier, boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, float, androidx.tv.material3.ListItemShape, androidx.tv.material3.ListItemColors, androidx.tv.material3.ListItemScale, androidx.tv.material3.ListItemBorder, androidx.tv.material3.ListItemGlow, androidx.compose.foundation.interaction.MutableInteractionSource):
- Added method androidx.tv.material3.ListItemKt.ListItem(boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.ui.Modifier,boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,float,androidx.tv.material3.ListItemShape,androidx.tv.material3.ListItemColors,androidx.tv.material3.ListItemScale,androidx.tv.material3.ListItemBorder,androidx.tv.material3.ListItemGlow,androidx.compose.foundation.interaction.MutableInteractionSource)
-AddedMethod: androidx.tv.material3.SurfaceKt#Surface(androidx.compose.ui.Modifier, float, androidx.compose.ui.graphics.Shape, androidx.tv.material3.SurfaceColors, androidx.tv.material3.Border, androidx.tv.material3.Glow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>):
- Added method androidx.tv.material3.SurfaceKt.Surface(androidx.compose.ui.Modifier,float,androidx.compose.ui.graphics.Shape,androidx.tv.material3.SurfaceColors,androidx.tv.material3.Border,androidx.tv.material3.Glow,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>)
-
-
-RemovedClass: androidx.tv.material3.CardLayoutColors:
- Removed class androidx.tv.material3.CardLayoutColors
-RemovedClass: androidx.tv.material3.CardLayoutDefaults:
- Removed class androidx.tv.material3.CardLayoutDefaults
-RemovedClass: androidx.tv.material3.CardLayoutKt:
- Removed class androidx.tv.material3.CardLayoutKt
-RemovedClass: androidx.tv.material3.NonInteractiveSurfaceColors:
- Removed class androidx.tv.material3.NonInteractiveSurfaceColors
-RemovedClass: androidx.tv.material3.NonInteractiveSurfaceDefaults:
- Removed class androidx.tv.material3.NonInteractiveSurfaceDefaults
-
-
-RemovedField: androidx.tv.material3.ListItemDefaults#SelectedContinerColorOpacity:
- Removed field androidx.tv.material3.ListItemDefaults.SelectedContinerColorOpacity
-
-
-RemovedMethod: androidx.tv.material3.CardDefaults#getContainerGradient():
- Removed method androidx.tv.material3.CardDefaults.getContainerGradient()
-RemovedMethod: androidx.tv.material3.ListItemDefaults#getFocusedDisabledBorder():
- Removed method androidx.tv.material3.ListItemDefaults.getFocusedDisabledBorder()
-RemovedMethod: androidx.tv.material3.ListItemDefaults#getListItemElevation():
- Removed method androidx.tv.material3.ListItemDefaults.getListItemElevation()
-RemovedMethod: androidx.tv.material3.ListItemDefaults#getListItemShape():
- Removed method androidx.tv.material3.ListItemDefaults.getListItemShape()
-RemovedMethod: androidx.tv.material3.ListItemKt#DenseListItem(boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.ui.Modifier, boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, float, androidx.tv.material3.ListItemShape, androidx.tv.material3.ListItemColors, androidx.tv.material3.ListItemScale, androidx.tv.material3.ListItemBorder, androidx.tv.material3.ListItemGlow, androidx.compose.foundation.interaction.MutableInteractionSource, kotlin.jvm.functions.Function0<kotlin.Unit>):
- Removed method androidx.tv.material3.ListItemKt.DenseListItem(boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.ui.Modifier,boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,float,androidx.tv.material3.ListItemShape,androidx.tv.material3.ListItemColors,androidx.tv.material3.ListItemScale,androidx.tv.material3.ListItemBorder,androidx.tv.material3.ListItemGlow,androidx.compose.foundation.interaction.MutableInteractionSource,kotlin.jvm.functions.Function0<kotlin.Unit>)
-RemovedMethod: androidx.tv.material3.ListItemKt#ListItem(boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.ui.Modifier, boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, float, androidx.tv.material3.ListItemShape, androidx.tv.material3.ListItemColors, androidx.tv.material3.ListItemScale, androidx.tv.material3.ListItemBorder, androidx.tv.material3.ListItemGlow, androidx.compose.foundation.interaction.MutableInteractionSource, kotlin.jvm.functions.Function0<kotlin.Unit>):
- Removed method androidx.tv.material3.ListItemKt.ListItem(boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.ui.Modifier,boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,float,androidx.tv.material3.ListItemShape,androidx.tv.material3.ListItemColors,androidx.tv.material3.ListItemScale,androidx.tv.material3.ListItemBorder,androidx.tv.material3.ListItemGlow,androidx.compose.foundation.interaction.MutableInteractionSource,kotlin.jvm.functions.Function0<kotlin.Unit>)
-RemovedMethod: androidx.tv.material3.SurfaceKt#Surface(androidx.compose.ui.Modifier, float, androidx.compose.ui.graphics.Shape, androidx.tv.material3.NonInteractiveSurfaceColors, androidx.tv.material3.Border, androidx.tv.material3.Glow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>):
- Removed method androidx.tv.material3.SurfaceKt.Surface(androidx.compose.ui.Modifier,float,androidx.compose.ui.graphics.Shape,androidx.tv.material3.NonInteractiveSurfaceColors,androidx.tv.material3.Border,androidx.tv.material3.Glow,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>)
-RemovedMethod: androidx.tv.material3.SurfaceKt#getLocalAbsoluteTonalElevation():
- Removed method androidx.tv.material3.SurfaceKt.getLocalAbsoluteTonalElevation()
diff --git a/tv/tv-material/api/restricted_current.ignore b/tv/tv-material/api/restricted_current.ignore
deleted file mode 100644
index 3bdd09b..0000000
--- a/tv/tv-material/api/restricted_current.ignore
+++ /dev/null
@@ -1,57 +0,0 @@
-// Baseline format: 1.0
-AddedClass: androidx.tv.material3.CardContainerColors:
- Added class androidx.tv.material3.CardContainerColors
-AddedClass: androidx.tv.material3.CardContainerDefaults:
- Added class androidx.tv.material3.CardContainerDefaults
-AddedClass: androidx.tv.material3.CardContainerKt:
- Added class androidx.tv.material3.CardContainerKt
-AddedClass: androidx.tv.material3.SurfaceColors:
- Added class androidx.tv.material3.SurfaceColors
-AddedClass: androidx.tv.material3.SurfaceDefaults:
- Added class androidx.tv.material3.SurfaceDefaults
-
-
-AddedMethod: androidx.tv.material3.CardDefaults#getScrimBrush():
- Added method androidx.tv.material3.CardDefaults.getScrimBrush()
-AddedMethod: androidx.tv.material3.ListItemDefaults#getTonalElevation():
- Added method androidx.tv.material3.ListItemDefaults.getTonalElevation()
-AddedMethod: androidx.tv.material3.ListItemKt#DenseListItem(boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.ui.Modifier, boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, float, androidx.tv.material3.ListItemShape, androidx.tv.material3.ListItemColors, androidx.tv.material3.ListItemScale, androidx.tv.material3.ListItemBorder, androidx.tv.material3.ListItemGlow, androidx.compose.foundation.interaction.MutableInteractionSource):
- Added method androidx.tv.material3.ListItemKt.DenseListItem(boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.ui.Modifier,boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,float,androidx.tv.material3.ListItemShape,androidx.tv.material3.ListItemColors,androidx.tv.material3.ListItemScale,androidx.tv.material3.ListItemBorder,androidx.tv.material3.ListItemGlow,androidx.compose.foundation.interaction.MutableInteractionSource)
-AddedMethod: androidx.tv.material3.ListItemKt#ListItem(boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.ui.Modifier, boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, float, androidx.tv.material3.ListItemShape, androidx.tv.material3.ListItemColors, androidx.tv.material3.ListItemScale, androidx.tv.material3.ListItemBorder, androidx.tv.material3.ListItemGlow, androidx.compose.foundation.interaction.MutableInteractionSource):
- Added method androidx.tv.material3.ListItemKt.ListItem(boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.ui.Modifier,boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,float,androidx.tv.material3.ListItemShape,androidx.tv.material3.ListItemColors,androidx.tv.material3.ListItemScale,androidx.tv.material3.ListItemBorder,androidx.tv.material3.ListItemGlow,androidx.compose.foundation.interaction.MutableInteractionSource)
-AddedMethod: androidx.tv.material3.SurfaceKt#Surface(androidx.compose.ui.Modifier, float, androidx.compose.ui.graphics.Shape, androidx.tv.material3.SurfaceColors, androidx.tv.material3.Border, androidx.tv.material3.Glow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>):
- Added method androidx.tv.material3.SurfaceKt.Surface(androidx.compose.ui.Modifier,float,androidx.compose.ui.graphics.Shape,androidx.tv.material3.SurfaceColors,androidx.tv.material3.Border,androidx.tv.material3.Glow,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>)
-
-
-RemovedClass: androidx.tv.material3.CardLayoutColors:
- Removed class androidx.tv.material3.CardLayoutColors
-RemovedClass: androidx.tv.material3.CardLayoutDefaults:
- Removed class androidx.tv.material3.CardLayoutDefaults
-RemovedClass: androidx.tv.material3.CardLayoutKt:
- Removed class androidx.tv.material3.CardLayoutKt
-RemovedClass: androidx.tv.material3.NonInteractiveSurfaceColors:
- Removed class androidx.tv.material3.NonInteractiveSurfaceColors
-RemovedClass: androidx.tv.material3.NonInteractiveSurfaceDefaults:
- Removed class androidx.tv.material3.NonInteractiveSurfaceDefaults
-
-
-RemovedField: androidx.tv.material3.ListItemDefaults#SelectedContinerColorOpacity:
- Removed field androidx.tv.material3.ListItemDefaults.SelectedContinerColorOpacity
-
-
-RemovedMethod: androidx.tv.material3.CardDefaults#getContainerGradient():
- Removed method androidx.tv.material3.CardDefaults.getContainerGradient()
-RemovedMethod: androidx.tv.material3.ListItemDefaults#getFocusedDisabledBorder():
- Removed method androidx.tv.material3.ListItemDefaults.getFocusedDisabledBorder()
-RemovedMethod: androidx.tv.material3.ListItemDefaults#getListItemElevation():
- Removed method androidx.tv.material3.ListItemDefaults.getListItemElevation()
-RemovedMethod: androidx.tv.material3.ListItemDefaults#getListItemShape():
- Removed method androidx.tv.material3.ListItemDefaults.getListItemShape()
-RemovedMethod: androidx.tv.material3.ListItemKt#DenseListItem(boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.ui.Modifier, boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, float, androidx.tv.material3.ListItemShape, androidx.tv.material3.ListItemColors, androidx.tv.material3.ListItemScale, androidx.tv.material3.ListItemBorder, androidx.tv.material3.ListItemGlow, androidx.compose.foundation.interaction.MutableInteractionSource, kotlin.jvm.functions.Function0<kotlin.Unit>):
- Removed method androidx.tv.material3.ListItemKt.DenseListItem(boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.ui.Modifier,boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,float,androidx.tv.material3.ListItemShape,androidx.tv.material3.ListItemColors,androidx.tv.material3.ListItemScale,androidx.tv.material3.ListItemBorder,androidx.tv.material3.ListItemGlow,androidx.compose.foundation.interaction.MutableInteractionSource,kotlin.jvm.functions.Function0<kotlin.Unit>)
-RemovedMethod: androidx.tv.material3.ListItemKt#ListItem(boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, androidx.compose.ui.Modifier, boolean, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>, float, androidx.tv.material3.ListItemShape, androidx.tv.material3.ListItemColors, androidx.tv.material3.ListItemScale, androidx.tv.material3.ListItemBorder, androidx.tv.material3.ListItemGlow, androidx.compose.foundation.interaction.MutableInteractionSource, kotlin.jvm.functions.Function0<kotlin.Unit>):
- Removed method androidx.tv.material3.ListItemKt.ListItem(boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,androidx.compose.ui.Modifier,boolean,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>,kotlin.jvm.functions.Function0<kotlin.Unit>,float,androidx.tv.material3.ListItemShape,androidx.tv.material3.ListItemColors,androidx.tv.material3.ListItemScale,androidx.tv.material3.ListItemBorder,androidx.tv.material3.ListItemGlow,androidx.compose.foundation.interaction.MutableInteractionSource,kotlin.jvm.functions.Function0<kotlin.Unit>)
-RemovedMethod: androidx.tv.material3.SurfaceKt#Surface(androidx.compose.ui.Modifier, float, androidx.compose.ui.graphics.Shape, androidx.tv.material3.NonInteractiveSurfaceColors, androidx.tv.material3.Border, androidx.tv.material3.Glow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>):
- Removed method androidx.tv.material3.SurfaceKt.Surface(androidx.compose.ui.Modifier,float,androidx.compose.ui.graphics.Shape,androidx.tv.material3.NonInteractiveSurfaceColors,androidx.tv.material3.Border,androidx.tv.material3.Glow,kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>)
-RemovedMethod: androidx.tv.material3.SurfaceKt#getLocalAbsoluteTonalElevation():
- Removed method androidx.tv.material3.SurfaceKt.getLocalAbsoluteTonalElevation()
diff --git a/tv/tv-material/build.gradle b/tv/tv-material/build.gradle
index 740d96f..a347f5a 100644
--- a/tv/tv-material/build.gradle
+++ b/tv/tv-material/build.gradle
@@ -33,8 +33,11 @@
dependencies {
api(libs.kotlinStdlib)
- def composeVersion = "1.6.4"
- api("androidx.annotation:annotation:$composeVersion")
+ def annotationVersion = "1.7.1"
+ def composeVersion = "1.6.5"
+ def profileInstallerVersion = "1.3.1"
+
+ api("androidx.annotation:annotation:$annotationVersion")
api("androidx.compose.animation:animation:$composeVersion")
api("androidx.compose.foundation:foundation:$composeVersion")
api("androidx.compose.foundation:foundation-layout:$composeVersion")
@@ -45,7 +48,7 @@
api("androidx.compose.ui:ui-graphics:$composeVersion")
api("androidx.compose.ui:ui-text:$composeVersion")
- implementation("androidx.profileinstaller:profileinstaller:1.3.1")
+ implementation("androidx.profileinstaller:profileinstaller:$profileInstallerVersion")
androidTestImplementation(libs.truth)
androidTestImplementation(project(":compose:runtime:runtime"))
@@ -68,7 +71,7 @@
androidx {
name = "TV Material"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.TV_MATERIAL
inceptionYear = "2022"
description = "build TV applications using controls that adhere to Material Design Language."
diff --git a/vectordrawable/integration-tests/testapp/lint-baseline.xml b/vectordrawable/integration-tests/testapp/lint-baseline.xml
index 9b7fbaf..40c0d41 100644
--- a/vectordrawable/integration-tests/testapp/lint-baseline.xml
+++ b/vectordrawable/integration-tests/testapp/lint-baseline.xml
@@ -1,122 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
-
- <issue
- id="NewApi"
- message="`<animated-vector>` requires API level 21 (current min is 19)"
- errorLine1="<animated-vector xmlns:android="http://schemas.android.com/apk/res/android""
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/animation_vector_drawable_grouping_1.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`<animated-vector>` requires API level 21 (current min is 19)"
- errorLine1="<animated-vector xmlns:android="http://schemas.android.com/apk/res/android""
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/animation_vector_drawable_grouping_1_path_motion.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`<animated-vector>` requires API level 21 (current min is 19)"
- errorLine1="<animated-vector xmlns:android="http://schemas.android.com/apk/res/android""
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/animation_vector_drawable_grouping_1_path_motion_object.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`<animated-vector>` requires API level 21 (current min is 19)"
- errorLine1="<animated-vector xmlns:android="http://schemas.android.com/apk/res/android""
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/animation_vector_drawable_grouping_accelerate.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`<animated-vector>` requires API level 21 (current min is 19)"
- errorLine1="<animated-vector xmlns:android="http://schemas.android.com/apk/res/android""
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/animation_vector_drawable_grouping_decelerate.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`<animated-vector>` requires API level 21 (current min is 19)"
- errorLine1="<animated-vector xmlns:android="http://schemas.android.com/apk/res/android""
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/animation_vector_progress_bar.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`<animated-vector>` requires API level 21 (current min is 19)"
- errorLine1="<animated-vector xmlns:aapt="http://schemas.android.com/aapt""
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/btn_radio_on_to_off_bundle.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`<animated-vector>` requires API level 21 (current min is 19)"
- errorLine1="<animated-vector xmlns:android="http://schemas.android.com/apk/res/android""
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/ic_hourglass_animation.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`<animated-vector>` requires API level 21 (current min is 19)"
- errorLine1="<animated-vector xmlns:android="http://schemas.android.com/apk/res/android""
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/ic_signal_airplane_v2_animation.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Using theme references in XML drawables requires API level 21 (current min is 19)"
- errorLine1=" android:fillColor="?android:attr/colorForeground""
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/vector_drawable23.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Using theme references in XML drawables requires API level 21 (current min is 19)"
- errorLine1=" android:strokeColor="?android:attr/colorForeground""
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/vector_drawable23.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Using theme references in XML drawables requires API level 21 (current min is 19)"
- errorLine1=" android:fillColor="?android:attr/colorForeground""
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/vector_drawable24.xml"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Using theme references in XML drawables requires API level 21 (current min is 19)"
- errorLine1=" android:strokeColor="?android:attr/colorForeground""
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/drawable/vector_drawable24.xml"/>
- </issue>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="InvalidVectorPath"
diff --git a/vectordrawable/vectordrawable-animated/lint-baseline.xml b/vectordrawable/vectordrawable-animated/lint-baseline.xml
index 75dee0e..b0787c6 100644
--- a/vectordrawable/vectordrawable-animated/lint-baseline.xml
+++ b/vectordrawable/vectordrawable-animated/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -38,6 +38,24 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/vectordrawable/graphics/drawable/AnimatedVectorDrawableCompat.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java"/>
+ </issue>
+
+ <issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" protected void onBoundsChange(Rect bounds) {"
diff --git a/versionedparcelable/versionedparcelable/lint-baseline.xml b/versionedparcelable/versionedparcelable/lint-baseline.xml
index 559f41d..c189bd7 100644
--- a/versionedparcelable/versionedparcelable/lint-baseline.xml
+++ b/versionedparcelable/versionedparcelable/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanUncheckedReflection"
@@ -20,6 +20,51 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/versionedparcelable/VersionedParcel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/versionedparcelable/VersionedParcel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/versionedparcelable/VersionedParcel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/versionedparcelable/VersionedParcel.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/versionedparcelable/VersionedParcel.java"/>
+ </issue>
+
+ <issue
id="LambdaLast"
message="Functional interface parameters (such as parameter 1, "val", in androidx.versionedparcelable.VersionedParcel.writeStrongInterface) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
errorLine1=" public void writeStrongInterface(@Nullable IInterface val, int fieldId) {"
diff --git a/viewpager2/viewpager2/lint-baseline.xml b/viewpager2/viewpager2/lint-baseline.xml
index 8687be6..964fd05 100644
--- a/viewpager2/viewpager2/lint-baseline.xml
+++ b/viewpager2/viewpager2/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha10" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha10)" variant="all" version="8.3.0-alpha10">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -10,4 +10,31 @@
file="src/androidTest/java/androidx/viewpager2/widget/swipe/ManualSwipeInjector.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/viewpager2/widget/ViewPager2.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" if (Build.VERSION.SDK_INT >= 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/viewpager2/widget/WindowInsetsApplier.java"/>
+ </issue>
+
</issues>
diff --git a/wear/compose/compose-foundation/build.gradle b/wear/compose/compose-foundation/build.gradle
index 9ac320a..02c4122 100644
--- a/wear/compose/compose-foundation/build.gradle
+++ b/wear/compose/compose-foundation/build.gradle
@@ -81,7 +81,7 @@
androidx {
name = "Android Wear Compose Foundation"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "WearOS Compose Foundation Library. This library makes it easier for developers" +
"to write Jetpack Compose applications for Wearable devices by providing " +
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/rotary/RotaryScrollable.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/rotary/RotaryScrollable.kt
index e3cd485..72e6f56 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/rotary/RotaryScrollable.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/rotary/RotaryScrollable.kt
@@ -69,6 +69,16 @@
* LazyList and others. [ScalingLazyColumn] has a build-in rotary support, and accepts
* [RotaryScrollableBehavior] directly as a parameter.
*
+ * This modifier handles rotary input devices, used for scrolling. These devices can be categorized
+ * as high-resolution or low-resolution based on their precision.
+ *
+ * - High-res devices: Offer finer control and can detect smaller rotations.
+ * This allows for more precise adjustments during scrolling. One example of a high-res
+ * device is the crown (also known as rotating side button), located on the side of the watch.
+ * - Low-res devices: Have less granular control, registering larger rotations
+ * at a time. Scrolling behavior is adapted to compensate for these larger jumps. Examples
+ * include physical or virtual bezels, positioned around the screen.
+ *
* This modifier supports rotary scrolling and snapping.
* The behaviour is configured by the provided [RotaryScrollableBehavior]:
* either provide [RotaryScrollableDefaults.behavior] for scrolling with/without fling
@@ -103,7 +113,8 @@
/**
* An interface for handling scroll events. Has implementations for handling scroll
* with/without fling [FlingRotaryScrollableBehavior] and for handling snap
- * [LowResSnapRotaryScrollableBehavior], [HighResSnapRotaryScrollableBehavior].
+ * [LowResSnapRotaryScrollableBehavior], [HighResSnapRotaryScrollableBehavior] (see
+ * [Modifier.rotaryScrollable] for descriptions of low-res and high-res devices).
*/
interface RotaryScrollableBehavior {
@@ -321,7 +332,8 @@
* Handles scroll with fling.
*
* @return A scroll with fling implementation of [RotaryScrollableBehavior] which is suitable
- * for both low-res and high-res inputs.
+ * for both low-res and high-res inputs (see [Modifier.rotaryScrollable] for descriptions
+ * of low-res and high-res devices).
*
* @param scrollableState Scrollable state which will be scrolled while receiving rotary events
* @param flingBehavior Logic describing Fling behavior. If null - fling will not happen
@@ -360,7 +372,8 @@
* Handles scroll with snap.
*
* @return A snap implementation of [RotaryScrollableBehavior] which is either suitable for low-res
- * or high-res input.
+ * or high-res input (see [Modifier.rotaryScrollable] for descriptions of low-res
+ * and high-res devices).
*
* @param layoutInfoProvider Implementation of [RotarySnapLayoutInfoProvider], which connects
* scrollableState to a rotary input for snapping scroll actions.
@@ -419,8 +432,9 @@
/**
* An abstract base class for handling scroll events. Has implementations for handling scroll
- * with/without fling [FlingRotaryScrollableBehavior] and for handling snap [LowResSnapRotaryScrollableBehavior],
- * [HighResSnapRotaryScrollableBehavior].
+ * with/without fling [FlingRotaryScrollableBehavior] and for handling snap
+ * [LowResSnapRotaryScrollableBehavior], [HighResSnapRotaryScrollableBehavior] (see
+ * [Modifier.rotaryScrollable] for descriptions of low-res and high-res devices ).
*/
internal abstract class BaseRotaryScrollableBehavior : RotaryScrollableBehavior {
@@ -797,7 +811,8 @@
*
* For a high-res input it has a filtering for events which are coming
* with an opposite sign (this might happen to devices with rsb,
- * especially at the end of the scroll )
+ * especially at the end of the scroll ) - see [Modifier.rotaryScrollable] for descriptions
+ * of low-res and high-res devices.
*
* This scroll behavior supports fling. It can be set with [RotaryFlingHandler].
*/
@@ -873,7 +888,8 @@
}
/**
- * A scroll behavior for RSB(high-res) input with snapping and without fling.
+ * A scroll behavior for RSB(high-res) input with snapping and without fling (see
+ * [Modifier.rotaryScrollable] for descriptions of low-res and high-res devices ).
*
* Threshold for snapping is set dynamically in ThresholdBehavior, which depends
* on the scroll speed and the average size of the items.
@@ -1030,7 +1046,8 @@
}
/**
- * A scroll behavior for Bezel(low-res) input with snapping and without fling
+ * A scroll behavior for Bezel(low-res) input with snapping and without fling (see
+ * [Modifier.rotaryScrollable] for descriptions of low-res and high-res devices ).
*
* This scroll behavior doesn't support fling.
*/
diff --git a/wear/compose/compose-material-core/build.gradle b/wear/compose/compose-material-core/build.gradle
index 7f7f700..0a0d543 100644
--- a/wear/compose/compose-material-core/build.gradle
+++ b/wear/compose/compose-material-core/build.gradle
@@ -75,7 +75,7 @@
androidx {
name = "Android Wear Compose Material Core"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2022"
description = "WearOS Compose Material Core Library. This library contains themeless " +
"components that are shared between different WearOS Compose Material libraries. It " +
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt
index d1e577c..1fdbc11 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt
@@ -127,21 +127,16 @@
{
onDrawWithContent {
drawBox(this, boxColorState.value, progress.value, isRtl)
-
- if (targetState == SelectionStage.Checked) {
- // Passing startXOffset as we want checkbox to be aligned to the end of the canvas.
- drawTick(checkmarkColorState.value, progress.value, startXOffset, enabled)
- } else {
- // Passing startXOffset as we want checkbox to be aligned to the end of the canvas.
- eraseTick(
- checkmarkColorState.value,
- progress.value,
- startXOffset,
- enabled
- )
- }
+ animateTick(
+ enabled = enabled,
+ checked = checked,
+ tickColor = checkmarkColorState.value,
+ tickProgress = progress.value,
+ startXOffset = startXOffset
+ )
}
- })
+ }
+ )
}
/**
@@ -405,6 +400,24 @@
operator fun invoke(selected: Boolean): Int
}
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun DrawScope.animateTick(
+ enabled: Boolean,
+ checked: Boolean,
+ tickColor: Color,
+ tickProgress: Float,
+ startXOffset: Dp,
+) {
+ val targetState = if (checked) SelectionStage.Checked else SelectionStage.Unchecked
+ if (targetState == SelectionStage.Checked) {
+ // Passing startXOffset as we want checkbox to be aligned to the end of the canvas.
+ drawTick(tickColor, tickProgress, startXOffset, enabled)
+ } else {
+ // Passing startXOffset as we want checkbox to be aligned to the end of the canvas.
+ eraseTick(tickColor, tickProgress, startXOffset, enabled)
+ }
+}
+
@Composable
private fun animateProgress(
transition: Transition<SelectionStage>,
diff --git a/wear/compose/compose-material/build.gradle b/wear/compose/compose-material/build.gradle
index 04c1634..4abcef7 100644
--- a/wear/compose/compose-material/build.gradle
+++ b/wear/compose/compose-material/build.gradle
@@ -77,7 +77,7 @@
androidx {
name = "Android Wear Compose Material"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "WearOS Compose Material Library. This library makes it easier for developers " +
"to write Jetpack Compose applications for Wearable devices that implement Wear " +
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/SwipeToRevealSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/SwipeToRevealSample.kt
index 7e565fd..44ff9ba 100644
--- a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/SwipeToRevealSample.kt
+++ b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/SwipeToRevealSample.kt
@@ -105,10 +105,11 @@
modifier = Modifier.fillMaxWidth(),
onClick = { /* Add the chip click handler here */ },
colors = ChipDefaults.primaryChipColors(),
- border = ChipDefaults.outlinedChipBorder()
- ) {
- Text("SwipeToReveal Chip")
- }
+ border = ChipDefaults.outlinedChipBorder(),
+ label = {
+ Text("SwipeToReveal Chip", maxLines = 3)
+ }
+ )
}
}
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index 5d8af35..9e58c4c 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -136,12 +136,14 @@
}
@androidx.compose.runtime.Immutable @androidx.compose.runtime.Stable public final class ColorScheme {
- ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
- method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
+ ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceContainerLow, optional long surfaceContainer, optional long surfaceContainerHigh, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer);
+ method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceContainerLow, optional long surfaceContainer, optional long surfaceContainerHigh, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer);
method public long getBackground();
method public long getError();
+ method public long getErrorContainer();
method public long getOnBackground();
method public long getOnError();
+ method public long getOnErrorContainer();
method public long getOnPrimary();
method public long getOnPrimaryContainer();
method public long getOnSecondary();
@@ -158,16 +160,18 @@
method public long getSecondary();
method public long getSecondaryContainer();
method public long getSecondaryDim();
- method public long getSurface();
- method public long getSurfaceBright();
- method public long getSurfaceDim();
+ method public long getSurfaceContainer();
+ method public long getSurfaceContainerHigh();
+ method public long getSurfaceContainerLow();
method public long getTertiary();
method public long getTertiaryContainer();
method public long getTertiaryDim();
property public final long background;
property public final long error;
+ property public final long errorContainer;
property public final long onBackground;
property public final long onError;
+ property public final long onErrorContainer;
property public final long onPrimary;
property public final long onPrimaryContainer;
property public final long onSecondary;
@@ -184,9 +188,9 @@
property public final long secondary;
property public final long secondaryContainer;
property public final long secondaryDim;
- property public final long surface;
- property public final long surfaceBright;
- property public final long surfaceDim;
+ property public final long surfaceContainer;
+ property public final long surfaceContainerHigh;
+ property public final long surfaceContainerLow;
property public final long tertiary;
property public final long tertiaryContainer;
property public final long tertiaryDim;
@@ -344,6 +348,34 @@
property @IntRange(from=0L) public abstract int pageCount;
}
+ public final class ProgressIndicatorColors {
+ ctor public ProgressIndicatorColors(androidx.compose.ui.graphics.Brush indicatorBrush, androidx.compose.ui.graphics.Brush trackBrush);
+ method public androidx.compose.ui.graphics.Brush getIndicatorBrush();
+ method public androidx.compose.ui.graphics.Brush getTrackBrush();
+ property public final androidx.compose.ui.graphics.Brush indicatorBrush;
+ property public final androidx.compose.ui.graphics.Brush trackBrush;
+ }
+
+ public final class ProgressIndicatorDefaults {
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ProgressIndicatorColors colors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ProgressIndicatorColors colors(optional androidx.compose.ui.graphics.Brush? indicatorBrush, optional androidx.compose.ui.graphics.Brush? trackBrush);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ProgressIndicatorColors colors(optional long indicatorColor, optional long trackColor);
+ method public float gapSize(float strokeWidth);
+ method public float getButtonCircularIndicatorStrokeWidth();
+ method public float getFullScreenPadding();
+ method public float getStartAngle();
+ method public float getStrokeWidth();
+ property public final float ButtonCircularIndicatorStrokeWidth;
+ property public final float FullScreenPadding;
+ property public final float StartAngle;
+ property public final float StrokeWidth;
+ field public static final androidx.wear.compose.material3.ProgressIndicatorDefaults INSTANCE;
+ }
+
+ public final class ProgressIndicatorKt {
+ method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional androidx.wear.compose.material3.ProgressIndicatorColors colors, optional float strokeWidth, optional float gapSize);
+ }
+
@androidx.compose.runtime.Immutable public final class RadioButtonColors {
ctor public RadioButtonColors(long selectedContainerColor, long selectedContentColor, long selectedSecondaryContentColor, long selectedIconColor, long unselectedContainerColor, long unselectedContentColor, long unselectedSecondaryContentColor, long unselectedIconColor, long disabledSelectedContainerColor, long disabledSelectedContentColor, long disabledSelectedSecondaryContentColor, long disabledSelectedIconColor, long disabledUnselectedContainerColor, long disabledUnselectedContentColor, long disabledUnselectedSecondaryContentColor, long disabledUnselectedIconColor);
method public long getDisabledSelectedContainerColor();
@@ -596,7 +628,7 @@
}
public final class SwitchDefaults {
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedThumbIconColor, optional long checkedTrackColor, optional long checkedTrackBorderColor, optional long uncheckedThumbColor, optional long uncheckedThumbIconColor, optional long uncheckedTrackColor, optional long uncheckedTrackBorderColor);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedThumbIconColor, optional long checkedTrackColor, optional long checkedTrackBorderColor, optional long uncheckedThumbColor, optional long uncheckedThumbIconColor, optional long uncheckedTrackColor, optional long uncheckedTrackBorderColor, optional long disabledCheckedThumbColor, optional long disabledCheckedThumbIconColor, optional long disabledCheckedTrackColor, optional long disabledCheckedTrackBorderColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedThumbIconColor, optional long disabledUncheckedTrackColor, optional long disabledUncheckedTrackBorderColor);
field public static final androidx.wear.compose.material3.SwitchDefaults INSTANCE;
}
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index 5d8af35..9e58c4c 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -136,12 +136,14 @@
}
@androidx.compose.runtime.Immutable @androidx.compose.runtime.Stable public final class ColorScheme {
- ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
- method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
+ ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceContainerLow, optional long surfaceContainer, optional long surfaceContainerHigh, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer);
+ method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceContainerLow, optional long surfaceContainer, optional long surfaceContainerHigh, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer);
method public long getBackground();
method public long getError();
+ method public long getErrorContainer();
method public long getOnBackground();
method public long getOnError();
+ method public long getOnErrorContainer();
method public long getOnPrimary();
method public long getOnPrimaryContainer();
method public long getOnSecondary();
@@ -158,16 +160,18 @@
method public long getSecondary();
method public long getSecondaryContainer();
method public long getSecondaryDim();
- method public long getSurface();
- method public long getSurfaceBright();
- method public long getSurfaceDim();
+ method public long getSurfaceContainer();
+ method public long getSurfaceContainerHigh();
+ method public long getSurfaceContainerLow();
method public long getTertiary();
method public long getTertiaryContainer();
method public long getTertiaryDim();
property public final long background;
property public final long error;
+ property public final long errorContainer;
property public final long onBackground;
property public final long onError;
+ property public final long onErrorContainer;
property public final long onPrimary;
property public final long onPrimaryContainer;
property public final long onSecondary;
@@ -184,9 +188,9 @@
property public final long secondary;
property public final long secondaryContainer;
property public final long secondaryDim;
- property public final long surface;
- property public final long surfaceBright;
- property public final long surfaceDim;
+ property public final long surfaceContainer;
+ property public final long surfaceContainerHigh;
+ property public final long surfaceContainerLow;
property public final long tertiary;
property public final long tertiaryContainer;
property public final long tertiaryDim;
@@ -344,6 +348,34 @@
property @IntRange(from=0L) public abstract int pageCount;
}
+ public final class ProgressIndicatorColors {
+ ctor public ProgressIndicatorColors(androidx.compose.ui.graphics.Brush indicatorBrush, androidx.compose.ui.graphics.Brush trackBrush);
+ method public androidx.compose.ui.graphics.Brush getIndicatorBrush();
+ method public androidx.compose.ui.graphics.Brush getTrackBrush();
+ property public final androidx.compose.ui.graphics.Brush indicatorBrush;
+ property public final androidx.compose.ui.graphics.Brush trackBrush;
+ }
+
+ public final class ProgressIndicatorDefaults {
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ProgressIndicatorColors colors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ProgressIndicatorColors colors(optional androidx.compose.ui.graphics.Brush? indicatorBrush, optional androidx.compose.ui.graphics.Brush? trackBrush);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ProgressIndicatorColors colors(optional long indicatorColor, optional long trackColor);
+ method public float gapSize(float strokeWidth);
+ method public float getButtonCircularIndicatorStrokeWidth();
+ method public float getFullScreenPadding();
+ method public float getStartAngle();
+ method public float getStrokeWidth();
+ property public final float ButtonCircularIndicatorStrokeWidth;
+ property public final float FullScreenPadding;
+ property public final float StartAngle;
+ property public final float StrokeWidth;
+ field public static final androidx.wear.compose.material3.ProgressIndicatorDefaults INSTANCE;
+ }
+
+ public final class ProgressIndicatorKt {
+ method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional androidx.wear.compose.material3.ProgressIndicatorColors colors, optional float strokeWidth, optional float gapSize);
+ }
+
@androidx.compose.runtime.Immutable public final class RadioButtonColors {
ctor public RadioButtonColors(long selectedContainerColor, long selectedContentColor, long selectedSecondaryContentColor, long selectedIconColor, long unselectedContainerColor, long unselectedContentColor, long unselectedSecondaryContentColor, long unselectedIconColor, long disabledSelectedContainerColor, long disabledSelectedContentColor, long disabledSelectedSecondaryContentColor, long disabledSelectedIconColor, long disabledUnselectedContainerColor, long disabledUnselectedContentColor, long disabledUnselectedSecondaryContentColor, long disabledUnselectedIconColor);
method public long getDisabledSelectedContainerColor();
@@ -596,7 +628,7 @@
}
public final class SwitchDefaults {
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedThumbIconColor, optional long checkedTrackColor, optional long checkedTrackBorderColor, optional long uncheckedThumbColor, optional long uncheckedThumbIconColor, optional long uncheckedTrackColor, optional long uncheckedTrackBorderColor);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedThumbIconColor, optional long checkedTrackColor, optional long checkedTrackBorderColor, optional long uncheckedThumbColor, optional long uncheckedThumbIconColor, optional long uncheckedTrackColor, optional long uncheckedTrackBorderColor, optional long disabledCheckedThumbColor, optional long disabledCheckedThumbIconColor, optional long disabledCheckedTrackColor, optional long disabledCheckedTrackBorderColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedThumbIconColor, optional long disabledUncheckedTrackColor, optional long disabledUncheckedTrackBorderColor);
field public static final androidx.wear.compose.material3.SwitchDefaults INSTANCE;
}
diff --git a/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ColorSchemeBenchmark.kt b/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ColorSchemeBenchmark.kt
index cdbce14..2e6efe4 100644
--- a/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ColorSchemeBenchmark.kt
+++ b/wear/compose/compose-material3/benchmark/src/androidTest/java/androidx/wear/compose/material3/benchmark/ColorSchemeBenchmark.kt
@@ -135,10 +135,14 @@
)
// Surface
- Box(modifier = Modifier.size(1.dp).background(MaterialTheme.colorScheme.surface))
- Box(
- modifier = Modifier.size(1.dp).background(
- MaterialTheme.colorScheme.contentColorFor(MaterialTheme.colorScheme.surface)
+ Box(modifier = Modifier
+ .size(1.dp)
+ .background(MaterialTheme.colorScheme.surfaceContainer))
+ Box(modifier = Modifier
+ .size(1.dp)
+ .background(
+ MaterialTheme.colorScheme
+ .contentColorFor(MaterialTheme.colorScheme.surfaceContainer)
)
)
diff --git a/wear/compose/compose-material3/build.gradle b/wear/compose/compose-material3/build.gradle
index d18ada1..a6c3a81 100644
--- a/wear/compose/compose-material3/build.gradle
+++ b/wear/compose/compose-material3/build.gradle
@@ -78,7 +78,7 @@
androidx {
name = "Android Wear Compose Material 3"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
mavenVersion = LibraryVersions.WEAR_COMPOSE_MATERIAL3
inceptionYear = "2022"
description = "WearOS Compose Material 3 Library. This library makes it easier for " +
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
index fa15794..0518e7a 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
@@ -26,7 +26,7 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.ButtonColors
import androidx.wear.compose.material3.ButtonDefaults
@@ -51,7 +51,7 @@
@Composable
fun ButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
@@ -98,7 +98,7 @@
@Composable
fun FilledTonalButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
@@ -145,7 +145,7 @@
@Composable
fun OutlinedButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
@@ -192,7 +192,7 @@
@Composable
fun ChildButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
@@ -239,7 +239,7 @@
@Composable
fun CompactButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
@@ -341,7 +341,7 @@
@Composable
fun MultilineButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
@@ -384,7 +384,7 @@
@Composable
fun AvatarButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/CardDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/CardDemo.kt
index 19c409d..9180758 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/CardDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/CardDemo.kt
@@ -30,7 +30,7 @@
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.AppCard
import androidx.wear.compose.material3.CardDefaults
import androidx.wear.compose.material3.ListHeader
@@ -48,7 +48,7 @@
@Composable
fun CardDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt
index fcb7c22..8ecebb0 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt
@@ -25,7 +25,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.FilledIconButton
import androidx.wear.compose.material3.FilledTonalIconButton
@@ -42,7 +42,7 @@
@Composable
fun IconButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconToggleButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconToggleButtonDemo.kt
index 2216bdd..86ac15d 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconToggleButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconToggleButtonDemo.kt
@@ -33,7 +33,7 @@
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.IconButtonDefaults
import androidx.wear.compose.material3.IconToggleButton
@@ -44,7 +44,7 @@
@Composable
fun IconToggleButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ListHeaderDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ListHeaderDemo.kt
index 7488d61..f5094c4 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ListHeaderDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ListHeaderDemo.kt
@@ -19,14 +19,14 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.samples.ListHeaderSample
import androidx.wear.compose.material3.samples.ListSubheaderSample
import androidx.wear.compose.material3.samples.ListSubheaderWithIconSample
@Composable
fun ListHeaderDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxWidth()
) {
item {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt
new file mode 100644
index 0000000..25a62eb
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024 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.wear.compose.material3.demos
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.integration.demos.common.Centralize
+import androidx.wear.compose.integration.demos.common.ComposableDemo
+import androidx.wear.compose.integration.demos.common.DemoCategory
+import androidx.wear.compose.material3.Button
+import androidx.wear.compose.material3.CircularProgressIndicator
+import androidx.wear.compose.material3.IconButtonDefaults
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.ProgressIndicatorDefaults
+import androidx.wear.compose.material3.ProgressIndicatorDefaults.ButtonCircularIndicatorStrokeWidth
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.samples.FullScreenProgressIndicatorSample
+import androidx.wear.compose.material3.samples.MediaButtonProgressIndicatorSample
+import androidx.wear.compose.material3.samples.OverflowProgressIndicatorSample
+
+val ProgressIndicatorDemos = listOf(
+ DemoCategory(
+ "Samples",
+ listOf(
+ ComposableDemo("Full screen") {
+ Centralize {
+ FullScreenProgressIndicatorSample()
+ }
+ },
+ ComposableDemo("Media button wrapping") {
+ Centralize {
+ MediaButtonProgressIndicatorSample()
+ }
+ },
+ ComposableDemo("Overflow progress (>100%)") {
+ Centralize {
+ OverflowProgressIndicatorSample()
+ }
+ },
+ ComposableDemo("Small sized indicator") {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.background)
+ ) {
+ Button({ /* No op */ }, modifier = Modifier.align(Alignment.Center)) {
+ Text(
+ "Loading...",
+ modifier = Modifier.align(Alignment.CenterVertically))
+ Spacer(modifier = Modifier.size(10.dp))
+ CircularProgressIndicator(
+ progress = { 0.75f },
+ modifier = Modifier.size(IconButtonDefaults.DefaultButtonSize),
+ startAngle = 120f,
+ endAngle = 60f,
+ strokeWidth = ButtonCircularIndicatorStrokeWidth,
+ colors = ProgressIndicatorDefaults.colors(indicatorColor = Color.Red)
+ )
+ }
+ }
+ }
+ )
+ )
+)
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/RadioButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/RadioButtonDemo.kt
index fd546275..cee65ef 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/RadioButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/RadioButtonDemo.kt
@@ -31,7 +31,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.RadioButton
@@ -40,7 +40,7 @@
@Composable
fun RadioButtonDemo() {
var selectedRadioIndex by remember { mutableIntStateOf(0) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize().selectableGroup(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SelectionControlsDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SelectionControlsDemo.kt
index 0f8bf1b..5e47afb 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SelectionControlsDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SelectionControlsDemo.kt
@@ -26,7 +26,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.RadioButton
import androidx.wear.compose.material3.Text
@@ -34,7 +34,7 @@
@Composable
fun RadioDemos() {
var selectedIndex by remember { mutableIntStateOf(0) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally,
) {
item {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SettingsDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SettingsDemo.kt
index dc81baf..f0415ce 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SettingsDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SettingsDemo.kt
@@ -22,8 +22,8 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.Icon
@@ -34,7 +34,7 @@
fun SettingsDemo() {
// TODO: Add Scaffold and TimeText when available
val scalingLazyListState = rememberScalingLazyListState()
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
state = scalingLazyListState,
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SliderDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SliderDemo.kt
index 1597b69..feab507 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SliderDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SliderDemo.kt
@@ -34,10 +34,10 @@
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.integration.demos.common.Centralize
import androidx.wear.compose.integration.demos.common.ComposableDemo
import androidx.wear.compose.integration.demos.common.DemoCategory
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
import androidx.wear.compose.material3.ExperimentalWearMaterial3Api
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.InlineSlider
@@ -90,7 +90,7 @@
var enabledValue by remember { mutableFloatStateOf(5f) }
var disabledValue by remember { mutableFloatStateOf(5f) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(
space = 4.dp,
@@ -134,7 +134,7 @@
var valueWithoutSegments by remember { mutableIntStateOf(5) }
var valueWithSegments by remember { mutableIntStateOf(5) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(
space = 4.dp,
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SplitRadioButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SplitRadioButtonDemo.kt
index b34aba8..8327371 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SplitRadioButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SplitRadioButtonDemo.kt
@@ -29,7 +29,7 @@
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.SplitRadioButton
import androidx.wear.compose.material3.Text
@@ -37,7 +37,7 @@
@Composable
fun SplitRadioButtonDemo() {
var selectedRadioIndex by remember { mutableIntStateOf(0) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SplitToggleButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SplitToggleButtonDemo.kt
index 108386c..62e3461 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SplitToggleButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SplitToggleButtonDemo.kt
@@ -29,7 +29,7 @@
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.Checkbox
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.SplitToggleButton
@@ -38,7 +38,7 @@
@Composable
fun SplitToggleButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt
index c6e0246..807cd0f 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt
@@ -26,7 +26,7 @@
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.MaterialTheme
@@ -42,7 +42,7 @@
@Composable
fun TextButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextToggleButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextToggleButtonDemo.kt
index 76d66b5..955f9a8 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextToggleButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextToggleButtonDemo.kt
@@ -30,7 +30,7 @@
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.TextButtonDefaults
@@ -41,7 +41,7 @@
@Composable
fun TextToggleButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ToggleButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ToggleButtonDemo.kt
index fc7d844..fc3460f 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ToggleButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ToggleButtonDemo.kt
@@ -30,7 +30,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.Checkbox
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.ListHeader
@@ -40,7 +40,7 @@
@Composable
fun ToggleButtonDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ToggleControlsDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ToggleControlsDemo.kt
index 3dcdcc6..f075f0f 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ToggleControlsDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ToggleControlsDemo.kt
@@ -28,7 +28,7 @@
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material3.Checkbox
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.Switch
@@ -37,7 +37,7 @@
@Composable
fun CheckboxDemos() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally,
) {
item {
@@ -63,7 +63,7 @@
@Composable
fun SwitchDemos() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally,
) {
item {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
index 854da28..d9d5a7d 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
@@ -125,6 +125,10 @@
"Slider",
SliderDemos
),
+ DemoCategory(
+ "Progress Indicator",
+ ProgressIndicatorDemos
+ ),
ComposableDemo(
title = "Fixed Font Size"
) {
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt
new file mode 100644
index 0000000..74e3487
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.PlayArrow
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material3.CircularProgressIndicator
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.IconButton
+import androidx.wear.compose.material3.IconButtonDefaults
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.ProgressIndicatorDefaults
+import androidx.wear.compose.material3.ProgressIndicatorDefaults.FullScreenPadding
+
+@Sampled
+@Composable
+fun FullScreenProgressIndicatorSample() {
+ Box(
+ modifier =
+ Modifier
+ .background(MaterialTheme.colorScheme.background)
+ .padding(FullScreenPadding)
+ .fillMaxSize()
+ ) {
+ CircularProgressIndicator(
+ progress = { 0.25f },
+ startAngle = 120f,
+ endAngle = 60f,
+ colors = ProgressIndicatorDefaults.colors(
+ indicatorColor = Color.Green,
+ trackColor = Color.Green.copy(alpha = 0.5f)
+ ),
+ )
+ }
+}
+
+@Sampled
+@Composable
+fun MediaButtonProgressIndicatorSample() {
+ var isPlaying by remember {
+ mutableStateOf(false)
+ }
+ val progressPadding = 4.dp
+
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.background)
+ ) {
+
+ Box(
+ modifier = Modifier
+ .align(Alignment.Center)
+ .size(IconButtonDefaults.DefaultButtonSize + progressPadding)
+ ) {
+ CircularProgressIndicator(progress = { 0.75f }, strokeWidth = progressPadding)
+ IconButton(
+ modifier = Modifier
+ .align(Alignment.Center)
+ .padding(progressPadding)
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.surfaceContainerLow),
+ onClick = { isPlaying = !isPlaying }
+ ) {
+ Icon(
+ imageVector = if (isPlaying) Icons.Filled.Close else Icons.Filled.PlayArrow,
+ contentDescription = "Play/pause button icon"
+ )
+ }
+ }
+ }
+}
+
+@Sampled
+@Composable
+fun OverflowProgressIndicatorSample() {
+ Box(
+ modifier =
+ Modifier
+ .background(MaterialTheme.colorScheme.background)
+ .padding(FullScreenPadding)
+ .fillMaxSize()
+ ) {
+ CircularProgressIndicator(
+ // The progress is limited by 100%, 120% ends up being 20% with the track brush
+ // indicating overflow.
+ progress = { 0.2f },
+ startAngle = 120f,
+ endAngle = 60f,
+ colors = ProgressIndicatorDefaults.colors(
+ trackBrush = Brush.linearGradient(
+ listOf(
+ MaterialTheme.colorScheme.primary,
+ MaterialTheme.colorScheme.surfaceContainer
+ )
+ )
+ )
+ )
+ }
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SwipeToDismissBoxSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SwipeToDismissBoxSample.kt
index 2058328..9a49f2e 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SwipeToDismissBoxSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SwipeToDismissBoxSample.kt
@@ -127,7 +127,7 @@
modifier = Modifier
.height(40.dp)
.background(
- color = MaterialTheme.colorScheme.surface,
+ color = MaterialTheme.colorScheme.surfaceContainer,
shape = CircleShape
)
.padding(horizontal = 12.dp),
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
index ed0430c..a7e67ae 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
@@ -400,7 +400,7 @@
fun gives_enabled_filled_tonal_base_button_correct_colors() {
rule.verifyButtonColors(
status = Status.Enabled,
- expectedContainerColor = { MaterialTheme.colorScheme.surface },
+ expectedContainerColor = { MaterialTheme.colorScheme.surfaceContainer },
expectedContentColor = { MaterialTheme.colorScheme.onSurface },
content = { FilledTonalButton(Status.Enabled) }
)
@@ -1049,7 +1049,7 @@
val padding = 0.dp
setContentWithTheme {
- background = MaterialTheme.colorScheme.surface
+ background = MaterialTheme.colorScheme.surfaceContainer
Box(Modifier.background(background)) {
buttonColor = (colors().containerPainter(true) as ColorPainter).color
if (buttonColor == Color.Transparent) {
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
index d9d875a..4c33129 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
@@ -398,7 +398,7 @@
rule.verifyIconButtonColors(
status = Status.Enabled,
colors = { IconButtonDefaults.filledTonalIconButtonColors() },
- expectedContainerColor = { MaterialTheme.colorScheme.surface },
+ expectedContainerColor = { MaterialTheme.colorScheme.surfaceContainer },
expectedContentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
)
}
@@ -536,7 +536,7 @@
val padding = 0.dp
setContentWithTheme {
- background = MaterialTheme.colorScheme.surface
+ background = MaterialTheme.colorScheme.surfaceContainer
Box(Modifier.background(background)) {
buttonColor = colors().containerColor(true)
if (buttonColor == Color.Transparent) {
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
index e8d1930..8d6e1bc 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
@@ -414,7 +414,7 @@
status = Status.Enabled,
checked = false,
colors = { IconButtonDefaults.iconToggleButtonColors() },
- containerColor = { MaterialTheme.colorScheme.surface },
+ containerColor = { MaterialTheme.colorScheme.surfaceContainer },
contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
)
@@ -425,7 +425,7 @@
status = Status.Disabled,
checked = false,
colors = { IconButtonDefaults.iconToggleButtonColors() },
- containerColor = { MaterialTheme.colorScheme.surface.toDisabledColor() },
+ containerColor = { MaterialTheme.colorScheme.surfaceContainer.toDisabledColor() },
contentColor = { MaterialTheme.colorScheme.onSurfaceVariant.toDisabledColor() }
)
@@ -507,7 +507,7 @@
uncheckedContentColor = overrideColor
)
},
- containerColor = { MaterialTheme.colorScheme.surface },
+ containerColor = { MaterialTheme.colorScheme.surfaceContainer },
contentColor = { overrideColor }
)
}
@@ -584,7 +584,7 @@
)
},
contentColor = { overrideColor },
- containerColor = { MaterialTheme.colorScheme.surface.toDisabledColor() }
+ containerColor = { MaterialTheme.colorScheme.surfaceContainer.toDisabledColor() }
)
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorScreenshotTest.kt
new file mode 100644
index 0000000..5f45b38
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorScreenshotTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2024 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.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.PlayArrow
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class ProgressIndicatorScreenshotTest {
+ @get:Rule
+ val rule = createComposeRule()
+
+ @get:Rule
+ val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+ @get:Rule
+ val testName = TestName()
+
+ @Test
+ fun progress_indicator_fullscreen() = verifyScreenshot {
+ CircularProgressIndicator(
+ progress = { 0.25f },
+ modifier = Modifier
+ .aspectRatio(1f)
+ .testTag(TEST_TAG),
+ startAngle = 120f,
+ endAngle = 60f,
+ )
+ }
+
+ @Test
+ fun progress_indicator_custom_color() = verifyScreenshot {
+ CircularProgressIndicator(
+ progress = { 0.75f },
+ modifier = Modifier
+ .size(200.dp)
+ .testTag(TEST_TAG),
+ startAngle = 120f,
+ endAngle = 60f,
+ colors = ProgressIndicatorDefaults.colors(
+ indicatorColor = Color.Green,
+ trackColor = Color.Red.copy(alpha = 0.5f)
+ )
+ )
+ }
+
+ @Test
+ fun progress_indicator_wrapping_media_button() = verifyScreenshot {
+ val progressPadding = 4.dp
+ Box(
+ modifier = Modifier
+ .size(IconButtonDefaults.DefaultButtonSize + progressPadding)
+ .testTag(TEST_TAG)
+ ) {
+ CircularProgressIndicator(progress = { 0.75f }, strokeWidth = progressPadding)
+ IconButton(
+ modifier = Modifier
+ .align(Alignment.Center)
+ .padding(progressPadding)
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.surfaceContainer),
+ onClick = { }
+ ) {
+ Icon(
+ imageVector = Icons.Filled.PlayArrow,
+ contentDescription = "Play/pause button icon"
+ )
+ }
+ }
+ }
+
+ @Test
+ fun progress_indicator_overflow() = verifyScreenshot {
+ CircularProgressIndicator(
+ progress = { 0.2f },
+ modifier = Modifier
+ .aspectRatio(1f)
+ .testTag(TEST_TAG),
+ startAngle = 120f,
+ endAngle = 60f,
+ colors = ProgressIndicatorDefaults.colors(
+ trackBrush = Brush.linearGradient(
+ listOf(
+ MaterialTheme.colorScheme.surfaceContainer,
+ MaterialTheme.colorScheme.primary
+ )
+ )
+ )
+ )
+ }
+
+ private fun verifyScreenshot(content: @Composable () -> Unit) {
+ rule.setContentWithTheme {
+ CompositionLocalProvider(
+ LocalLayoutDirection provides LayoutDirection.Ltr,
+ content = content
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertAgainstGolden(screenshotRule, testName.methodName)
+ }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt
new file mode 100644
index 0000000..fa6cb56
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2024 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.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.testutils.assertDoesNotContainColor
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.ProgressBarRangeInfo
+import androidx.compose.ui.test.assertRangeInfoEquals
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.SdkSuppress
+import org.junit.Rule
+import org.junit.Test
+
+class CircularProgressIndicatorTest {
+
+ @get:Rule
+ val rule = createComposeRule()
+
+ @Test
+ fun supports_testtag() {
+ setContentWithTheme {
+ CircularProgressIndicator(progress = { 0.5f }, modifier = Modifier.testTag(TEST_TAG))
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun changes_progress() {
+ val progress = mutableStateOf(0f)
+
+ setContentWithTheme {
+ CircularProgressIndicator(
+ modifier = Modifier.testTag(TEST_TAG),
+ progress = { progress.value }
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG)
+ .assertRangeInfoEquals(ProgressBarRangeInfo(0f, 0f..1f))
+
+ rule.runOnIdle { progress.value = 0.5f }
+
+ rule.onNodeWithTag(TEST_TAG)
+ .assertRangeInfoEquals(ProgressBarRangeInfo(0.5f, 0f..1f))
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun contains_progress_color() {
+ setContentWithTheme {
+ CircularProgressIndicator(
+ modifier = Modifier.testTag(TEST_TAG),
+ progress = { 1f },
+ colors = ProgressIndicatorDefaults.colors(
+ indicatorColor = Color.Yellow,
+ trackColor = Color.Red
+ ),
+ )
+ }
+ rule.waitForIdle()
+ // by default fully filled progress approximately takes 25% of the control.
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Yellow, 23f..27f)
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertDoesNotContainColor(Color.Red)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun contains_progress_incomplete_color() {
+ setContentWithTheme {
+ CircularProgressIndicator(
+ modifier = Modifier.testTag(TEST_TAG),
+ progress = { 0f },
+ colors = ProgressIndicatorDefaults.colors(
+ indicatorColor = Color.Yellow,
+ trackColor = Color.Red
+ ),
+ )
+ }
+ rule.waitForIdle()
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertDoesNotContainColor(Color.Yellow)
+ // by default progress track approximately takes 25% of the control.
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Red, 23f..27f)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun change_start_end_angle() {
+ setContentWithTheme {
+ CircularProgressIndicator(
+ modifier = Modifier.testTag(TEST_TAG),
+ progress = { 0.5f },
+ startAngle = 0f,
+ endAngle = 180f,
+ colors = ProgressIndicatorDefaults.colors(
+ indicatorColor = Color.Yellow,
+ trackColor = Color.Red
+ ),
+ )
+ }
+ rule.waitForIdle()
+ // Color should take approximately a quarter of what it normally takes
+ // (a little bit less), eg 25% / 4 ≈ 6%.
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Yellow, 4f..8f)
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Red, 4f..8f)
+ }
+
+ @Test
+ fun coerces_highest_out_of_bound_progress() {
+ val progress = mutableStateOf(0f)
+
+ setContentWithTheme {
+ CircularProgressIndicator(
+ modifier = Modifier.testTag(TEST_TAG),
+ progress = { progress.value }
+ )
+ }
+
+ rule.runOnIdle { progress.value = 1.5f }
+
+ rule.onNodeWithTag(TEST_TAG)
+ .assertRangeInfoEquals(ProgressBarRangeInfo(1f, 0f..1f))
+ }
+
+ @Test
+ fun coerces_lowest_out_of_bound_progress() {
+ val progress = mutableStateOf(0f)
+
+ setContentWithTheme {
+ CircularProgressIndicator(
+ modifier = Modifier.testTag(TEST_TAG),
+ progress = { progress.value }
+ )
+ }
+
+ rule.runOnIdle { progress.value = -1.5f }
+
+ rule.onNodeWithTag(TEST_TAG)
+ .assertRangeInfoEquals(ProgressBarRangeInfo(0f, 0f..1f))
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun set_small_progress_value() {
+ setContentWithTheme {
+ CircularProgressIndicator(
+ modifier = Modifier.testTag(TEST_TAG),
+ progress = { 0.05f },
+ colors = ProgressIndicatorDefaults.colors(
+ indicatorColor = Color.Yellow,
+ trackColor = Color.Red
+ ),
+ )
+ }
+ rule.waitForIdle()
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Yellow, 0.5f..1f)
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Red, 23f..27f)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun set_small_stroke_width() {
+ setContentWithTheme {
+ CircularProgressIndicator(
+ modifier = Modifier.testTag(TEST_TAG),
+ progress = { 0.5f },
+ strokeWidth = 4.dp,
+ colors = ProgressIndicatorDefaults.colors(
+ indicatorColor = Color.Yellow,
+ trackColor = Color.Red
+ ),
+ )
+ }
+ rule.waitForIdle()
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Yellow, 2f..6f)
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Red, 2f..6f)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun set_large_stroke_width() {
+ setContentWithTheme {
+ CircularProgressIndicator(
+ modifier = Modifier.testTag(TEST_TAG),
+ progress = { 0.5f },
+ strokeWidth = 36.dp,
+ colors = ProgressIndicatorDefaults.colors(
+ indicatorColor = Color.Yellow,
+ trackColor = Color.Red
+ ),
+ )
+ }
+ rule.waitForIdle()
+ // Because of the stroke cap, progress color takes same amount as track color.
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Yellow, 20f..25f)
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertColorInPercentageRange(Color.Red, 20f..25f)
+ }
+
+ private fun setContentWithTheme(composable: @Composable BoxScope.() -> Unit) {
+ // Use constant size modifier to limit relative color percentage ranges.
+ rule.setContentWithTheme(modifier = Modifier.size(204.dp), composable = composable)
+ }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsScreenshotTest.kt
index 9a176be..bb5a14a 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsScreenshotTest.kt
@@ -99,6 +99,6 @@
.background(
MaterialTheme.colorScheme.primary
.copy(alpha = 0.5f)
- .compositeOver(MaterialTheme.colorScheme.surface)
+ .compositeOver(MaterialTheme.colorScheme.surfaceContainer)
)
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
index 87e1a71..f00b3af 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
@@ -387,7 +387,7 @@
rule.verifyTextButtonColors(
status = Status.Enabled,
colors = { TextButtonDefaults.filledTonalTextButtonColors() },
- expectedContainerColor = { MaterialTheme.colorScheme.surface },
+ expectedContainerColor = { MaterialTheme.colorScheme.surfaceContainer },
expectedContentColor = { MaterialTheme.colorScheme.onSurface }
)
}
@@ -530,7 +530,7 @@
val padding = 0.dp
setContentWithTheme {
- background = MaterialTheme.colorScheme.surface
+ background = MaterialTheme.colorScheme.surfaceContainer
Box(Modifier.background(background)) {
buttonColor = colors().containerColor(true)
if (buttonColor == Color.Transparent) {
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
index d1202bbb..08d4b07 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
@@ -404,7 +404,7 @@
status = Status.Enabled,
checked = false,
colors = { TextButtonDefaults.textToggleButtonColors() },
- containerColor = { MaterialTheme.colorScheme.surface },
+ containerColor = { MaterialTheme.colorScheme.surfaceContainer },
contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
)
@@ -415,7 +415,7 @@
status = Status.Disabled,
checked = false,
colors = { TextButtonDefaults.textToggleButtonColors() },
- containerColor = { MaterialTheme.colorScheme.surface.toDisabledColor() },
+ containerColor = { MaterialTheme.colorScheme.surfaceContainer.toDisabledColor() },
contentColor = { MaterialTheme.colorScheme.onSurfaceVariant.toDisabledColor() }
)
@@ -497,7 +497,7 @@
uncheckedContentColor = override
)
},
- containerColor = { MaterialTheme.colorScheme.surface },
+ containerColor = { MaterialTheme.colorScheme.surfaceContainer },
contentColor = { override }
)
}
@@ -584,7 +584,7 @@
},
contentColor = { override },
containerColor = {
- MaterialTheme.colorScheme.surface.toDisabledColor()
+ MaterialTheme.colorScheme.surfaceContainer.toDisabledColor()
}
)
}
@@ -671,6 +671,7 @@
}
)
}
+
@RequiresApi(Build.VERSION_CODES.O)
private fun ComposeContentTestRule.isShape(
shape: Shape = CircleShape,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleButtonTest.kt
index 95f5df1..5f43605 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleButtonTest.kt
@@ -816,7 +816,7 @@
checked: Boolean
): Color {
return if (checked) MaterialTheme.colorScheme.primaryContainer
- else MaterialTheme.colorScheme.surface
+ else MaterialTheme.colorScheme.surfaceContainer
}
@Composable
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleControlsScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleControlsScreenshotTest.kt
index 14cdb66..85d3f0e 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleControlsScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleControlsScreenshotTest.kt
@@ -174,6 +174,6 @@
.background(
MaterialTheme.colorScheme.primary
.copy(alpha = 0.5f)
- .compositeOver(MaterialTheme.colorScheme.surface)
+ .compositeOver(MaterialTheme.colorScheme.surfaceContainer)
)
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleControlsTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleControlsTest.kt
index 253e171..4755838 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleControlsTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleControlsTest.kt
@@ -142,7 +142,7 @@
Switch(modifier = Modifier.testTag(TEST_TAG))
}
}
- .assertHeightIsEqualTo(TOGGLE_CONTROL_HEIGHT)
+ .assertHeightIsEqualTo(SWITCH_CONTROL_HEIGHT)
.assertWidthIsEqualTo(TOGGLE_CONTROL_WIDTH)
}
@@ -234,4 +234,5 @@
}
private val TOGGLE_CONTROL_WIDTH = 32.dp
+private val SWITCH_CONTROL_HEIGHT = 22.dp
private val TOGGLE_CONTROL_HEIGHT = 24.dp
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
index 9b830dc..991d893 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
@@ -28,53 +28,56 @@
/**
* A [ColorScheme] holds all the named color parameters for a [MaterialTheme].
*
- * Color schemes are designed to be harmonious, ensure accessible text, and distinguish UI
- * elements and surfaces from one another.
+ * Color schemes are designed to be harmonious, ensure accessible text, and distinguish UI elements
+ * and surfaces from one another.
*
* The Material color system and custom schemes provide default values for color as a starting point
* for customization.
*
- * To learn more about color schemes,
- * see [Material Design Color System](https://m3.material.io/styles/color/the-color-system/color-roles).
+ * To learn more about color schemes, see
+ * [Material Design Color System](https://m3.material.io/styles/color/the-color-system/color-roles).
*
* @property primary The primary color is the color displayed most frequently across your app’s
- * screens and components.
+ * screens and components.
* @property primaryDim is less prominent than [primary] for component backgrounds
* @property primaryContainer is a standout container color for key components.
* @property onPrimary Color used for text and icons displayed on top of the primary color.
* @property onPrimaryContainer The color (and state variants) that should be used for content on
- * top of [primaryContainer].
+ * top of [primaryContainer].
* @property secondary The secondary color provides more ways to accent and distinguish your
- * product.
+ * product.
* @property secondaryDim is less prominent than [secondary] for component backgrounds.
* @property secondaryContainer A tonal color to be used in containers.
* @property onSecondary Color used for text and icons displayed on top of the secondary color.
* @property onSecondaryContainer The color (and state variants) that should be used for content on
- * top of [secondaryContainer].
- * @property tertiary The tertiary color that can be used to balance primary and secondary
- * colors, or bring heightened attention to an element.
- * @property tertiaryDim A less prominent tertiary color that can be used to balance
- * primary and secondary colors, or bring heightened attention to an element.
+ * top of [secondaryContainer].
+ * @property tertiary The tertiary color that can be used to balance primary and secondary colors,
+ * or bring heightened attention to an element.
+ * @property tertiaryDim A less prominent tertiary color that can be used to balance primary and
+ * secondary colors, or bring heightened attention to an element.
* @property tertiaryContainer A tonal color to be used in containers.
* @property onTertiary Color used for text and icons displayed on top of the tertiary color.
* @property onTertiaryContainer The color (and state variants) that should be used for content on
- * top of [tertiaryContainer].
- * @property surfaceDim A surface color used for large containment components
- * such as Card and Button with low prominence.
- * @property surface The main surface color that affect surfaces of components with large
- * containment areas, such as Card and Button.
- * @property surfaceBright A surface color used for large containment components
- * such Card and Button with high prominence.
+ * top of [tertiaryContainer].
+ * @property surfaceContainerLow A surface color used for large containment components such as Card
+ * and Button with low prominence.
+ * @property surfaceContainer The main surface color that affect surfaces of components with large
+ * containment areas, such as Card and Button.
+ * @property surfaceContainerHigh A surface color used for large containment components such Card
+ * and Button with high prominence.
* @property onSurface Color used for text and icons displayed on top of the surface color.
- * @property onSurfaceVariant The color for secondary text and icons on top of
- * [surface].
- * @property outline The main color for primary outline components.
- * The outline color role adds contrast for accessibility purposes.
+ * @property onSurfaceVariant The color for secondary text and icons on top of [surfaceContainer].
+ * @property outline The main color for primary outline components. The outline color role adds
+ * contrast for accessibility purposes.
* @property outlineVariant The secondary color for secondary outline components.
* @property background The background color that appears behind other content.
* @property onBackground Color used for text and icons displayed on top of the background color.
- * @property error The error color is used to indicate errors.
+ * @property error Color that indicates remove, delete, close or dismiss actions, such as Swipe to
+ * Reveal. Added as an errorContainer alternative that is slightly less alarming and urgent color.
* @property onError Color used for text and icons displayed on top of the error color.
+ * @property errorContainer Color that indicates errors or emergency actions, such as safety alerts.
+ * This color is for use-cases that are more alarming and urgent than the error color.
+ * @property onErrorContainer Color used for text and icons on the errorContainer color.
*/
@Immutable
@Stable
@@ -94,9 +97,9 @@
val tertiaryContainer: Color = ColorTokens.TertiaryContainer,
val onTertiary: Color = ColorTokens.OnTertiary,
val onTertiaryContainer: Color = ColorTokens.OnTertiaryContainer,
- val surfaceDim: Color = ColorTokens.SurfaceDim,
- val surface: Color = ColorTokens.Surface,
- val surfaceBright: Color = ColorTokens.SurfaceBright,
+ val surfaceContainerLow: Color = ColorTokens.SurfaceContainerLow,
+ val surfaceContainer: Color = ColorTokens.SurfaceContainer,
+ val surfaceContainerHigh: Color = ColorTokens.SurfaceContainerHigh,
val onSurface: Color = ColorTokens.OnSurface,
val onSurfaceVariant: Color = ColorTokens.OnSurfaceVariant,
val outline: Color = ColorTokens.Outline,
@@ -105,10 +108,10 @@
val onBackground: Color = ColorTokens.OnBackground,
val error: Color = ColorTokens.Error,
val onError: Color = ColorTokens.OnError,
+ val errorContainer: Color = ColorTokens.ErrorContainer,
+ val onErrorContainer: Color = ColorTokens.OnErrorContainer,
) {
- /**
- * Returns a copy of this Colors, optionally overriding some of the values.
- */
+ /** Returns a copy of this Colors, optionally overriding some of the values. */
fun copy(
primary: Color = this.primary,
primaryDim: Color = this.primaryDim,
@@ -125,9 +128,9 @@
tertiaryContainer: Color = this.tertiaryContainer,
onTertiary: Color = this.onTertiary,
onTertiaryContainer: Color = this.onTertiaryContainer,
- surfaceDim: Color = this.surfaceDim,
- surface: Color = this.surface,
- surfaceBright: Color = this.surfaceBright,
+ surfaceContainerLow: Color = this.surfaceContainerLow,
+ surfaceContainer: Color = this.surfaceContainer,
+ surfaceContainerHigh: Color = this.surfaceContainerHigh,
onSurface: Color = this.onSurface,
onSurfaceVariant: Color = this.onSurfaceVariant,
outline: Color = this.outline,
@@ -135,35 +138,40 @@
background: Color = this.background,
onBackground: Color = this.onBackground,
error: Color = this.error,
- onError: Color = this.onError
- ): ColorScheme = ColorScheme(
- primary = primary,
- primaryDim = primaryDim,
- primaryContainer = primaryContainer,
- onPrimary = onPrimary,
- onPrimaryContainer = onPrimaryContainer,
- secondary = secondary,
- secondaryDim = secondaryDim,
- secondaryContainer = secondaryContainer,
- onSecondary = onSecondary,
- onSecondaryContainer = onSecondaryContainer,
- tertiary = tertiary,
- tertiaryDim = tertiaryDim,
- tertiaryContainer = tertiaryContainer,
- onTertiary = onTertiary,
- onTertiaryContainer = onTertiaryContainer,
- surfaceDim = surfaceDim,
- surface = surface,
- surfaceBright = surfaceBright,
- onSurface = onSurface,
- onSurfaceVariant = onSurfaceVariant,
- outline = outline,
- outlineVariant = outlineVariant,
- background = background,
- onBackground = onBackground,
- error = error,
- onError = onError
- )
+ onError: Color = this.onError,
+ errorContainer: Color = this.errorContainer,
+ onErrorContainer: Color = this.onErrorContainer,
+ ): ColorScheme =
+ ColorScheme(
+ primary = primary,
+ primaryDim = primaryDim,
+ primaryContainer = primaryContainer,
+ onPrimary = onPrimary,
+ onPrimaryContainer = onPrimaryContainer,
+ secondary = secondary,
+ secondaryDim = secondaryDim,
+ secondaryContainer = secondaryContainer,
+ onSecondary = onSecondary,
+ onSecondaryContainer = onSecondaryContainer,
+ tertiary = tertiary,
+ tertiaryDim = tertiaryDim,
+ tertiaryContainer = tertiaryContainer,
+ onTertiary = onTertiary,
+ onTertiaryContainer = onTertiaryContainer,
+ surfaceContainerLow = surfaceContainerLow,
+ surfaceContainer = surfaceContainer,
+ surfaceContainerHigh = surfaceContainerHigh,
+ onSurface = onSurface,
+ onSurfaceVariant = onSurfaceVariant,
+ outline = outline,
+ outlineVariant = outlineVariant,
+ background = background,
+ onBackground = onBackground,
+ error = error,
+ onError = onError,
+ errorContainer = errorContainer,
+ onErrorContainer = onErrorContainer,
+ )
override fun toString(): String {
return "Colors(" +
@@ -182,17 +190,18 @@
"tertiaryContainer=$tertiaryContainer, " +
"onTertiary=$onTertiary, " +
"onTertiaryContainer=$onTertiaryContainer, " +
- "surfaceDim=$surfaceDim, " +
- "surface=$surface, " +
- "surfaceBright=$surfaceBright, " +
+ "surfaceContainerLow=$surfaceContainerLow, " +
+ "surfaceContainer=$surfaceContainer, " +
+ "surfaceContainerHigh=$surfaceContainerHigh, " +
"onSurface=$onSurface, " +
"onSurfaceVariant=$onSurfaceVariant, " +
"outline=$outline, " +
"outlineVariant=$outlineVariant, " +
"background=$background, " +
"onBackground=$onBackground, " +
- "error=$error, " +
- "onError=$onError" +
+ "onError=$onError," +
+ "errorContainer=$errorContainer, " +
+ "onErrorContainer=$onErrorContainer" +
")"
}
@@ -226,11 +235,14 @@
// Radio Button
internal var defaultRadioButtonColorsCached: RadioButtonColors? = null
internal var defaultSplitRadioButtonColorsCached: SplitRadioButtonColors? = null
+
+ // Progress Indicator
+ internal var defaultProgressIndicatorColorsCached: ProgressIndicatorColors? = null
}
/**
- * The Material color system contains pairs of colors that are typically used for the background
- * and content color inside a component. For example, a Button typically uses `primary` for its
+ * The Material color system contains pairs of colors that are typically used for the background and
+ * content color inside a component. For example, a Button typically uses `primary` for its
* background, and `onPrimary` for the color of its content (usually text or iconography).
*
* This function tries to match the provided [backgroundColor] to a 'background' color in this
@@ -241,53 +253,56 @@
* [Color.Unspecified].
*
* @return the matching content color for [backgroundColor]. If [backgroundColor] is not present in
- * the theme's [ColorScheme], then returns [Color.Unspecified].
- *
+ * the theme's [ColorScheme], then returns [Color.Unspecified].
* @see contentColorFor
*/
fun ColorScheme.contentColorFor(backgroundColor: Color): Color {
return when (backgroundColor) {
- primary, primaryDim -> onPrimary
+ primary,
+ primaryDim -> onPrimary
primaryContainer -> onPrimaryContainer
- secondary, secondaryDim -> onSecondary
+ secondary,
+ secondaryDim -> onSecondary
secondaryContainer -> onSecondaryContainer
- tertiary, tertiaryDim -> onTertiary
+ tertiary,
+ tertiaryDim -> onTertiary
tertiaryContainer -> onTertiaryContainer
- surface, surfaceDim, surfaceBright -> onSurface
+ surfaceContainer,
+ surfaceContainerLow,
+ surfaceContainerHigh -> onSurface
background -> onBackground
error -> onError
+ errorContainer -> onErrorContainer
else -> Color.Unspecified
}
}
/**
- * The Material color system contains pairs of colors that are typically used for the background
- * and content color inside a component. For example, a Button typically uses `primary` for its
+ * The Material color system contains pairs of colors that are typically used for the background and
+ * content color inside a component. For example, a Button typically uses `primary` for its
* background, and `onPrimary` for the color of its content (usually text or iconography).
*
* This function tries to match the provided [backgroundColor] to a 'background' color in this
* [ColorScheme], and then will return the corresponding color used for content. For example, when
* [backgroundColor] is [ColorScheme.primary], this will return [ColorScheme.onPrimary].
*
- * If [backgroundColor] does not match a background color in the theme, this will return
- * the current value of [LocalContentColor] as a best-effort color.
+ * If [backgroundColor] does not match a background color in the theme, this will return the current
+ * value of [LocalContentColor] as a best-effort color.
*
* @return the matching content color for [backgroundColor]. If [backgroundColor] is not present in
- * the theme's [ColorScheme], then returns the current value of [LocalContentColor].
- *
+ * the theme's [ColorScheme], then returns the current value of [LocalContentColor].
* @see ColorScheme.contentColorFor
*/
@Composable
@ReadOnlyComposable
fun contentColorFor(backgroundColor: Color): Color =
- MaterialTheme.colorScheme
- .contentColorFor(backgroundColor)
- .takeOrElse { LocalContentColor.current }
+ MaterialTheme.colorScheme.contentColorFor(backgroundColor).takeOrElse {
+ LocalContentColor.current
+ }
/**
* Helper function for component color tokens. Here is an example on how to use component color
- * tokens:
- * ``MaterialTheme.colorScheme.fromToken(FilledButtonTokens.ContainerColor)``
+ * tokens: ``MaterialTheme.colorScheme.fromToken(FilledButtonTokens.ContainerColor)``
*/
internal fun ColorScheme.fromToken(value: ColorSchemeKeyTokens): Color {
return when (value) {
@@ -306,9 +321,9 @@
ColorSchemeKeyTokens.TertiaryContainer -> tertiaryContainer
ColorSchemeKeyTokens.OnTertiary -> onTertiary
ColorSchemeKeyTokens.OnTertiaryContainer -> onTertiaryContainer
- ColorSchemeKeyTokens.SurfaceDim -> surfaceDim
- ColorSchemeKeyTokens.Surface -> surface
- ColorSchemeKeyTokens.SurfaceBright -> surfaceBright
+ ColorSchemeKeyTokens.SurfaceContainerLow -> surfaceContainerLow
+ ColorSchemeKeyTokens.SurfaceContainer -> surfaceContainer
+ ColorSchemeKeyTokens.SurfaceContainerHigh -> surfaceContainerHigh
ColorSchemeKeyTokens.OnSurface -> onSurface
ColorSchemeKeyTokens.OnSurfaceVariant -> onSurfaceVariant
ColorSchemeKeyTokens.Outline -> outline
@@ -317,33 +332,33 @@
ColorSchemeKeyTokens.OnBackground -> onBackground
ColorSchemeKeyTokens.Error -> error
ColorSchemeKeyTokens.OnError -> onError
+ ColorSchemeKeyTokens.ErrorContainer -> errorContainer
+ ColorSchemeKeyTokens.OnErrorContainer -> onErrorContainer
}
}
/**
* CompositionLocal used to pass [ColorScheme] down the tree.
*
- * Setting the value here is typically done as part of [MaterialTheme].
- * To retrieve the current value of this CompositionLocal, use
- * [MaterialTheme.colorScheme].
+ * Setting the value here is typically done as part of [MaterialTheme]. To retrieve the current
+ * value of this CompositionLocal, use [MaterialTheme.colorScheme].
*/
internal val LocalColorScheme = staticCompositionLocalOf<ColorScheme> { ColorScheme() }
/**
* Convert given color to disabled color.
+ *
* @param disabledAlpha Alpha used to represent disabled colors.
*/
internal fun Color.toDisabledColor(disabledAlpha: Float = DisabledContentAlpha) =
this.copy(alpha = this.alpha * disabledAlpha)
/**
- * Converts a color token key to the local color scheme provided by the theme.
- * The color references the [LocalColorScheme].
+ * Converts a color token key to the local color scheme provided by the theme. The color references
+ * the [LocalColorScheme].
*/
internal val ColorSchemeKeyTokens.value: Color
- @ReadOnlyComposable
- @Composable
- get() = MaterialTheme.colorScheme.fromToken(this)
+ @ReadOnlyComposable @Composable get() = MaterialTheme.colorScheme.fromToken(this)
internal const val DisabledContentAlpha = 0.38f
internal const val DisabledContainerAlpha = 0.12f
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentColor.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentColor.kt
index f05bed6..0f79f2e 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentColor.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentColor.kt
@@ -21,8 +21,9 @@
/**
* CompositionLocal containing the preferred content color for a given position in the hierarchy.
- * This typically represents the `on` color for a color in [ColorScheme]. For example, if the background
- * color is [ColorScheme.surface], this color is typically set to [ColorScheme.onSurface].
+ * This typically represents the `on` color for a color in [ColorScheme]. For example, if the
+ * background color is [ColorScheme.surfaceContainer], this color is typically set to
+ * [ColorScheme.onSurface].
*
* This color should be used for any typography / iconography, to ensure that the color of these
* adjusts when the background color changes. For example, on a dark background, text should be
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt
new file mode 100644
index 0000000..7e9f2be
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2024 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.wear.compose.material3
+
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.graphics.isSpecified
+import androidx.compose.ui.semantics.ProgressBarRangeInfo
+import androidx.compose.ui.semantics.progressBarRangeInfo
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material3.ProgressIndicatorDefaults.StartAngle
+import androidx.wear.compose.material3.ProgressIndicatorDefaults.StrokeWidth
+import androidx.wear.compose.material3.tokens.ColorSchemeKeyTokens
+import androidx.wear.compose.materialcore.toRadians
+import kotlin.math.PI
+import kotlin.math.asin
+import kotlin.math.cos
+import kotlin.math.min
+import kotlin.math.sin
+
+/**
+ * Material Design circular progress indicator.
+ *
+ * Example of a full screen [CircularProgressIndicator]. Note that the padding
+ * [ProgressIndicatorDefaults.FullScreenPadding] should be applied:
+ * @sample androidx.wear.compose.material3.samples.FullScreenProgressIndicatorSample
+ *
+ * Example of progress showing overflow value (more than 1) by [CircularProgressIndicator]:
+ * @sample androidx.wear.compose.material3.samples.OverflowProgressIndicatorSample
+ *
+ * Example of progress indicator wrapping media control by [CircularProgressIndicator]:
+ * @sample androidx.wear.compose.material3.samples.MediaButtonProgressIndicatorSample
+ *
+ * Progress indicators express the proportion of completion of an ongoing task.
+ *
+ * @param progress The progress of this progress indicator where 0.0 represents no progress and 1.0
+ * represents completion. Values outside of this range are coerced into the range 0..1.
+ * @param modifier Modifier to be applied to the CircularProgressIndicator.
+ * @param startAngle The starting position of the progress arc, measured clockwise in degrees (0
+ * to 360) from the 3 o'clock position. For example, 0 and 360 represent 3 o'clock, 90 and 180
+ * represent 6 o'clock and 9 o'clock respectively.
+ * Default is 270 degrees [ProgressIndicatorDefaults.StartAngle] (top of the screen).
+ * @param endAngle The ending position of the progress arc, measured clockwise in degrees (0 to 360)
+ * from the 3 o'clock position. For example, 0 and 360 represent 3 o'clock, 90 and 180 represent 6
+ * o'clock and 9 o'clock respectively. By default equal to [startAngle].
+ * @param colors [ProgressIndicatorColors] that will be used to resolve the indicator and track
+ * color for this progress indicator in different states.
+ * @param strokeWidth The stroke width for the progress indicator.
+ * @param gapSize The space left between the ends of the progress indicator and the track (in Dp).
+ */
+@Composable
+fun CircularProgressIndicator(
+ progress: () -> Float,
+ modifier: Modifier = Modifier,
+ startAngle: Float = StartAngle,
+ endAngle: Float = startAngle,
+ colors: ProgressIndicatorColors = ProgressIndicatorDefaults.colors(),
+ strokeWidth: Dp = StrokeWidth,
+ gapSize: Dp = ProgressIndicatorDefaults.gapSize(strokeWidth),
+) {
+ val coercedProgress = { progress().coerceIn(0f, 1f) }
+ // Canvas internally uses Spacer.drawBehind.
+ // Using Spacer.drawWithCache to optimize the stroke allocations.
+ Spacer(
+ modifier
+ .semantics(mergeDescendants = true) {
+ progressBarRangeInfo = ProgressBarRangeInfo(coercedProgress(), 0f..1f)
+ }
+ .fillMaxSize()
+ .focusable()
+ .drawWithCache {
+ val fullSweep = 360f - ((startAngle - endAngle) % 360 + 360) % 360
+ val progressSweep = fullSweep * coercedProgress()
+ val stroke = Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Round)
+ val minSize = min(size.height, size.width)
+ // Sweep angle between two progress indicator segments.
+ val gapSweep =
+ asin((stroke.width + gapSize.toPx()) / (minSize - stroke.width))
+ .toDegrees() * 2f
+
+ onDrawWithContent {
+ // Draw an indicator.
+ drawIndicatorSegment(
+ startAngle = startAngle,
+ sweep = progressSweep,
+ gapSweep = gapSweep,
+ brush = colors.indicatorBrush,
+ stroke = stroke
+ )
+
+ // Draw a background.
+ drawIndicatorSegment(
+ startAngle = startAngle + progressSweep,
+ sweep = fullSweep - progressSweep,
+ gapSweep = gapSweep,
+ brush = colors.trackBrush,
+ stroke = stroke
+ )
+ }
+ }
+ )
+}
+
+/** Contains defaults for Progress Indicators. */
+object ProgressIndicatorDefaults {
+ /**
+ * The default stroke width for a circular progress indicator. For example, you can apply this
+ * value when drawn around an [IconButton] with size [IconButtonDefaults.DefaultButtonSize].
+ *
+ * This can be customized with `strokeWidth` parameter on [CircularProgressIndicator].
+ */
+ val ButtonCircularIndicatorStrokeWidth = 6.dp
+
+ /**
+ * The recommended stroke width when used for default and large size circular progress
+ * indicators.
+ *
+ * This can be customized with `strokeWidth` parameter on [CircularProgressIndicator].
+ */
+ val StrokeWidth = 18.dp
+
+ /**
+ * The default angle used for the start of the progress indicator arc.
+ *
+ * This can be customized with `startAngle` parameter on [CircularProgressIndicator].
+ */
+ val StartAngle = 270f
+
+ /**
+ * Returns recommended size of the gap based on `strokeWidth`.
+ *
+ * The absolute value can be customized with `gapSize` parameter on [CircularProgressIndicator].
+ */
+ fun gapSize(strokeWidth: Dp): Dp = strokeWidth / 3f
+
+ /** Padding used for displaying [CircularProgressIndicator] full screen. */
+ val FullScreenPadding = 2.dp
+
+ /**
+ * Creates a [ProgressIndicatorColors] that represents the default arc colors used in
+ * a [CircularProgressIndicator].
+ */
+ @Composable
+ fun colors() = MaterialTheme.colorScheme.defaultProgressIndicatorColors
+
+ /**
+ * Creates a [ProgressIndicatorColors] with modified colors used in a
+ * [CircularProgressIndicator].
+ *
+ * @param indicatorColor The indicator arc color.
+ * @param trackColor The track arc color.
+ */
+ @Composable
+ fun colors(indicatorColor: Color = Color.Unspecified, trackColor: Color = Color.Unspecified) =
+ MaterialTheme.colorScheme.defaultProgressIndicatorColors.copy(
+ indicatorColor = indicatorColor,
+ trackColor = trackColor
+ )
+
+ /**
+ * Creates a [ProgressIndicatorColors] with modified brushes used to draw arcs in a
+ * [CircularProgressIndicator].
+ *
+ * @param indicatorBrush The brush used to draw indicator arc.
+ * @param trackBrush The brush used to draw track arc.
+ */
+ @Composable
+ fun colors(indicatorBrush: Brush? = null, trackBrush: Brush? = null) =
+ MaterialTheme.colorScheme.defaultProgressIndicatorColors.copy(
+ indicatorBrush = indicatorBrush,
+ trackBrush = trackBrush
+ )
+
+ private val ColorScheme.defaultProgressIndicatorColors: ProgressIndicatorColors
+ get() {
+ return defaultProgressIndicatorColorsCached ?: ProgressIndicatorColors(
+ indicatorBrush = SolidColor(fromToken(ColorSchemeKeyTokens.Primary)),
+ trackBrush = SolidColor(fromToken(ColorSchemeKeyTokens.SurfaceContainer)),
+ ).also {
+ defaultProgressIndicatorColorsCached = it
+ }
+ }
+}
+
+/**
+ * Represents the indicator and track colors used in progress indicator.
+ *
+ * @param indicatorBrush [Brush] used to draw the indicator arc of progress indicator.
+ * @param trackBrush [Brush] used to draw the track arc of progress indicator.
+ */
+class ProgressIndicatorColors(val indicatorBrush: Brush, val trackBrush: Brush) {
+ internal fun copy(
+ indicatorColor: Color = Color.Unspecified,
+ trackColor: Color = Color.Unspecified,
+ ) = ProgressIndicatorColors(
+ indicatorBrush =
+ if (indicatorColor.isSpecified) SolidColor(indicatorColor) else indicatorBrush,
+ trackBrush = if (trackColor.isSpecified) SolidColor(trackColor) else trackBrush
+ )
+
+ internal fun copy(
+ indicatorBrush: Brush? = null,
+ trackBrush: Brush? = null,
+ ) = ProgressIndicatorColors(
+ indicatorBrush = indicatorBrush ?: this.indicatorBrush,
+ trackBrush = trackBrush ?: this.trackBrush
+ )
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is ProgressIndicatorColors) return false
+
+ if (indicatorBrush != other.indicatorBrush) return false
+ if (trackBrush != other.trackBrush) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = indicatorBrush.hashCode()
+ result = 31 * result + trackBrush.hashCode()
+ return result
+ }
+}
+
+/**
+ * Draws an arc for indicator segment leaving half of the `gapSweep` before each visual end.
+ *
+ * If indicator gets too small, the circle that proportionally scales down is drawn instead.
+ */
+private fun DrawScope.drawIndicatorSegment(
+ startAngle: Float,
+ sweep: Float,
+ gapSweep: Float,
+ brush: Brush,
+ stroke: Stroke
+) {
+ if (sweep < gapSweep) {
+ // Draw a small indicator.
+ val angle = (startAngle + sweep / 2f).toRadians()
+ val radius = size.minDimension / 2 - stroke.width / 2
+ val circleRadius = (stroke.width / 2) * sweep / gapSweep
+ val alpha = (circleRadius / stroke.width * 2f).coerceAtMost(1f)
+ val brushWithAlpha =
+ if (brush is SolidColor && alpha < 1f) {
+ SolidColor(brush.value.copy(alpha = alpha))
+ } else {
+ brush
+ }
+ drawCircle(
+ brushWithAlpha,
+ circleRadius,
+ center =
+ Offset(
+ radius * cos(angle) + size.minDimension / 2,
+ radius * sin(angle) + size.minDimension / 2
+ )
+ )
+ } else {
+ // To draw this circle we need a rect with edges that line up with the midpoint of the
+ // stroke.
+ // To do this we need to remove half the stroke width from the total diameter for both
+ // sides.
+ val diameter = min(size.width, size.height)
+ val diameterOffset = stroke.width / 2
+ val arcDimen = diameter - 2 * diameterOffset
+ drawArc(
+ brush = brush,
+ startAngle = startAngle + gapSweep / 2,
+ sweepAngle = sweep - gapSweep,
+ useCenter = false,
+ topLeft =
+ Offset(
+ diameterOffset + (size.width - diameter) / 2,
+ diameterOffset + (size.height - diameter) / 2
+ ),
+ size = Size(arcDimen, arcDimen),
+ style = stroke
+ )
+ }
+}
+
+private fun Float.toDegrees() = this * 180f / PI.toFloat()
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
index a02f410..133d447 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
@@ -329,7 +329,7 @@
*/
@Composable
fun colors(
- containerColor: Color = MaterialTheme.colorScheme.surface,
+ containerColor: Color = MaterialTheme.colorScheme.surfaceContainer,
buttonIconColor: Color = MaterialTheme.colorScheme.secondary,
selectedBarColor: Color = MaterialTheme.colorScheme.primary,
unselectedBarColor: Color = MaterialTheme.colorScheme.background.copy(alpha = 0.3f),
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleControls.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleControls.kt
index 7074803..b44ce9b 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleControls.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleControls.kt
@@ -18,26 +18,39 @@
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.TweenSpec
+import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.State
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import androidx.wear.compose.material3.tokens.MotionTokens
+import androidx.wear.compose.materialcore.SelectionStage
import androidx.wear.compose.materialcore.animateSelectionColor
-import androidx.wear.compose.materialcore.directionVector
-import androidx.wear.compose.materialcore.toRadians
+import androidx.wear.compose.materialcore.animateTick
+import androidx.wear.compose.materialcore.isLayoutDirectionRtl
/**
* [Checkbox] provides an animated checkbox for use as a toggle control in
@@ -81,7 +94,7 @@
},
progressAnimationSpec = PROGRESS_ANIMATION_SPEC,
width = WIDTH,
- height = HEIGHT,
+ height = CHECKBOX_HEIGHT,
ripple = rippleOrFallbackImplementation()
)
@@ -100,51 +113,49 @@
fun ToggleControlScope.Switch(
modifier: Modifier = Modifier,
colors: SwitchColors = SwitchDefaults.colors(),
-) = androidx.wear.compose.materialcore.Switch(
- modifier = modifier,
- checked = isChecked,
- enabled = isEnabled,
- onCheckedChange = null,
- interactionSource = null,
- trackFillColor = { isEnabled, isChecked ->
- colors.trackColor(
- enabled = isEnabled,
- checked = isChecked
- )
- },
- trackStrokeColor = { isEnabled, isChecked ->
- colors.trackStrokeColor(
- enabled = isEnabled,
- checked = isChecked
- )
- },
- thumbColor = { isEnabled, isChecked ->
- colors.thumbColor(
- enabled = isEnabled,
- checked = isChecked
- )
- },
- thumbIconColor = { isEnabled, isChecked ->
- colors.thumbIconColor(
- enabled = isEnabled,
- checked = isChecked
- )
- },
- trackWidth = TRACK_WIDTH,
- trackHeight = TRACK_HEIGHT,
- drawThumb = { drawScope, thumbColor, progress, thumbIconColor, isRtl ->
- drawScope.drawThumb(
- thumbColor,
- progress,
- thumbIconColor,
- isRtl
- )
- },
- progressAnimationSpec = SWITCH_PROGRESS_ANIMATION_SPEC,
- width = WIDTH,
- height = HEIGHT,
- ripple = rippleOrFallbackImplementation()
-)
+) {
+ val isRtl = isLayoutDirectionRtl()
+ val targetState = if (isChecked) SelectionStage.Checked else SelectionStage.Unchecked
+ val transition = updateTransition(targetState, label = "switchTransition")
+ val thumbProgress = transition.animateFloat(
+ transitionSpec = { SWITCH_PROGRESS_ANIMATION_SPEC }, label = "switchTransition"
+ ) {
+ when (it) {
+ SelectionStage.Unchecked -> 0f
+ SelectionStage.Checked -> 1f
+ }
+ }
+ val thumbColor = colors.thumbColor(enabled = isEnabled, checked = isChecked).value
+ val thumbIconColor = colors.thumbIconColor(enabled = isEnabled, checked = isChecked).value
+ val trackColor = colors.trackColor(enabled = isEnabled, checked = isChecked).value
+ val trackBorderColor = colors.trackBorderColor(enabled = isEnabled, checked = isChecked).value
+ Box(
+ modifier = modifier
+ .semantics { this.role = Role.Switch }
+ .height(SWITCH_HEIGHT)
+ .width(WIDTH)
+ .border(
+ width = SWITCH_TRACK_WIDTH,
+ shape = CircleShape,
+ color = if (trackColor == trackBorderColor) Color.Transparent else trackBorderColor
+ )
+ .background(
+ color = trackColor,
+ shape = CircleShape
+ )
+ .drawBehind {
+ drawThumbAndTick(
+ isEnabled,
+ isChecked,
+ thumbColor,
+ thumbProgress.value,
+ thumbIconColor,
+ isRtl
+ )
+ }
+ .wrapContentSize(Alignment.CenterEnd)
+ )
+}
/**
* Represents the content colors used in [Checkbox] in different states.
@@ -319,7 +330,7 @@
)
@Composable
- internal fun trackStrokeColor(enabled: Boolean, checked: Boolean): State<Color> =
+ internal fun trackBorderColor(enabled: Boolean, checked: Boolean): State<Color> =
animateSelectionColor(
enabled = enabled,
checked = checked,
@@ -417,22 +428,50 @@
* @param checkedThumbColor The thumb color of this [Switch] when enabled and checked.
* @param checkedThumbIconColor The thumb icon color of this [Switch] when enabled and checked.
* @param checkedTrackColor The track color of this [Switch] when enabled and checked.
- * @param checkedTrackBorderColor The track border color of this [Switch] when enabled and checked.
+ * @param checkedTrackBorderColor The border color of this [Switch] when enabled and checked.
* @param uncheckedThumbColor The thumb color of this [Switch] when enabled and unchecked.
- * @param uncheckedThumbIconColor The thumb icon color of this [Switch] when enabled and checked.
+ * @param uncheckedThumbIconColor The thumb icon color of this [Switch] when enabled and
+ * checked.
* @param uncheckedTrackColor The track color of this [Switch] when enabled and unchecked.
- * @param uncheckedTrackBorderColor The track border color of this [Switch] when enabled and unchecked.
+ * @param uncheckedTrackBorderColor The border color of this [Switch] when enabled and
+ * unchecked.
+ * @param disabledCheckedThumbColor The thumb color of this [Switch] when disabled and checked.
+ * @param disabledCheckedThumbIconColor The thumb icon color of this [Switch] when disabled
+ * and checked.
+ * @param disabledCheckedTrackColor The track color of this [Switch] when disabled and checked.
+ * @param disabledCheckedTrackBorderColor The border color of this [Switch] when disabled and
+ * unchecked.
+ * @param disabledUncheckedThumbColor The thumb color of this [Switch] when disabled and
+ * unchecked.
+ * @param disabledUncheckedThumbIconColor The thumb icon color of this [Switch] when disabled
+ * and unchecked.
+ * @param disabledUncheckedTrackColor The track color of this [Switch] when disabled and
+ * unchecked.
+ * @param disabledUncheckedTrackBorderColor The border color of this [Switch] when disabled
+ * and unchecked.
*/
@Composable
fun colors(
checkedThumbColor: Color = MaterialTheme.colorScheme.onPrimary,
checkedThumbIconColor: Color = MaterialTheme.colorScheme.primary,
- checkedTrackColor: Color = MaterialTheme.colorScheme.primaryDim,
- checkedTrackBorderColor: Color = MaterialTheme.colorScheme.primaryDim,
+ checkedTrackColor: Color = MaterialTheme.colorScheme.primary,
+ checkedTrackBorderColor: Color = MaterialTheme.colorScheme.primary,
uncheckedThumbColor: Color = MaterialTheme.colorScheme.outline,
- uncheckedThumbIconColor: Color = MaterialTheme.colorScheme.background,
- uncheckedTrackColor: Color = MaterialTheme.colorScheme.surface,
- uncheckedTrackBorderColor: Color = MaterialTheme.colorScheme.outline
+ uncheckedThumbIconColor: Color = Color.Transparent,
+ uncheckedTrackColor: Color = MaterialTheme.colorScheme.surfaceContainer,
+ uncheckedTrackBorderColor: Color = MaterialTheme.colorScheme.outline,
+ disabledCheckedThumbColor: Color = MaterialTheme.colorScheme.background.toDisabledColor(),
+ disabledCheckedThumbIconColor: Color =
+ MaterialTheme.colorScheme.onSurface.toDisabledColor(disabledAlpha = 0.12f),
+ disabledCheckedTrackColor: Color =
+ MaterialTheme.colorScheme.onSurface.toDisabledColor(disabledAlpha = 0.12f),
+ disabledCheckedTrackBorderColor: Color =
+ MaterialTheme.colorScheme.onSurface.toDisabledColor(disabledAlpha = 0.12f),
+ disabledUncheckedThumbColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(),
+ disabledUncheckedThumbIconColor: Color = Color.Transparent,
+ disabledUncheckedTrackColor: Color = Color.Transparent,
+ disabledUncheckedTrackBorderColor: Color =
+ MaterialTheme.colorScheme.onSurface.toDisabledColor()
): SwitchColors = SwitchColors(
checkedThumbColor = checkedThumbColor,
checkedThumbIconColor = checkedThumbIconColor,
@@ -442,41 +481,32 @@
uncheckedThumbIconColor = uncheckedThumbIconColor,
uncheckedTrackColor = uncheckedTrackColor,
uncheckedTrackBorderColor = uncheckedTrackBorderColor,
- disabledCheckedThumbColor = checkedThumbColor.toDisabledColor(),
- disabledCheckedThumbIconColor = checkedThumbIconColor.toDisabledColor(),
- disabledCheckedTrackColor = checkedTrackColor.toDisabledColor(
- disabledAlpha = DisabledContainerAlpha
- ),
- disabledCheckedTrackBorderColor = checkedTrackBorderColor.toDisabledColor(
- disabledAlpha = DisabledBorderAlpha
- ),
- disabledUncheckedThumbColor = uncheckedThumbColor.toDisabledColor(),
- disabledUncheckedThumbIconColor = uncheckedThumbIconColor.toDisabledColor(),
- disabledUncheckedTrackColor = uncheckedTrackColor.toDisabledColor(
- disabledAlpha = DisabledContainerAlpha
- ),
- disabledUncheckedTrackBorderColor = uncheckedTrackBorderColor.toDisabledColor(
- disabledAlpha = DisabledBorderAlpha
- )
+ disabledCheckedThumbColor = disabledCheckedThumbColor,
+ disabledCheckedThumbIconColor = disabledCheckedThumbIconColor,
+ disabledCheckedTrackColor = disabledCheckedTrackColor,
+ disabledCheckedTrackBorderColor = disabledCheckedTrackBorderColor,
+ disabledUncheckedThumbColor = disabledUncheckedThumbColor,
+ disabledUncheckedThumbIconColor = disabledUncheckedThumbIconColor,
+ disabledUncheckedTrackColor = disabledUncheckedTrackColor,
+ disabledUncheckedTrackBorderColor = disabledUncheckedTrackBorderColor
)
}
private fun DrawScope.drawBox(color: Color, progress: Float, isRtl: Boolean) {
// Centering vertically.
- val topCornerPx = (HEIGHT - BOX_SIZE).toPx() / 2
+ val topCornerPx = (CHECKBOX_HEIGHT - BOX_SIZE).toPx() / 2
val strokeWidthPx = BOX_STROKE.toPx()
val halfStrokeWidthPx = strokeWidthPx / 2.0f
val radiusPx = BOX_RADIUS.toPx()
val checkboxSizePx = BOX_SIZE.toPx()
// Aligning the box to the end.
- val startXOffsetPx = if (isRtl) 0f else (WIDTH - HEIGHT).toPx()
+ val startXOffsetPx = if (isRtl) 0f else (WIDTH - CHECKBOX_HEIGHT).toPx()
// Draw the outline of the box.
drawRoundRect(
color,
topLeft = Offset(
- topCornerPx + halfStrokeWidthPx + startXOffsetPx,
- topCornerPx + halfStrokeWidthPx
+ topCornerPx + halfStrokeWidthPx + startXOffsetPx, topCornerPx + halfStrokeWidthPx
),
size = Size(checkboxSizePx - strokeWidthPx, checkboxSizePx - strokeWidthPx),
cornerRadius = CornerRadius(radiusPx - halfStrokeWidthPx),
@@ -495,15 +525,17 @@
)
}
-private fun DrawScope.drawThumb(
+private fun DrawScope.drawThumbAndTick(
+ enabled: Boolean,
+ checked: Boolean,
thumbColor: Color,
progress: Float,
thumbIconColor: Color,
isRtl: Boolean
) {
- val thumbPaddingUnchecked = TRACK_HEIGHT / 2 - THUMB_RADIUS_UNCHECKED
- val thumbPaddingChecked = TRACK_HEIGHT / 2 - THUMB_RADIUS_CHECKED
+ val thumbPaddingUnchecked = SWITCH_HEIGHT / 2 - THUMB_RADIUS_UNCHECKED
+ val thumbPaddingChecked = SWITCH_HEIGHT / 2 - THUMB_RADIUS_CHECKED
val switchThumbRadiusPx = lerp(
start = THUMB_RADIUS_UNCHECKED.toPx(),
@@ -514,18 +546,16 @@
val switchTrackLengthPx = WIDTH.toPx()
// For Rtl mode the thumb progress will start from the end of the switch.
- val thumbProgressPx = if (isRtl)
- lerp(
- start = switchTrackLengthPx - switchThumbRadiusPx - thumbPaddingUnchecked.toPx(),
- stop = switchThumbRadiusPx + thumbPaddingChecked.toPx(),
- fraction = progress
- )
- else
- lerp(
- start = switchThumbRadiusPx + thumbPaddingUnchecked.toPx(),
- stop = switchTrackLengthPx - switchThumbRadiusPx - thumbPaddingChecked.toPx(),
- fraction = progress
- )
+ val thumbProgressPx = if (isRtl) lerp(
+ start = switchTrackLengthPx - switchThumbRadiusPx - thumbPaddingUnchecked.toPx(),
+ stop = switchThumbRadiusPx + thumbPaddingChecked.toPx(),
+ fraction = progress
+ )
+ else lerp(
+ start = switchThumbRadiusPx + thumbPaddingUnchecked.toPx(),
+ stop = switchTrackLengthPx - switchThumbRadiusPx - thumbPaddingChecked.toPx(),
+ fraction = progress
+ )
drawCircle(
color = thumbColor,
@@ -533,81 +563,37 @@
center = Offset(thumbProgressPx, center.y)
)
- val totalDist = switchTrackLengthPx - 2 * switchThumbRadiusPx - 4.dp.toPx()
+ val ltrAdditionalOffset = 5.dp.toPx()
+ val rtlAdditionalOffset = 6.dp.toPx()
+
+ val totalDist = switchTrackLengthPx - 2 * switchThumbRadiusPx - ltrAdditionalOffset
// Offset value to be added if RTL mode is enabled.
// We need to move the tick to the checked position in ltr mode when unchecked.
- val rtlOffset = switchTrackLengthPx - 2 * THUMB_RADIUS_CHECKED.toPx() - 4.dp.toPx()
+ val rtlOffset = switchTrackLengthPx - 2 * THUMB_RADIUS_CHECKED.toPx() - rtlAdditionalOffset
val distMoved = if (isRtl) rtlOffset - progress * totalDist else progress * totalDist
// Draw tick icon
- drawTickIcon(thumbIconColor, progress, distMoved)
-}
-
-private fun DrawScope.drawTickIcon(tickColor: Color, alpha: Float, distMoved: Float) {
- val tickBaseLength = TICK_BASE_LENGTH.toPx()
- val tickStickLength = TICK_STICK_LENGTH.toPx()
- val tickTotalLength = tickBaseLength + tickStickLength
- val center = Offset(9.dp.toPx(), 9.dp.toPx())
- val angle = TICK_ROTATION - TICK_ROTATION / tickTotalLength * tickTotalLength
- val angleRadians = angle.toRadians()
-
- val baseStart = Offset(6.7f.dp.toPx() + distMoved, 13.3f.dp.toPx())
-
- val path = Path()
- path.moveTo(baseStart.rotate(angleRadians, center))
- path.lineTo(
- (baseStart + Offset(tickBaseLength, tickBaseLength)).rotate(angleRadians, center)
- )
-
- val stickStart = Offset(9.3f.dp.toPx() + distMoved, 16.3f.dp.toPx())
- // Move back to the start of the stick (without drawing)
- path.moveTo(stickStart.rotate(angleRadians, center))
- path.lineTo(
- Offset(stickStart.x + tickStickLength, stickStart.y - tickStickLength).rotate(
- angleRadians,
- center
- )
- )
- // Use StrokeCap.Butt because Square adds an extension on the end of each line.
- drawPath(
- path,
- tickColor,
- style = Stroke(width = 1.dp.toPx(), cap = StrokeCap.Butt),
- alpha = alpha
+ animateTick(
+ enabled = enabled,
+ checked = checked,
+ tickColor = thumbIconColor,
+ tickProgress = progress,
+ startXOffset = distMoved.toDp()
)
}
-private fun Path.moveTo(offset: Offset) {
- moveTo(offset.x, offset.y)
-}
-
-private fun Path.lineTo(offset: Offset) {
- lineTo(offset.x, offset.y)
-}
-
-private fun Offset.rotate(angleRadians: Float): Offset {
- val angledDirection = directionVector(angleRadians)
- return angledDirection * x + angledDirection.rotate90() * y
-}
-
-private fun Offset.rotate(angleRadians: Float, center: Offset): Offset =
- (this - center).rotate(angleRadians) + center
-
-private fun Offset.rotate90() = Offset(-y, x)
-
private val BOX_STROKE = 2.dp
private val BOX_RADIUS = 2.dp
private val BOX_SIZE = 18.dp
-private val THUMB_RADIUS_UNCHECKED = 7.dp
+private val THUMB_RADIUS_UNCHECKED = 6.dp
private val THUMB_RADIUS_CHECKED = 9.dp
-private val TRACK_WIDTH = 32.dp
-private val TRACK_HEIGHT = 22.dp
-private val TICK_BASE_LENGTH = 3.dp
-private val TICK_STICK_LENGTH = 7.dp
-private const val TICK_ROTATION = 15f
+private val SWITCH_HEIGHT = 22.dp
+private val CHECKBOX_HEIGHT = 24.dp
+private val WIDTH = 32.dp
+private val SWITCH_TRACK_WIDTH = 2.dp
private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
@@ -615,6 +601,3 @@
tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
private val SWITCH_PROGRESS_ANIMATION_SPEC: TweenSpec<Float> =
tween(MotionTokens.DurationMedium2, 0, MotionTokens.EasingStandardDecelerate)
-
-private val WIDTH = 32.dp
-private val HEIGHT = 24.dp
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CardTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CardTokens.kt
index 93778f2..f0cc446 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CardTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/CardTokens.kt
@@ -25,7 +25,7 @@
val AppImageSize = 16.0.dp
val AppNameColor = ColorSchemeKeyTokens.OnSurfaceVariant
val AppNameTypography = TypographyKeyTokens.LabelSmall
- val ContainerColor = ColorSchemeKeyTokens.Surface
+ val ContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val ContentColor = ColorSchemeKeyTokens.OnSurfaceVariant
val ContentTypography = TypographyKeyTokens.BodyLarge
val Shape = ShapeKeyTokens.CornerLarge
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt
index 9bbfe57..3dbbc0c 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-// VERSION: v0_7
+// VERSION: v0_42
// GENERATED CODE - DO NOT MODIFY BY HAND
package androidx.wear.compose.material3.tokens
@@ -22,8 +22,10 @@
internal enum class ColorSchemeKeyTokens {
Background,
Error,
+ ErrorContainer,
OnBackground,
OnError,
+ OnErrorContainer,
OnPrimary,
OnPrimaryContainer,
OnSecondary,
@@ -40,9 +42,9 @@
Secondary,
SecondaryContainer,
SecondaryDim,
- Surface,
- SurfaceBright,
- SurfaceDim,
+ SurfaceContainer,
+ SurfaceContainerHigh,
+ SurfaceContainerLow,
Tertiary,
TertiaryContainer,
TertiaryDim,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt
index e3be595..68dc43f 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-// VERSION: v0_8
+// VERSION: v0_42
// GENERATED CODE - DO NOT MODIFY BY HAND
package androidx.wear.compose.material3.tokens
internal object ColorTokens {
val Background = PaletteTokens.Neutral0
- val Error = PaletteTokens.Error65
+ val Error = PaletteTokens.Error80
+ val ErrorContainer = PaletteTokens.Error70
val OnBackground = PaletteTokens.Neutral100
- val OnError = PaletteTokens.Error10
+ val OnError = PaletteTokens.Error20
+ val OnErrorContainer = PaletteTokens.Error10
val OnPrimary = PaletteTokens.Primary10
val OnPrimaryContainer = PaletteTokens.Primary90
val OnSecondary = PaletteTokens.Secondary10
@@ -40,9 +42,9 @@
val Secondary = PaletteTokens.Secondary90
val SecondaryContainer = PaletteTokens.Secondary30
val SecondaryDim = PaletteTokens.Secondary80
- val Surface = PaletteTokens.Neutral20
- val SurfaceBright = PaletteTokens.Neutral30
- val SurfaceDim = PaletteTokens.Neutral15
+ val SurfaceContainer = PaletteTokens.Neutral20
+ val SurfaceContainerHigh = PaletteTokens.Neutral30
+ val SurfaceContainerLow = PaletteTokens.Neutral15
val Tertiary = PaletteTokens.Tertiary90
val TertiaryContainer = PaletteTokens.Tertiary30
val TertiaryDim = PaletteTokens.Tertiary80
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalButtonTokens.kt
index 1341a0b..9275864 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalButtonTokens.kt
@@ -22,7 +22,7 @@
import androidx.compose.ui.unit.dp
internal object FilledTonalButtonTokens {
- val ContainerColor = ColorSchemeKeyTokens.Surface
+ val ContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val ContainerHeight = 52.0.dp
val ContainerShape = ShapeKeyTokens.CornerLarge
val DisabledContainerColor = ColorSchemeKeyTokens.OnSurface
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalIconButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalIconButtonTokens.kt
index e2c42f5..f4dc76f 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalIconButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalIconButtonTokens.kt
@@ -20,10 +20,10 @@
package androidx.wear.compose.material3.tokens
internal object FilledTonalIconButtonTokens {
- val ContainerColor = ColorSchemeKeyTokens.Surface
- val ContentColor = ColorSchemeKeyTokens.OnSurfaceVariant
- val DisabledContainerColor = ColorSchemeKeyTokens.OnSurface
- val DisabledContainerOpacity = 0.12f
- val DisabledContentColor = ColorSchemeKeyTokens.OnSurface
- val DisabledContentOpacity = 0.38f
+ val ContainerColor = ColorSchemeKeyTokens.SurfaceContainer
+ val ContentColor = ColorSchemeKeyTokens.OnSurfaceVariant
+ val DisabledContainerColor = ColorSchemeKeyTokens.OnSurface
+ val DisabledContainerOpacity = 0.12f
+ val DisabledContentColor = ColorSchemeKeyTokens.OnSurface
+ val DisabledContentOpacity = 0.38f
}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalTextButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalTextButtonTokens.kt
index a77dd0a..8459b18 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalTextButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/FilledTonalTextButtonTokens.kt
@@ -20,7 +20,7 @@
package androidx.wear.compose.material3.tokens
internal object FilledTonalTextButtonTokens {
- val ContainerColor = ColorSchemeKeyTokens.Surface
+ val ContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val ContainerShape = ShapeKeyTokens.CornerFull
val ContentColor = ColorSchemeKeyTokens.OnSurface
val ContentFont = TypographyKeyTokens.LabelMedium
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconToggleButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconToggleButtonTokens.kt
index b6ae374..c626ab9 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconToggleButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/IconToggleButtonTokens.kt
@@ -26,10 +26,10 @@
val DisabledCheckedContainerOpacity = 0.38f
val DisabledCheckedContentColor = ColorSchemeKeyTokens.OnPrimary
val DisabledCheckedContentOpacity = 0.38f
- val DisabledUncheckedContainerColor = ColorSchemeKeyTokens.Surface
+ val DisabledUncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val DisabledUncheckedContainerOpacity = 0.38f
val DisabledUncheckedContentColor = ColorSchemeKeyTokens.OnSurfaceVariant
val DisabledUncheckedContentOpacity = 0.38f
- val UncheckedContainerColor = ColorSchemeKeyTokens.Surface
+ val UncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val UncheckedContentColor = ColorSchemeKeyTokens.OnSurfaceVariant
}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageButtonTokens.kt
index 1f8188e..51a8342 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ImageButtonTokens.kt
@@ -20,7 +20,7 @@
package androidx.wear.compose.material3.tokens
internal object ImageButtonTokens {
- val BackgroundImageGradientColor = ColorSchemeKeyTokens.Surface
+ val BackgroundImageGradientColor = ColorSchemeKeyTokens.SurfaceContainer
val ContentColor = ColorSchemeKeyTokens.OnSurface
val DisabledContentOpacity = 0.38f
val DisabledContentColor = ColorSchemeKeyTokens.OnSurface
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt
index 65e3d32..6c79d71 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-// VERSION: v0_8
+// VERSION: v0_44
// GENERATED CODE - DO NOT MODIFY BY HAND
package androidx.wear.compose.material3.tokens
@@ -23,78 +23,79 @@
internal object PaletteTokens {
val Error0 = Color(red = 0, green = 0, blue = 0)
- val Error10 = Color(red = 65, green = 0, blue = 2)
+ val Error10 = Color(red = 65, green = 14, blue = 11)
val Error100 = Color(red = 255, green = 255, blue = 255)
- val Error20 = Color(red = 104, green = 1, blue = 7)
- val Error30 = Color(red = 137, green = 29, blue = 26)
- val Error40 = Color(red = 170, green = 53, blue = 47)
- val Error50 = Color(red = 203, green = 77, blue = 69)
- val Error60 = Color(red = 236, green = 102, blue = 91)
- val Error65 = Color(red = 253, green = 114, blue = 103)
- val Error70 = Color(red = 255, green = 137, blue = 126)
- val Error80 = Color(red = 255, green = 180, blue = 172)
- val Error85 = Color(red = 255, green = 199, blue = 193)
- val Error90 = Color(red = 255, green = 218, blue = 214)
- val Error95 = Color(red = 255, green = 237, blue = 234)
+ val Error20 = Color(red = 96, green = 20, blue = 16)
+ val Error30 = Color(red = 140, green = 29, blue = 24)
+ val Error40 = Color(red = 179, green = 38, blue = 30)
+ val Error5 = Color(red = 33, green = 7, blue = 6)
+ val Error50 = Color(red = 220, green = 54, blue = 46)
+ val Error60 = Color(red = 228, green = 105, blue = 98)
+ val Error65 = Color(red = 232, green = 126, blue = 120)
+ val Error70 = Color(red = 236, green = 146, blue = 142)
+ val Error80 = Color(red = 242, green = 184, blue = 181)
+ val Error85 = Color(red = 245, green = 203, blue = 200)
+ val Error90 = Color(red = 249, green = 222, blue = 220)
+ val Error95 = Color(red = 252, green = 238, blue = 238)
val Neutral0 = Color(red = 0, green = 0, blue = 0)
- val Neutral10 = Color(red = 31, green = 31, blue = 31)
+ val Neutral10 = Color(red = 29, green = 26, blue = 38)
val Neutral100 = Color(red = 255, green = 255, blue = 255)
- val Neutral15 = Color(red = 37, green = 38, blue = 38)
- val Neutral20 = Color(red = 48, green = 48, blue = 48)
- val Neutral30 = Color(red = 71, green = 71, blue = 71)
- val Neutral40 = Color(red = 94, green = 94, blue = 94)
- val Neutral50 = Color(red = 117, green = 117, blue = 117)
- val Neutral60 = Color(red = 143, green = 143, blue = 143)
- val Neutral70 = Color(red = 171, green = 171, blue = 171)
- val Neutral80 = Color(red = 199, green = 199, blue = 199)
- val Neutral90 = Color(red = 227, green = 227, blue = 227)
- val Neutral95 = Color(red = 242, green = 242, blue = 242)
+ val Neutral15 = Color(red = 39, green = 36, blue = 48)
+ val Neutral20 = Color(red = 51, green = 46, blue = 60)
+ val Neutral30 = Color(red = 73, green = 68, blue = 83)
+ val Neutral40 = Color(red = 97, green = 92, blue = 107)
+ val Neutral50 = Color(red = 122, green = 116, blue = 132)
+ val Neutral60 = Color(red = 148, green = 142, blue = 159)
+ val Neutral70 = Color(red = 175, green = 168, blue = 185)
+ val Neutral80 = Color(red = 203, green = 195, blue = 213)
+ val Neutral90 = Color(red = 232, green = 223, blue = 242)
+ val Neutral95 = Color(red = 246, green = 237, blue = 255)
val NeutralVariant0 = Color(red = 0, green = 0, blue = 0)
- val NeutralVariant10 = Color(red = 25, green = 29, blue = 28)
+ val NeutralVariant10 = Color(red = 29, green = 26, blue = 35)
val NeutralVariant100 = Color(red = 255, green = 255, blue = 255)
- val NeutralVariant20 = Color(red = 45, green = 49, blue = 47)
- val NeutralVariant30 = Color(red = 68, green = 71, blue = 70)
- val NeutralVariant40 = Color(red = 92, green = 95, blue = 94)
- val NeutralVariant50 = Color(red = 116, green = 119, blue = 117)
- val NeutralVariant60 = Color(red = 142, green = 145, blue = 143)
- val NeutralVariant70 = Color(red = 169, green = 172, blue = 170)
- val NeutralVariant80 = Color(red = 196, green = 199, blue = 197)
- val NeutralVariant90 = Color(red = 225, green = 227, blue = 225)
- val NeutralVariant95 = Color(red = 239, green = 242, blue = 239)
+ val NeutralVariant20 = Color(red = 50, green = 47, blue = 56)
+ val NeutralVariant30 = Color(red = 73, green = 69, blue = 79)
+ val NeutralVariant40 = Color(red = 97, green = 93, blue = 103)
+ val NeutralVariant50 = Color(red = 122, green = 117, blue = 128)
+ val NeutralVariant60 = Color(red = 148, green = 143, blue = 154)
+ val NeutralVariant70 = Color(red = 175, green = 169, blue = 181)
+ val NeutralVariant80 = Color(red = 202, green = 196, blue = 208)
+ val NeutralVariant90 = Color(red = 231, green = 224, blue = 236)
+ val NeutralVariant95 = Color(red = 245, green = 238, blue = 251)
val Primary0 = Color(red = 0, green = 0, blue = 0)
- val Primary10 = Color(red = 4, green = 30, blue = 73)
+ val Primary10 = Color(red = 33, green = 15, blue = 72)
val Primary100 = Color(red = 255, green = 255, blue = 255)
- val Primary20 = Color(red = 4, green = 30, blue = 73)
- val Primary30 = Color(red = 8, green = 66, blue = 160)
- val Primary40 = Color(red = 11, green = 87, blue = 208)
- val Primary50 = Color(red = 27, green = 110, blue = 243)
- val Primary60 = Color(red = 76, green = 141, blue = 246)
- val Primary70 = Color(red = 124, green = 172, blue = 248)
- val Primary80 = Color(red = 168, green = 199, blue = 250)
- val Primary90 = Color(red = 211, green = 227, blue = 253)
- val Primary95 = Color(red = 236, green = 243, blue = 254)
+ val Primary20 = Color(red = 55, green = 38, blue = 94)
+ val Primary30 = Color(red = 77, green = 61, blue = 118)
+ val Primary40 = Color(red = 102, green = 85, blue = 144)
+ val Primary50 = Color(red = 127, green = 109, blue = 170)
+ val Primary60 = Color(red = 153, green = 135, blue = 198)
+ val Primary70 = Color(red = 180, green = 161, blue = 226)
+ val Primary80 = Color(red = 208, green = 188, blue = 255)
+ val Primary90 = Color(red = 233, green = 221, blue = 255)
+ val Primary95 = Color(red = 246, green = 237, blue = 255)
val Secondary0 = Color(red = 0, green = 0, blue = 0)
- val Secondary10 = Color(red = 0, green = 29, blue = 53)
+ val Secondary10 = Color(red = 30, green = 24, blue = 46)
val Secondary100 = Color(red = 255, green = 255, blue = 255)
- val Secondary20 = Color(red = 0, green = 51, blue = 85)
- val Secondary30 = Color(red = 0, green = 51, blue = 85)
- val Secondary40 = Color(red = 0, green = 99, blue = 155)
- val Secondary50 = Color(red = 4, green = 125, blue = 183)
- val Secondary60 = Color(red = 57, green = 152, blue = 211)
- val Secondary70 = Color(red = 90, green = 179, blue = 240)
- val Secondary80 = Color(red = 127, green = 207, blue = 255)
- val Secondary90 = Color(red = 194, green = 231, blue = 255)
- val Secondary95 = Color(red = 223, green = 243, blue = 255)
+ val Secondary20 = Color(red = 51, green = 45, blue = 68)
+ val Secondary30 = Color(red = 74, green = 67, blue = 91)
+ val Secondary40 = Color(red = 98, green = 90, blue = 116)
+ val Secondary50 = Color(red = 123, green = 115, blue = 141)
+ val Secondary60 = Color(red = 150, green = 140, blue = 168)
+ val Secondary70 = Color(red = 177, green = 167, blue = 195)
+ val Secondary80 = Color(red = 204, green = 194, blue = 223)
+ val Secondary90 = Color(red = 233, green = 222, blue = 252)
+ val Secondary95 = Color(red = 246, green = 237, blue = 255)
val Tertiary0 = Color(red = 0, green = 0, blue = 0)
- val Tertiary10 = Color(red = 7, green = 39, blue = 17)
+ val Tertiary10 = Color(red = 46, green = 21, blue = 0)
val Tertiary100 = Color(red = 255, green = 255, blue = 255)
- val Tertiary20 = Color(red = 7, green = 39, blue = 17)
- val Tertiary30 = Color(red = 15, green = 82, blue = 35)
- val Tertiary40 = Color(red = 20, green = 108, blue = 46)
- val Tertiary50 = Color(red = 25, green = 134, blue = 57)
- val Tertiary60 = Color(red = 30, green = 164, blue = 70)
- val Tertiary70 = Color(red = 55, green = 190, blue = 95)
- val Tertiary80 = Color(red = 109, green = 213, blue = 140)
- val Tertiary90 = Color(red = 196, green = 238, blue = 208)
- val Tertiary95 = Color(red = 231, green = 248, blue = 237)
+ val Tertiary20 = Color(red = 76, green = 39, blue = 0)
+ val Tertiary30 = Color(red = 108, green = 58, blue = 3)
+ val Tertiary40 = Color(red = 136, green = 81, blue = 27)
+ val Tertiary50 = Color(red = 165, green = 105, blue = 49)
+ val Tertiary60 = Color(red = 195, green = 130, blue = 72)
+ val Tertiary70 = Color(red = 226, green = 156, blue = 95)
+ val Tertiary80 = Color(red = 255, green = 183, blue = 122)
+ val Tertiary90 = Color(red = 255, green = 220, blue = 194)
+ val Tertiary95 = Color(red = 255, green = 238, blue = 226)
}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/RadioButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/RadioButtonTokens.kt
index 85d5654..71802e6 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/RadioButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/RadioButtonTokens.kt
@@ -25,7 +25,7 @@
val DisabledSelectedIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
val DisabledSelectedSecondaryLabelColor = ColorSchemeKeyTokens.OnPrimaryContainer
val DisabledSelectedSecondaryLabelOpacity = 0.8f
- val DisabledUnselectedContainerColor = ColorSchemeKeyTokens.Surface
+ val DisabledUnselectedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val DisabledUnselectedContentColor = ColorSchemeKeyTokens.OnSurface
val DisabledUnselectedIconColor = ColorSchemeKeyTokens.Primary
val DisabledUnselectedSecondaryLabelColor = ColorSchemeKeyTokens.OnSurfaceVariant
@@ -37,7 +37,7 @@
val SelectedSecondaryLabelColor = ColorSchemeKeyTokens.OnPrimaryContainer
val SelectedSecondaryLabelOpacity = 0.8f
val Shape = ShapeKeyTokens.CornerLarge
- val UnselectedContainerColor = ColorSchemeKeyTokens.Surface
+ val UnselectedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val UnselectedContentColor = ColorSchemeKeyTokens.OnSurface
val UnselectedIconColor = ColorSchemeKeyTokens.Primary
val UnselectedSecondaryLabelColor = ColorSchemeKeyTokens.OnSurfaceVariant
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt
index 7a65a84..f9c5b20 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-// VERSION: v0_7
+// VERSION: v0_45
// GENERATED CODE - DO NOT MODIFY BY HAND
package androidx.wear.compose.material3.tokens
@@ -29,7 +29,7 @@
val CornerExtraSmall = RoundedCornerShape(4.0.dp)
val CornerFull = CircleShape
val CornerLarge = RoundedCornerShape(26.0.dp)
- val CornerMedium = RoundedCornerShape(16.0.dp)
+ val CornerMedium = RoundedCornerShape(18.0.dp)
val CornerNone = RectangleShape
val CornerSmall = RoundedCornerShape(8.0.dp)
}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitRadioButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitRadioButtonTokens.kt
index 91b1135..afa6d71 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitRadioButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitRadioButtonTokens.kt
@@ -26,10 +26,10 @@
val DisabledSelectedSecondaryLabelOpacity = 0.8f
val DisabledSelectedSplitContainerColor = ColorSchemeKeyTokens.Primary
val DisabledSelectedSplitContainerOpacity = 0.15f
- val DisabledUnselectedContainerColor = ColorSchemeKeyTokens.Surface
+ val DisabledUnselectedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val DisabledUnselectedContentColor = ColorSchemeKeyTokens.OnSurface
val DisabledUnselectedSecondaryLabelColor = ColorSchemeKeyTokens.OnSurfaceVariant
- val DisabledUnselectedSplitContainerColor = ColorSchemeKeyTokens.SurfaceBright
+ val DisabledUnselectedSplitContainerColor = ColorSchemeKeyTokens.SurfaceContainerHigh
val LabelFont = TypographyKeyTokens.LabelMedium
val SecondaryLabelFont = TypographyKeyTokens.LabelSmall
val SelectedContainerColor = ColorSchemeKeyTokens.PrimaryContainer
@@ -39,8 +39,8 @@
val SelectedSplitContainerColor = ColorSchemeKeyTokens.Primary
val SelectedSplitContainerOpacity = 0.15f
val Shape = ShapeKeyTokens.CornerLarge
- val UnselectedContainerColor = ColorSchemeKeyTokens.Surface
+ val UnselectedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val UnselectedContentColor = ColorSchemeKeyTokens.OnSurface
val UnselectedSecondaryLabelColor = ColorSchemeKeyTokens.OnSurfaceVariant
- val UnselectedSplitContainerColor = ColorSchemeKeyTokens.SurfaceBright
+ val UnselectedSplitContainerColor = ColorSchemeKeyTokens.SurfaceContainerHigh
}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitToggleButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitToggleButtonTokens.kt
index 6241bb6..73a0847 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitToggleButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/SplitToggleButtonTokens.kt
@@ -34,14 +34,14 @@
val DisabledCheckedSplitContainerColor = ColorSchemeKeyTokens.Primary
val DisabledCheckedSplitContainerOpacity = 0.15f
val DisabledOpacity = 0.38f
- val DisabledUncheckedContainerColor = ColorSchemeKeyTokens.Surface
+ val DisabledUncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val DisabledUncheckedContentColor = ColorSchemeKeyTokens.OnSurface
val DisabledUncheckedSecondaryLabelColor = ColorSchemeKeyTokens.OnSurfaceVariant
- val DisabledUncheckedSplitContainerColor = ColorSchemeKeyTokens.SurfaceBright
+ val DisabledUncheckedSplitContainerColor = ColorSchemeKeyTokens.SurfaceContainerHigh
val LabelFont = TypographyKeyTokens.LabelMedium
val SecondaryLabelFont = TypographyKeyTokens.LabelSmall
- val UncheckedContainerColor = ColorSchemeKeyTokens.Surface
+ val UncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val UncheckedContentColor = ColorSchemeKeyTokens.OnSurface
val UncheckedSecondaryLabelColor = ColorSchemeKeyTokens.OnSurfaceVariant
- val UncheckedSplitContainerColor = ColorSchemeKeyTokens.SurfaceBright
+ val UncheckedSplitContainerColor = ColorSchemeKeyTokens.SurfaceContainerHigh
}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextToggleButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextToggleButtonTokens.kt
index 3e04ba6..fb0b3c2 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextToggleButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TextToggleButtonTokens.kt
@@ -28,10 +28,10 @@
val DisabledCheckedContainerOpacity = 0.38f
val DisabledCheckedContentColor = ColorSchemeKeyTokens.OnPrimary
val DisabledCheckedContentOpacity = 0.38f
- val DisabledUncheckedContainerColor = ColorSchemeKeyTokens.Surface
+ val DisabledUncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val DisabledUncheckedContainerOpacity = 0.38f
val DisabledUncheckedContentColor = ColorSchemeKeyTokens.OnSurfaceVariant
val DisabledUncheckedContentOpacity = 0.38f
- val UncheckedContainerColor = ColorSchemeKeyTokens.Surface
+ val UncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val UncheckedContentColor = ColorSchemeKeyTokens.OnSurfaceVariant
}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ToggleButtonTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ToggleButtonTokens.kt
index c211e57..f4c0eef 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ToggleButtonTokens.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ToggleButtonTokens.kt
@@ -32,13 +32,13 @@
val DisabledCheckedSecondaryLabelColor = ColorSchemeKeyTokens.OnPrimaryContainer
val DisabledCheckedSecondaryLabelOpacity = 0.8f
val DisabledOpacity = 0.38f
- val DisabledUncheckedContainerColor = ColorSchemeKeyTokens.Surface
+ val DisabledUncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val DisabledUncheckedContentColor = ColorSchemeKeyTokens.OnSurface
val DisabledUncheckedIconColor = ColorSchemeKeyTokens.Primary
val DisabledUncheckedSecondaryLabelColor = ColorSchemeKeyTokens.OnSurfaceVariant
val LabelFont = TypographyKeyTokens.LabelMedium
val SecondaryLabelFont = TypographyKeyTokens.LabelSmall
- val UncheckedContainerColor = ColorSchemeKeyTokens.Surface
+ val UncheckedContainerColor = ColorSchemeKeyTokens.SurfaceContainer
val UncheckedContentColor = ColorSchemeKeyTokens.OnSurface
val UncheckedIconColor = ColorSchemeKeyTokens.Primary
val UncheckedSecondaryLabelColor = ColorSchemeKeyTokens.OnSurfaceVariant
diff --git a/wear/compose/compose-navigation/build.gradle b/wear/compose/compose-navigation/build.gradle
index 4ba30f4..60d6009 100644
--- a/wear/compose/compose-navigation/build.gradle
+++ b/wear/compose/compose-navigation/build.gradle
@@ -68,7 +68,7 @@
androidx {
name = "Android Wear Compose Navigation"
- type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "WearOS Compose Navigation Library. This library makes it easier for developers" +
"to write Jetpack Compose applications for Wearable devices by providing " +
diff --git a/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt b/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt
index 2c07c85..11bf649 100644
--- a/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt
+++ b/wear/compose/compose-navigation/src/main/java/androidx/wear/compose/navigation/SwipeDismissableNavHost.kt
@@ -152,7 +152,7 @@
userSwipeEnabled: Boolean = true,
state: SwipeDismissableNavHostState = rememberSwipeDismissableNavHostState(),
) {
- val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
+ val lifecycleOwner = LocalLifecycleOwner.current
val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
"SwipeDismissableNavHost requires a ViewModelStoreOwner to be provided " +
"via LocalViewModelStoreOwner"
@@ -272,11 +272,11 @@
} else {
Modifier.graphicsLayer {
val scaleProgression = NAV_HOST_ENTER_TRANSITION_EASING_STANDARD
- .transform((animationProgress.value / 0.75f).coerceAtMost(1f))
+ .transform((animationProgress.value / 0.75f))
val opacityProgression = NAV_HOST_ENTER_TRANSITION_EASING_STANDARD
- .transform((animationProgress.value / 0.25f).coerceAtMost(1f))
- val scale = lerp(0.75f, 1f, scaleProgression)
- val opacity = lerp(0.1f, 1f, opacityProgression)
+ .transform((animationProgress.value / 0.25f))
+ val scale = lerp(0.75f, 1f, scaleProgression).coerceAtMost(1f)
+ val opacity = lerp(0.1f, 1f, opacityProgression).coerceIn(0f, 1f)
scaleX = scale
scaleY = scale
alpha = opacity
@@ -491,10 +491,10 @@
if (layerColor != Color.Unspecified) {
Canvas(Modifier.fillMaxSize()) {
val absoluteProgression =
- ((animatable.value - 0.25f).coerceAtLeast(0f) / 0.75f).coerceAtMost(1f)
+ ((animatable.value - 0.25f) / 0.75f).coerceIn(0f, 1f)
val easedProgression = NAV_HOST_ENTER_TRANSITION_EASING_STANDARD
.transform(absoluteProgression)
- val alpha = lerp(0.07f, 0f, easedProgression)
+ val alpha = lerp(0.07f, 0f, easedProgression).coerceIn(0f, 1f)
if (isRoundDevice) {
drawCircle(color = layerColor.copy(alpha))
} else {
diff --git a/wear/compose/compose-ui-tooling/build.gradle b/wear/compose/compose-ui-tooling/build.gradle
index ee2ece8..367cb5e 100644
--- a/wear/compose/compose-ui-tooling/build.gradle
+++ b/wear/compose/compose-ui-tooling/build.gradle
@@ -44,7 +44,7 @@
androidx {
name = "Wear Compose Tools"
- type = LibraryType.PUBLISHED_LIBRARY
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2023"
description = "Tools for Wear Composable"
metalavaK2UastEnabled = true
diff --git a/wear/compose/integration-tests/demos/build.gradle b/wear/compose/integration-tests/demos/build.gradle
index 9774ec0..28d0392 100644
--- a/wear/compose/integration-tests/demos/build.gradle
+++ b/wear/compose/integration-tests/demos/build.gradle
@@ -25,8 +25,8 @@
defaultConfig {
applicationId "androidx.wear.compose.integration.demos"
minSdk 25
- versionCode 22
- versionName "1.22"
+ versionCode 23
+ versionName "1.23"
}
buildTypes {
diff --git a/wear/compose/integration-tests/demos/common/src/main/java/androidx/wear/compose/integration/demos/common/Rotary.kt b/wear/compose/integration-tests/demos/common/src/main/java/androidx/wear/compose/integration/demos/common/Rotary.kt
deleted file mode 100644
index f53977e..0000000
--- a/wear/compose/integration-tests/demos/common/src/main/java/androidx/wear/compose/integration/demos/common/Rotary.kt
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright 2023 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.wear.compose.integration.demos.common
-
-import androidx.compose.foundation.MutatePriority
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.gestures.FlingBehavior
-import androidx.compose.foundation.gestures.ScrollableDefaults
-import androidx.compose.foundation.gestures.ScrollableState
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.input.rotary.onRotaryScrollEvent
-import androidx.compose.ui.unit.dp
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
-import androidx.wear.compose.foundation.lazy.AutoCenteringParams
-import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
-import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults
-import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
-import androidx.wear.compose.foundation.lazy.ScalingLazyListState
-import androidx.wear.compose.foundation.lazy.ScalingParams
-import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.foundation.rememberActiveFocusRequester
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.receiveAsFlow
-
-@Suppress("DEPRECATION")
-@OptIn(ExperimentalWearFoundationApi::class)
-@Composable
-fun ScalingLazyColumnWithRSB(
- modifier: Modifier = Modifier,
- state: ScalingLazyListState = rememberScalingLazyListState(),
- contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
- scalingParams: ScalingParams = ScalingLazyColumnDefaults.scalingParams(),
- reverseLayout: Boolean = false,
- snap: Boolean = true,
- horizontalAlignment: Alignment.Horizontal = Alignment.Start,
- verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(
- space = 4.dp,
- alignment = if (!reverseLayout) Alignment.Top else Alignment.Bottom
- ),
- autoCentering: AutoCenteringParams? = AutoCenteringParams(),
- content: ScalingLazyListScope.() -> Unit
-) {
- val flingBehavior = if (snap) ScalingLazyColumnDefaults.snapFlingBehavior(
- state = state
- ) else ScrollableDefaults.flingBehavior()
- val focusRequester = rememberActiveFocusRequester()
- ScalingLazyColumn(
- modifier = modifier.rsbScroll(
- scrollableState = state,
- flingBehavior = flingBehavior,
- focusRequester = focusRequester
- ),
- state = state,
- contentPadding = contentPadding,
- reverseLayout = reverseLayout,
- scalingParams = scalingParams,
- flingBehavior = flingBehavior,
- horizontalAlignment = horizontalAlignment,
- verticalArrangement = verticalArrangement,
- autoCentering = autoCentering,
- content = content
- )
-}
-
-@Suppress("ComposableModifierFactory")
-@Composable
-@Deprecated("Use .rotary modifier instead")
-fun Modifier.rsbScroll(
- scrollableState: ScrollableState,
- flingBehavior: FlingBehavior,
- focusRequester: FocusRequester? = null
-): Modifier {
- val channel = remember {
- Channel<TimestampedDelta>(
- capacity = 10,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
- )
- }
-
- var lastTimeMillis = remember { 0L }
- var smoothSpeed = remember { 0f }
- val speedWindowMillis = 200L
- val timeoutToFling = 100L
-
- return composed {
- var rsbScrollInProgress by remember { mutableStateOf(false) }
- LaunchedEffect(rsbScrollInProgress) {
- if (rsbScrollInProgress) {
- scrollableState.scroll(MutatePriority.UserInput) {
- channel.receiveAsFlow().collectLatest {
- val toScroll = if (lastTimeMillis > 0L && it.time > lastTimeMillis) {
- val timeSinceLastEventMillis = it.time - lastTimeMillis
-
- // Speed is in pixels per second.
- val speed = it.delta * 1000 / timeSinceLastEventMillis
- val cappedElapsedTimeMillis =
- timeSinceLastEventMillis.coerceAtMost(speedWindowMillis)
- smoothSpeed = ((speedWindowMillis - cappedElapsedTimeMillis) * speed +
- cappedElapsedTimeMillis * smoothSpeed) / speedWindowMillis
- smoothSpeed * cappedElapsedTimeMillis / 1000
- } else {
- 0f
- }
- lastTimeMillis = it.time
- scrollBy(toScroll)
-
- // If more than the given time pass, start a fling.
- delay(timeoutToFling)
-
- lastTimeMillis = 0L
-
- if (smoothSpeed != 0f) {
- val launchSpeed = smoothSpeed
- smoothSpeed = 0f
- with(flingBehavior) {
- performFling(launchSpeed)
- }
- rsbScrollInProgress = false
- }
- }
- }
- }
- }
- this
- .onRotaryScrollEvent {
- channel.trySend(TimestampedDelta(it.uptimeMillis, it.verticalScrollPixels))
- rsbScrollInProgress = true
- true
- }
- .let {
- if (focusRequester != null) {
- it
- .focusRequester(focusRequester)
- .focusable()
- } else it
- }
- }
-}
-
-internal data class TimestampedDelta(val time: Long, val delta: Float)
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ButtonDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ButtonDemo.kt
index 93d971b..22fa29d 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ButtonDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ButtonDemo.kt
@@ -35,8 +35,8 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
import androidx.wear.compose.material.Button
import androidx.wear.compose.material.ButtonDefaults
import androidx.wear.compose.material.CompactButton
@@ -115,7 +115,7 @@
var enabled by remember { mutableStateOf(true) }
val context = LocalContext.current
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
@@ -315,7 +315,7 @@
@Composable
fun ButtonGallery() {
val state = rememberScalingLazyListState()
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
state = state,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize(),
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/CardDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/CardDemo.kt
index b20cf49..fc7b485 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/CardDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/CardDemo.kt
@@ -32,7 +32,7 @@
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material.AppCard
import androidx.wear.compose.material.Card
import androidx.wear.compose.material.CardDefaults
@@ -42,7 +42,7 @@
@Composable
fun CardDemo() {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(
space = 4.dp,
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ChipDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ChipDemo.kt
index 0c06756..0c549bd 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ChipDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ChipDemo.kt
@@ -49,7 +49,6 @@
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipColors
import androidx.wear.compose.material.ChipDefaults
@@ -70,7 +69,7 @@
var enabled by remember { mutableStateOf(true) }
var chipStyle by remember { mutableStateOf(ChipStyle.Primary) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterVertically),
) {
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
index 8929f17..c187929 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
@@ -30,6 +30,7 @@
import androidx.wear.compose.foundation.SwipeToDismissKeys
import androidx.wear.compose.foundation.SwipeToDismissValue
import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.ScalingLazyListState
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
@@ -38,7 +39,6 @@
import androidx.wear.compose.integration.demos.common.Demo
import androidx.wear.compose.integration.demos.common.DemoCategory
import androidx.wear.compose.integration.demos.common.DemoParameters
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipDefaults
import androidx.wear.compose.material.ListHeader
@@ -122,13 +122,12 @@
) {
val state = rememberScalingLazyListState()
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.testTag(DemoListTag),
state = scrollStates[scrollStateIndex],
- snap = false,
autoCentering = AutoCenteringParams(itemIndex = if (category.demos.size >= 2) 2 else 1),
) {
item {
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt
index 12b9de3..2276907 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt
@@ -40,6 +40,7 @@
import androidx.wear.compose.foundation.samples.SimpleScalingLazyColumnWithSnap
import androidx.wear.compose.foundation.samples.SimpleSwipeToDismissBox
import androidx.wear.compose.foundation.samples.StatefulSwipeToDismissBox
+import androidx.wear.compose.integration.demos.common.Centralize
import androidx.wear.compose.integration.demos.common.ComposableDemo
import androidx.wear.compose.integration.demos.common.DemoCategory
import androidx.wear.compose.material.samples.SwipeToRevealCardSample
@@ -165,18 +166,31 @@
"Samples",
listOf(
ComposableDemo("Material S2R Chip") { params ->
- SwipeToRevealChipSample(params.swipeToDismissBoxState)
+ Centralize {
+ SwipeToRevealChipSample(params.swipeToDismissBoxState)
+ }
},
ComposableDemo("Material S2R Card") { params ->
- SwipeToRevealCardSample(params.swipeToDismissBoxState)
+ Centralize {
+ SwipeToRevealCardSample(params.swipeToDismissBoxState)
+ }
},
)
),
DemoCategory(
"Demos",
listOf(
- ComposableDemo("S2R Chip") { params ->
- SwipeToRevealChips(params.swipeToDismissBoxState)
+ ComposableDemo("S2R Chip, 2 actions") { params ->
+ SwipeToRevealChips(
+ params.swipeToDismissBoxState,
+ includeSecondaryAction = true
+ )
+ },
+ ComposableDemo("S2R Chip, 1 action") { params ->
+ SwipeToRevealChips(
+ params.swipeToDismissBoxState,
+ includeSecondaryAction = false
+ )
},
ComposableDemo("S2R Card") { params ->
SwipeToRevealCards(params.swipeToDismissBoxState)
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt
index be9889d..b68787e 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt
@@ -27,7 +27,6 @@
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -67,12 +66,10 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
-import androidx.wear.compose.integration.demos.common.rsbScroll
import androidx.wear.compose.material.Button
import androidx.wear.compose.material.Icon
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Picker
-import androidx.wear.compose.material.PickerDefaults
import androidx.wear.compose.material.PickerGroup
import androidx.wear.compose.material.PickerGroupItem
import androidx.wear.compose.material.PickerGroupState
@@ -194,7 +191,7 @@
horizontalArrangement = Arrangement.Center,
) {
PickerGroup(
- pickerGroupItemWithRSB(
+ PickerGroupItem(
pickerState = hourState,
modifier = Modifier
.size(40.dp, 100.dp),
@@ -207,7 +204,7 @@
contentDescription = hourContentDescription,
option = pickerOption
),
- pickerGroupItemWithRSB(
+ PickerGroupItem(
pickerState = minuteState,
modifier = Modifier
.size(40.dp, 100.dp),
@@ -220,7 +217,7 @@
contentDescription = minuteContentDescription,
option = pickerOption
),
- pickerGroupItemWithRSB(
+ PickerGroupItem(
pickerState = secondState,
modifier = Modifier
.size(40.dp, 100.dp),
@@ -400,7 +397,7 @@
}
Spacer(Modifier.width(16.dp))
PickerGroup(
- pickerGroupItemWithRSB(
+ PickerGroupItem(
pickerState = hourState,
modifier = Modifier.size(48.dp, 100.dp),
onSelected = {
@@ -412,7 +409,7 @@
contentDescription = hoursContentDescription,
option = pickerTextOption(textStyle) { "%02d".format(it + 1) }
),
- pickerGroupItemWithRSB(
+ PickerGroupItem(
pickerState = minuteState,
modifier = Modifier.size(48.dp, 100.dp),
onSelected = {
@@ -424,7 +421,7 @@
contentDescription = minutesContentDescription,
option = pickerTextOption(textStyle) { "%02d".format(it) }
),
- pickerGroupItemWithRSB(
+ PickerGroupItem(
pickerState = periodState,
modifier = Modifier.size(64.dp, 100.dp),
contentDescription = periodContentDescription,
@@ -666,7 +663,7 @@
horizontalArrangement = Arrangement.Center
) {
PickerGroup(
- pickerGroupItemWithRSB(
+ PickerGroupItem(
pickerState = datePickerState.dayState,
modifier = Modifier.size(dayWidth, 100.dp),
contentDescription = dayContentDescription,
@@ -680,7 +677,7 @@
"%d".format(datePickerState.currentDay(it))
}
),
- pickerGroupItemWithRSB(
+ PickerGroupItem(
pickerState = datePickerState.monthState,
modifier = Modifier.size(monthWidth, 100.dp),
onSelected = {
@@ -694,7 +691,7 @@
shortMonthNames[(datePickerState.currentMonth(it) - 1) % 12]
}
),
- pickerGroupItemWithRSB(
+ PickerGroupItem(
pickerState = datePickerState.yearState,
modifier = Modifier.size(yearWidth, 100.dp),
onSelected = {
@@ -817,27 +814,6 @@
Spacer(Modifier.width(width))
}
-@Suppress("DEPRECATION")
-@Composable
-fun pickerGroupItemWithRSB(
- pickerState: PickerState,
- modifier: Modifier,
- contentDescription: String?,
- onSelected: () -> Unit,
- readOnlyLabel: @Composable (BoxScope.() -> Unit)? = null,
- option: @Composable PickerScope.(optionIndex: Int, pickerSelected: Boolean) -> Unit
-) = PickerGroupItem(
- pickerState = pickerState,
- modifier = modifier.rsbScroll(
- scrollableState = pickerState,
- flingBehavior = PickerDefaults.flingBehavior(pickerState)
- ),
- contentDescription = contentDescription,
- onSelected = onSelected,
- readOnlyLabel = readOnlyLabel,
- option = option
-)
-
@Composable
fun PickerWithoutGradient() {
val items = listOf("One", "Two", "Three", "Four", "Five")
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PlaceholderDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PlaceholderDemo.kt
index 4aa990c..a736de1 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PlaceholderDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PlaceholderDemo.kt
@@ -41,8 +41,8 @@
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.integration.demos.common.Centralize
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
import androidx.wear.compose.material.AppCard
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipColors
@@ -62,7 +62,7 @@
fun PlaceholderChips() {
var resetCount by remember { mutableIntStateOf(0) }
Box {
- ScalingLazyColumnWithRSB {
+ ScalingLazyColumn {
item {
ListHeader {
Text(text = "Primary Label Center Aligned", textAlign = TextAlign.Center)
@@ -313,7 +313,7 @@
}
}
- ScalingLazyColumnWithRSB {
+ ScalingLazyColumn {
item {
ListHeader {
Text("Overlaid Placeholders", textAlign = TextAlign.Center)
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt
index 17a6fa6..628206c 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt
@@ -32,7 +32,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material.Button
import androidx.wear.compose.material.ButtonDefaults
import androidx.wear.compose.material.CircularProgressIndicator
@@ -58,7 +58,7 @@
val animatedProgress: Float by animateFloatAsState(targetValue = progress)
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 8.dp),
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ScrollAwayDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ScrollAwayDemos.kt
index 60e8213..7bb195c 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ScrollAwayDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ScrollAwayDemos.kt
@@ -36,10 +36,11 @@
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
import androidx.wear.compose.foundation.rememberActiveFocusRequester
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
-import androidx.wear.compose.integration.demos.common.rsbScroll
+import androidx.wear.compose.foundation.rotary.RotaryScrollableDefaults
+import androidx.wear.compose.foundation.rotary.rotaryScrollable
import androidx.wear.compose.material.Card
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipDefaults
@@ -154,9 +155,11 @@
Column(
modifier = Modifier
.verticalScroll(scrollState)
- .rsbScroll(
- scrollableState = scrollState,
- flingBehavior = ScrollableDefaults.flingBehavior(),
+ .rotaryScrollable(
+ RotaryScrollableDefaults.behavior(
+ scrollableState = scrollState,
+ flingBehavior = ScrollableDefaults.flingBehavior()
+ ),
focusRequester = focusRequester
)
) {
@@ -190,9 +193,11 @@
) {
LazyColumn(
state = scrollState,
- modifier = Modifier.rsbScroll(
- scrollableState = scrollState,
- flingBehavior = ScrollableDefaults.flingBehavior(),
+ modifier = Modifier.rotaryScrollable(
+ RotaryScrollableDefaults.behavior(
+ scrollableState = scrollState,
+ flingBehavior = ScrollableDefaults.flingBehavior()
+ ),
focusRequester = focusRequester
)
) {
@@ -230,7 +235,7 @@
PositionIndicator(scalingLazyListState = scrollState)
}
) {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
contentPadding = PaddingValues(10.dp),
state = scrollState,
autoCentering = AutoCenteringParams(itemIndex = 1, itemOffset = 0)
@@ -272,7 +277,7 @@
PositionIndicator(scalingLazyListState = scrollState)
}
) {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
contentPadding = PaddingValues(10.dp),
state = scrollState,
) {
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SelectableChipDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SelectableChipDemo.kt
index c187ef4..7137f26 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SelectableChipDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SelectableChipDemo.kt
@@ -32,9 +32,9 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.ScalingLazyListState
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
import androidx.wear.compose.material.ListHeader
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.RadioButton
@@ -60,7 +60,7 @@
var radioIconWithSecondarySelected by remember { mutableStateOf(true) }
var splitWithRadioIconSelected by remember { mutableStateOf(true) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
state = scrollState,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterVertically),
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SettingsDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SettingsDemo.kt
index 5f80379..cb68ec5 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SettingsDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SettingsDemo.kt
@@ -22,8 +22,8 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipDefaults
import androidx.wear.compose.material.Icon
@@ -41,7 +41,7 @@
TimeText(modifier = Modifier.scrollAway(scalingLazyListState))
}
) {
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
state = scalingLazyListState,
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SliderDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SliderDemo.kt
index a1d22eb..585c7d1 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SliderDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SliderDemo.kt
@@ -34,7 +34,7 @@
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.lazy.AutoCenteringParams
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material.Icon
import androidx.wear.compose.material.InlineSlider
import androidx.wear.compose.material.InlineSliderColors
@@ -49,7 +49,7 @@
var valueWithSegments by remember { mutableFloatStateOf(2f) }
var enabled by remember { mutableStateOf(true) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(
space = 4.dp,
@@ -105,7 +105,7 @@
var valueWithoutSegments by remember { mutableIntStateOf(5) }
var valueWithSegments by remember { mutableIntStateOf(2) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(
space = 4.dp,
@@ -167,7 +167,7 @@
var numberOfSegments by remember { mutableFloatStateOf(5f) }
var progress by remember { mutableFloatStateOf(10f) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(
space = 4.dp,
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt
index 6bb6e21..aaa66be 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt
@@ -42,6 +42,7 @@
import androidx.wear.compose.foundation.ExpandableState
import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
import androidx.wear.compose.foundation.RevealActionType
+import androidx.wear.compose.foundation.RevealState
import androidx.wear.compose.foundation.RevealValue
import androidx.wear.compose.foundation.SwipeToDismissBoxState
import androidx.wear.compose.foundation.edgeSwipeToDismiss
@@ -67,9 +68,12 @@
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-@OptIn(ExperimentalWearFoundationApi::class, ExperimentalWearMaterialApi::class)
+@OptIn(ExperimentalWearFoundationApi::class)
@Composable
-fun SwipeToRevealChips(swipeToDismissBoxState: SwipeToDismissBoxState) {
+fun SwipeToRevealChips(
+ swipeToDismissBoxState: SwipeToDismissBoxState,
+ includeSecondaryAction: Boolean
+) {
val expandableStateMapping = rememberExpandableStateMapping<Int>(
initiallyExpanded = { true }
)
@@ -90,7 +94,7 @@
var undoActionEnabled by remember { mutableStateOf(true) }
val revealState = rememberRevealState()
val coroutineScope = rememberCoroutineScope()
- val deleteItem = {
+ val deleteItem: () -> Unit = {
coroutineScope.launch {
revealState.animateTo(RevealValue.Revealed)
@@ -103,7 +107,7 @@
}
}
}
- val addItem = {
+ val addItem: () -> Unit = {
coroutineScope.launch {
revealState.animateTo(RevealValue.Revealed)
itemCount++
@@ -116,85 +120,33 @@
}
}
}
- if (expanded) {
- SwipeToRevealChip(
- modifier = Modifier
- .edgeSwipeToDismiss(swipeToDismissBoxState)
- .semantics {
- customActions = listOf(
- CustomAccessibilityAction("Delete") {
- deleteItem()
- true
- },
- CustomAccessibilityAction("Duplicate") {
- addItem()
- true
- }
- )
- },
- revealState = revealState,
- onFullSwipe = { deleteItem() },
- primaryAction = {
- SwipeToRevealPrimaryAction(
- revealState = revealState,
- icon = {
- Icon(
- SwipeToRevealDefaults.Delete,
- contentDescription = "Delete"
- )
- },
- label = { Text(text = "Delete") },
- onClick = { deleteItem() },
- )
- },
- secondaryAction = {
- SwipeToRevealSecondaryAction(
- revealState = revealState,
- content = {
- Icon(Icons.Outlined.Add, contentDescription = "Duplicate")
- },
- onClick = { addItem() }
- )
- },
- undoPrimaryAction = {
- SwipeToRevealUndoAction(
- revealState = revealState,
- label = { Text("Undo Primary Action") },
- onClick = {
- if (undoActionEnabled) {
- coroutineScope.launch {
- // reset the state when undo is clicked
- revealState.animateTo(RevealValue.Covered)
- revealState.lastActionType = RevealActionType.None
- }
- }
- }
- )
- },
- undoSecondaryAction = {
- SwipeToRevealUndoAction(
- revealState = revealState,
- label = { Text("Undo Secondary Action") },
- onClick = {
- coroutineScope.launch {
- itemCount--
- // reset the state when undo is clicked
- revealState.animateTo(RevealValue.Covered)
- revealState.lastActionType = RevealActionType.None
- }
- }
- )
+ val undoDeleteItem: () -> Unit = {
+ if (undoActionEnabled) {
+ coroutineScope.launch {
+ // reset the state when undo is clicked
+ revealState.animateTo(RevealValue.Covered)
+ revealState.lastActionType = RevealActionType.None
}
- ) {
- Chip(
- onClick = { /*TODO*/ },
- colors = ChipDefaults.secondaryChipColors(),
- modifier = Modifier.fillMaxWidth(),
- label = {
- Text("Chip #$it")
- }
- )
}
+ }
+ val undoAddItem: () -> Unit = {
+ coroutineScope.launch {
+ itemCount--
+ // reset the state when undo is clicked
+ revealState.animateTo(RevealValue.Covered)
+ revealState.lastActionType = RevealActionType.None
+ }
+ }
+ if (expanded) {
+ SwipeToRevealChipExpandable(
+ modifier = Modifier.edgeSwipeToDismiss(swipeToDismissBoxState),
+ text = "Chip #$it",
+ revealState = revealState,
+ onDeleteAction = deleteItem,
+ onUndoDelete = undoDeleteItem,
+ onDuplicateAction = addItem.takeIf { includeSecondaryAction },
+ onUndoDuplicate = undoAddItem.takeIf { includeSecondaryAction }
+ )
} else {
Spacer(modifier = Modifier.width(200.dp))
}
@@ -203,6 +155,86 @@
}
}
+@OptIn(ExperimentalWearFoundationApi::class, ExperimentalWearMaterialApi::class)
+@Composable
+private fun SwipeToRevealChipExpandable(
+ modifier: Modifier = Modifier,
+ text: String,
+ revealState: RevealState,
+ onDeleteAction: () -> Unit,
+ onUndoDelete: () -> Unit,
+ onDuplicateAction: (() -> Unit)?,
+ onUndoDuplicate: (() -> Unit)?
+) {
+ SwipeToRevealChip(
+ modifier = modifier.semantics {
+ customActions = listOfNotNull(
+ CustomAccessibilityAction("Delete") {
+ onDeleteAction()
+ true
+ },
+ onDuplicateAction?.let {
+ CustomAccessibilityAction("Duplicate") {
+ onDuplicateAction()
+ true
+ }
+ }
+ )
+ },
+ revealState = revealState,
+ onFullSwipe = onDeleteAction,
+ primaryAction = {
+ SwipeToRevealPrimaryAction(
+ revealState = revealState,
+ icon = {
+ Icon(
+ SwipeToRevealDefaults.Delete,
+ contentDescription = "Delete"
+ )
+ },
+ label = { Text(text = "Delete") },
+ onClick = onDeleteAction,
+ )
+ },
+ secondaryAction = onDuplicateAction?.let {
+ {
+ SwipeToRevealSecondaryAction(
+ revealState = revealState,
+ content = {
+ Icon(Icons.Outlined.Add, contentDescription = "Duplicate")
+ },
+ onClick = onDuplicateAction
+ )
+ }
+ },
+ undoPrimaryAction = {
+ SwipeToRevealUndoAction(
+ revealState = revealState,
+ label = { Text("Undo Delete") },
+ onClick = onUndoDelete
+ )
+ },
+ undoSecondaryAction = onUndoDuplicate?.let {
+ {
+ SwipeToRevealUndoAction(
+ revealState = revealState,
+ label = { Text("Undo Duplicate") },
+ onClick = onUndoDuplicate
+ )
+ }
+ }
+ ) {
+ Chip(
+ onClick = { /*TODO*/ },
+ colors = ChipDefaults.secondaryChipColors(),
+ modifier = Modifier.fillMaxWidth(),
+ label = {
+ Text(text)
+ }
+ )
+ }
+}
+
@Composable
fun SwipeToRevealCards(swipeToDismissBoxState: SwipeToDismissBoxState) {
val emailMap = mutableMapOf(
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ThemeDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ThemeDemo.kt
index 7807eaf..0306e38 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ThemeDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ThemeDemo.kt
@@ -29,13 +29,13 @@
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
@Composable
fun ThemeFonts() {
- ScalingLazyColumnWithRSB {
+ ScalingLazyColumn {
item {
ThemeFontRow(style = MaterialTheme.typography.display1, description = "display1")
}
@@ -88,7 +88,7 @@
@Composable
fun ThemeColors() {
- ScalingLazyColumnWithRSB {
+ ScalingLazyColumn {
item {
ThemeColorRow(
backgroundColor = MaterialTheme.colors.background,
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ToggleChipDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ToggleChipDemo.kt
index e932b6a3..bcebbf3 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ToggleChipDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ToggleChipDemo.kt
@@ -32,9 +32,9 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.ScalingLazyListState
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.integration.demos.common.ScalingLazyColumnWithRSB
import androidx.wear.compose.material.Checkbox
import androidx.wear.compose.material.CheckboxDefaults
import androidx.wear.compose.material.ListHeader
@@ -66,7 +66,7 @@
var switchIconWithIconChecked by remember { mutableStateOf(true) }
var splitWithCustomColorChecked by remember { mutableStateOf(true) }
- ScalingLazyColumnWithRSB(
+ ScalingLazyColumn(
state = scrollState,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterVertically),
diff --git a/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/Fingerprint.java b/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/Fingerprint.java
index d92eaa5..9693204 100644
--- a/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/Fingerprint.java
+++ b/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/Fingerprint.java
@@ -40,10 +40,34 @@
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public final class Fingerprint {
+ private static final int[] POW_31 = {
+ 1,
+ 31,
+ 961,
+ 29791,
+ 923521,
+ 28629151,
+ 887503681,
+ 1742810335,
+ -1807454463,
+ -196513505,
+ -1796951359,
+ 129082719,
+ -293403007,
+ -505558625,
+ 1507551809,
+ -510534177,
+ 1353309697,
+ -997072353,
+ -844471871,
+ -408824225
+ };
private static final int DEFAULT_VALUE = 0;
private final int selfTypeValue;
private int selfPropsValue;
private int childNodesValue;
+ // itself + children + grand children + grand grand children + ...
+ private int subNodeCount;
private @Nullable List<Fingerprint> childNodes;
public Fingerprint(int selfTypeValue) {
@@ -51,6 +75,7 @@
this.selfPropsValue = DEFAULT_VALUE;
this.childNodesValue = DEFAULT_VALUE;
this.childNodes = null;
+ this.subNodeCount = 1; // self
}
public Fingerprint(@NonNull NodeFingerprint proto) {
@@ -102,7 +127,12 @@
childNodes = new ArrayList<>();
}
childNodes.add(childNode);
- childNodesValue = (31 * childNodesValue) + childNode.aggregateValueAsInt();
+ // We need to include the number of grandchildren of the new node, otherwise swapping the
+ // place of "b" and "c" in a layout like "[a b] [c d]" could result in the same fingerprint.
+ int coeff = pow31Unsafe(childNode.subNodeCount);
+ childNodesValue =
+ (coeff * childNodesValue) + (childNodes.size() * childNode.aggregateValueAsInt());
+ subNodeCount += childNode.subNodeCount;
}
/** Record a property value being updated. */
@@ -115,6 +145,25 @@
selfPropsValue = (31 * selfPropsValue) + entry;
}
+ /**
+ * An int version of {@link Math#pow(double, double)} }. The result will overflow if it's larger
+ * than {@link Integer#MAX_VALUE}.
+ *
+ * <p>Note that this only support positive exponents
+ */
+ private static int pow31Unsafe(int n) {
+ // Check for available precomputed result (as an optimization).
+ if (n < POW_31.length) {
+ return POW_31[n];
+ }
+
+ int result = POW_31[POW_31.length - 1];
+ for (int i = POW_31.length; i <= n; i++) {
+ result *= 31;
+ }
+ return result;
+ }
+
NodeFingerprint toProto() {
NodeFingerprint.Builder builder = NodeFingerprint.newBuilder();
if (selfTypeValue() != 0) {
diff --git a/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/FingerprintTest.java b/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/FingerprintTest.java
index 2f99ad8..a6eda1c 100644
--- a/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/FingerprintTest.java
+++ b/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/FingerprintTest.java
@@ -28,9 +28,13 @@
public final class FingerprintTest {
private static final int SELF_TYPE_VALUE = 1234;
private static final int FIELD_1 = 1;
- private static final int VALUE_HASH1 = 10;
-
- private static final int DISCARDED_VALUE = -1;
+ private static final int VALUE_HASH1 = 101;
+ private static final int FIELD_2 = 2;
+ private static final int VALUE_HASH2 = 202;
+ private static final int FIELD_3 = 3;
+ private static final int VALUE_HASH3 = 301;
+ private static final int FIELD_4 = 4;
+ private static final int VALUE_HASH4 = 401;
@Test
public void addChildNode() {
@@ -59,4 +63,33 @@
assertThat(child.selfTypeValue()).isEqualTo(SELF_TYPE_VALUE);
assertThat(child.selfPropsValue()).isEqualTo(31 * FIELD_1 + VALUE_HASH1);
}
+
+ @Test
+ public void childNodeOrderMatters() {
+ Fingerprint root1 = new Fingerprint(SELF_TYPE_VALUE);
+ Fingerprint root2 = new Fingerprint(SELF_TYPE_VALUE);
+ Fingerprint parent12 = createParentFor(FIELD_1, VALUE_HASH1, FIELD_2, VALUE_HASH2);
+ Fingerprint parent34 = createParentFor(FIELD_3, VALUE_HASH3, FIELD_4, VALUE_HASH4);
+ Fingerprint parent13 = createParentFor(FIELD_1, VALUE_HASH1, FIELD_3, VALUE_HASH3);
+ Fingerprint parent24 = createParentFor(FIELD_2, VALUE_HASH2, FIELD_4, VALUE_HASH4);
+
+ root1.addChildNode(parent12);
+ root1.addChildNode(parent34);
+ root2.addChildNode(parent13);
+ root2.addChildNode(parent24);
+
+ assertThat(root1.childNodesValue()).isNotEqualTo(root2.childNodesValue());
+ }
+
+ private Fingerprint createParentFor(
+ int fieldId1, int fieldValue1, int fieldId2, int fieldValue2) {
+ Fingerprint parentFingerPrint = new Fingerprint(SELF_TYPE_VALUE);
+ Fingerprint child1FingerPrint = new Fingerprint(SELF_TYPE_VALUE);
+ child1FingerPrint.recordPropertyUpdate(fieldId1, fieldValue1);
+ parentFingerPrint.addChildNode(child1FingerPrint);
+ Fingerprint child2FingerPrint = new Fingerprint(SELF_TYPE_VALUE);
+ child2FingerPrint.recordPropertyUpdate(fieldId2, fieldValue2);
+ parentFingerPrint.addChildNode(child2FingerPrint);
+ return parentFingerPrint;
+ }
}
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/dynamic.proto b/wear/protolayout/protolayout-proto/src/main/proto/dynamic.proto
index f0a0f04..0b7b008 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/dynamic.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/dynamic.proto
@@ -634,7 +634,7 @@
string zone_id = 2;
}
-// The datetime part to retrieve using ZonedDateTimePartOp.
+// The date-time part to retrieve using ZonedDateTimePartOp.
enum ZonedDateTimePartType {
// Undefined date-time part type.
ZONED_DATE_TIME_PART_UNDEFINED = 0;
@@ -654,12 +654,12 @@
ZONED_DATE_TIME_PART_YEAR = 7;
}
-// Retrieve the specified datetime part of a DynamicZonedDateTime instance as a
+// Retrieve the specified date-time part of a DynamicZonedDateTime instance as a
// DynamicInt32.
message GetZonedDateTimePartOp {
- // The zoned datetime input.
+ // The zoned date-time input.
DynamicZonedDateTime input = 1;
- // The datetime part to retrieve.
+ // The date-time part to retrieve.
ZonedDateTimePartType part_type = 2;
}
@@ -755,7 +755,6 @@
DurationPartType duration_part = 2;
}
-
// A dynamic Instant which sources its data from the a state entry.
message StateInstantSource {
// The key in the state to bind to.
@@ -772,4 +771,4 @@
// The namespace for the state key.
string source_namespace = 2;
-}
\ No newline at end of file
+}
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutExtensionViewProvider.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutExtensionViewProvider.java
index ca4f923..42df804 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutExtensionViewProvider.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/ProtoLayoutExtensionViewProvider.java
@@ -1,5 +1,3 @@
-package androidx.wear.protolayout.renderer;
-
/*
* Copyright 2023 The Android Open Source Project
*
@@ -15,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package androidx.wear.protolayout.renderer;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
/**
* View provider for a View ExtensionLayoutElement. This should check that the given renderer
@@ -28,7 +28,7 @@
* payload. The returned View will be measured using the width/height from the {@link
* androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement} message.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY)
+@RestrictTo(Scope.LIBRARY)
public interface ProtoLayoutExtensionViewProvider {
/**
* Return an Android View from the given renderer extension. In case of an error, this method
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java
index 65f4fa2..6860d79 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java
@@ -35,7 +35,7 @@
import java.util.Collections;
import java.util.List;
-/** Utility to diff 2 proto layouts in order to be able to partially update the display. */
+/** Utility to diff two proto layouts in order to be able to partially update the display. */
@RestrictTo(Scope.LIBRARY_GROUP)
public class ProtoLayoutDiffer {
/** Prefix for all node IDs generated by this differ. */
@@ -63,7 +63,9 @@
@RestrictTo(Scope.LIBRARY_GROUP)
public static final int FIRST_CHILD_INDEX = 0;
- private enum NodeChangeType {
+ /** Type of the change applied to the node. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public enum NodeChangeType {
NO_CHANGE,
CHANGE_IN_SELF_ONLY,
CHANGE_IN_SELF_AND_ALL_CHILDREN,
@@ -108,8 +110,8 @@
}
@NonNull
- TreeNodeWithChange withChange(boolean isSelfOnlyChange) {
- return new TreeNodeWithChange(this, isSelfOnlyChange);
+ TreeNodeWithChange withChange(@NonNull NodeChangeType nodeChangeType) {
+ return new TreeNodeWithChange(this, nodeChangeType);
}
}
@@ -117,11 +119,11 @@
@RestrictTo(Scope.LIBRARY_GROUP)
public static final class TreeNodeWithChange {
@NonNull private final TreeNode mTreeNode;
- private final boolean mIsSelfOnlyChange;
+ @NonNull private final NodeChangeType mNodeChangeType;
- TreeNodeWithChange(@NonNull TreeNode treeNode, boolean isSelfOnlyChange) {
+ TreeNodeWithChange(@NonNull TreeNode treeNode, @NonNull NodeChangeType nodeChangeType) {
this.mTreeNode = treeNode;
- this.mIsSelfOnlyChange = isSelfOnlyChange;
+ this.mNodeChangeType = nodeChangeType;
}
/**
@@ -167,7 +169,20 @@
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public boolean isSelfOnlyChange() {
- return mIsSelfOnlyChange;
+ switch (mNodeChangeType) {
+ case CHANGE_IN_SELF_ONLY:
+ case CHANGE_IN_SELF_AND_SOME_CHILDREN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** Returns the {@link NodeChangeType} of this {@link TreeNodeWithChange}. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public NodeChangeType getChangeType() {
+ return mNodeChangeType;
}
}
@@ -295,15 +310,14 @@
@NonNull TreeNode node,
@NonNull List<TreeNodeWithChange> changedNodes)
throws InconsistentFingerprintException {
- switch (getChangeType(prevNodeFingerprint, node.mFingerprint)) {
+ NodeChangeType changeType = getChangeType(prevNodeFingerprint, node.mFingerprint);
+ switch (changeType) {
case CHANGE_IN_SELF_ONLY:
- changedNodes.add(node.withChange(/* isSelfOnlyChange= */ true));
- break;
case CHANGE_IN_SELF_AND_ALL_CHILDREN:
- changedNodes.add(node.withChange(/* isSelfOnlyChange= */ false));
+ changedNodes.add(node.withChange(changeType));
break;
case CHANGE_IN_SELF_AND_SOME_CHILDREN:
- changedNodes.add(node.withChange(/* isSelfOnlyChange= */ true));
+ changedNodes.add(node.withChange(changeType));
addChangedChildNodes(prevNodeFingerprint, node, changedNodes);
break;
case CHANGE_IN_CHILDREN:
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/PositionIdTree.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/PositionIdTree.java
index 18b5fa9..c2ad693 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/PositionIdTree.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/PositionIdTree.java
@@ -25,6 +25,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.VisibleForTesting;
import androidx.wear.protolayout.renderer.dynamicdata.PositionIdTree.TreeNode;
@@ -45,10 +47,11 @@
*
* <p>This class is not thread-safe.
*/
-final class PositionIdTree<T extends TreeNode> {
+@RestrictTo(Scope.LIBRARY_GROUP)
+public final class PositionIdTree<T extends TreeNode> {
/** Interface for nodes stored in this tree. */
- interface TreeNode {
+ public interface TreeNode {
/** Will be called after a node is removed from the tree. */
void destroy();
}
@@ -61,7 +64,7 @@
}
/** Removes all of the nodes in the tree and calls their {@link TreeNode#destroy()}. */
- void clear() {
+ public void clear() {
mPosIdToTreeNode.values().forEach(TreeNode::destroy);
mPosIdToTreeNode.clear();
}
@@ -71,7 +74,7 @@
* {@link TreeNode#destroy()} on all of the removed node. Note that the {@code posId} node won't
* be removed.
*/
- void removeChildNodesFor(@NonNull String posId) {
+ public void removeChildNodesFor(@NonNull String posId) {
removeChildNodesFor(posId, /* removeRoot= */ false);
}
@@ -92,7 +95,7 @@
* Adds the {@code newNode} to the tree. If the tree already contains a node at that position,
* the old node will be removed and will be destroyed.
*/
- void addOrReplace(@NonNull String posId, @NonNull T newNode) {
+ public void addOrReplace(@NonNull String posId, @NonNull T newNode) {
T oldNode = mPosIdToTreeNode.put(posId, newNode);
if (oldNode != null) {
oldNode.destroy();
@@ -107,16 +110,35 @@
/** Returns the node with {@code posId} or null if it doesn't exist. */
@Nullable
- T get(String posId) {
+ public T get(@NonNull String posId) {
return mPosIdToTreeNode.get(posId);
}
/**
- * Returns all of the ancestors of the node with {@code posId} matching the {@code predicate}.
+ * Returns all of the ancestors of the node {@code posId} and value matching the {@code
+ * predicate}.
*/
@NonNull
- List<T> findAncestorsFor(@NonNull String posId, @NonNull Predicate<? super T> predicate) {
+ public List<T> findAncestorsFor(
+ @NonNull String posId, @NonNull Predicate<? super T> predicate) {
List<T> result = new ArrayList<>();
+ for (String id : findAncestorsNodesFor(posId, predicate)) {
+ T value = mPosIdToTreeNode.get(id);
+ if (value != null) {
+ result.add(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns all of the ancestors' posIds of the node {@code posId} with value matching the {@code
+ * predicate}.
+ */
+ @NonNull
+ public List<String> findAncestorsNodesFor(
+ @NonNull String posId, @NonNull Predicate<? super T> predicate) {
+ List<String> result = new ArrayList<>();
while (true) {
String parentPosId = getParentNodePosId(posId);
if (parentPosId == null) {
@@ -124,7 +146,7 @@
}
T value = mPosIdToTreeNode.get(parentPosId);
if (value != null && predicate.test(value)) {
- result.add(value);
+ result.add(parentPosId);
}
posId = parentPosId;
}
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutThemeImpl.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutThemeImpl.java
index 819b270..b540781 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutThemeImpl.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutThemeImpl.java
@@ -106,15 +106,6 @@
return new ProtoLayoutThemeImpl(context.getResources(), R.style.ProtoLayoutBaseTheme);
}
- /**
- * Creates a ProtoLayoutTheme for the default theme, based on R.style.ProtoLayoutBaseTheme and
- * R.attr.protoLayoutFallbackAppearance from the local package.
- */
- @NonNull
- public static ProtoLayoutTheme defaultTheme(@NonNull Resources resources) {
- return new ProtoLayoutThemeImpl(resources, R.style.ProtoLayoutBaseTheme);
- }
-
private final Map<Integer, FontSet> mVariantToFontSet = new ArrayMap<>();
private final Theme mTheme;
@AttrRes private final int mFallbackTextAppearanceAttrId;
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/TouchDelegateComposite.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/TouchDelegateComposite.java
index b5d91a6..e6bf97c 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/TouchDelegateComposite.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/TouchDelegateComposite.java
@@ -50,14 +50,13 @@
*/
class TouchDelegateComposite extends TouchDelegate {
- @NonNull
- private final WeakHashMap<View, DelegateInfo> mDelegates = new WeakHashMap<>();
+ @NonNull private final WeakHashMap<View, DelegateInfo> mDelegates = new WeakHashMap<>();
/**
* Constructor
*
- * @param delegateView The view that should receive motion events.
- * @param actualBounds The hit rect of the view.
+ * @param delegateView The view that should receive motion events.
+ * @param actualBounds The hit rect of the view.
* @param extendedBounds The hit rect to be delegated.
*/
TouchDelegateComposite(
@@ -110,13 +109,11 @@
}
return checkNotNull(mDelegates.get(view)).mTouchDelegate.onTouchEvent(event);
} else {
- // For other motion event, forward to ALL the delegate view whose extended bounds
- // with touch
- // slop contains the touch point.
+ // For other motion event, forward to ALL the delegate view whose extended bounds with
+ // touch slop contains the touch point.
for (DelegateInfo delegateInfo : mDelegates.values()) {
- // set the event location back to the original coordinates, which might get
- // offset by the
- // previous TouchDelegate#onTouchEvent call
+ // set the event location back to the original coordinates, which might get offset
+ // by the previous TouchDelegate#onTouchEvent call.
event.setLocation(x, y);
eventForwarded |= delegateInfo.mTouchDelegate.onTouchEvent(event);
}
@@ -151,15 +148,13 @@
}
private static final class DelegateInfo {
- @NonNull
- final Rect mActualBounds;
- @NonNull
- final Rect mExtendedBounds;
- @NonNull
- final TouchDelegate mTouchDelegate;
+ @NonNull final Rect mActualBounds;
+ @NonNull final Rect mExtendedBounds;
+ @NonNull final TouchDelegate mTouchDelegate;
DelegateInfo(
- @NonNull View delegateView, @NonNull Rect actualBounds,
+ @NonNull View delegateView,
+ @NonNull Rect actualBounds,
@NonNull Rect extendedBounds) {
mActualBounds = actualBounds;
mExtendedBounds = extendedBounds;
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/PositionIdTreeTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/PositionIdTreeTest.java
index 38d616d..a52e426 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/PositionIdTreeTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/PositionIdTreeTest.java
@@ -161,7 +161,7 @@
}
@Test
- public void findAncestor_onlySearchesNodesAboveTheNode() {
+ public void findAncestorValues_onlySearchesNodesAboveTheNode() {
List<TreeNode> nodesOfInterest = Arrays.asList(mNodeRoot, mNode2, mNode2Child1, mNode1);
assertThat(mTree.findAncestorsFor(NODE_2_1, nodesOfInterest::contains))
@@ -169,7 +169,15 @@
}
@Test
- public void findAncestor_disjointTree_searchesAllAboveNodes() {
+ public void findAncestorIds_onlySearchesNodesAboveTheNode() {
+ List<TreeNode> nodesOfInterest = Arrays.asList(mNodeRoot, mNode2, mNode2Child1, mNode1);
+
+ assertThat(mTree.findAncestorsNodesFor(NODE_2_1, nodesOfInterest::contains))
+ .containsExactly(NODE_2, NODE_ROOT);
+ }
+
+ @Test
+ public void findAncestorValues_disjointTree_searchesAllAboveNodes() {
// Missing NODE_3_1
mTree.addOrReplace(NODE_3_1_1, mNode3Child1Child1);
mTree.addOrReplace(NODE_3_1_1_1, mNode3Child1Child1Child1);
@@ -181,7 +189,19 @@
}
@Test
- public void findAncestor_emptyTree_returnsNothing() {
+ public void findAncestorIds_disjointTree_searchesAllAboveNodes() {
+ // Missing NODE_3_1
+ mTree.addOrReplace(NODE_3_1_1, mNode3Child1Child1);
+ mTree.addOrReplace(NODE_3_1_1_1, mNode3Child1Child1Child1);
+ List<TreeNode> nodesOfInterest =
+ Arrays.asList(mNodeRoot, mNode1, mNode3Child1Child1, mNode3);
+
+ assertThat(mTree.findAncestorsNodesFor(NODE_3_1_1_1, nodesOfInterest::contains))
+ .containsExactly(ROOT_NODE_ID, NODE_3_1_1, NODE_3);
+ }
+
+ @Test
+ public void findAncestorValues_emptyTree_returnsNothing() {
mTree.clear();
List<TreeNode> nodesOfInterest = Arrays.asList(mNodeRoot, mNode2, mNode2Child1, mNode1);
@@ -189,11 +209,24 @@
}
@Test
- public void findAncestor_noMatch_returnsNothing() {
+ public void findAncestorIds_emptyTree_returnsNothing() {
+ mTree.clear();
+ List<TreeNode> nodesOfInterest = Arrays.asList(mNodeRoot, mNode2, mNode2Child1, mNode1);
+
+ assertThat(mTree.findAncestorsNodesFor(NODE_2_1, nodesOfInterest::contains)).isEmpty();
+ }
+
+ @Test
+ public void findAncestorValues_noMatch_returnsNothing() {
assertThat(mTree.findAncestorsFor(NODE_2_1, treeNode -> false)).isEmpty();
}
@Test
+ public void findAncestorIds_noMatch_returnsNothing() {
+ assertThat(mTree.findAncestorsNodesFor(NODE_2_1, treeNode -> false)).isEmpty();
+ }
+
+ @Test
public void findChildren_onlySearchesBelowTheNode() {
List<TreeNode> nodesOfInterest = Arrays.asList(mNodeRoot, mNode2, mNode2Child1, mNode1);
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestFingerprinter.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestFingerprinter.java
index de6cd5a..0f8d2d3 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestFingerprinter.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestFingerprinter.java
@@ -16,6 +16,8 @@
package androidx.wear.protolayout.renderer.helper;
+import static androidx.wear.protolayout.proto.LayoutElementProto.ArcLayoutElement.InnerCase.ADAPTER;
+
import androidx.annotation.Nullable;
import androidx.wear.protolayout.proto.FingerprintProto.NodeFingerprint;
import androidx.wear.protolayout.proto.FingerprintProto.TreeFingerprint;
@@ -96,6 +98,7 @@
for (LayoutElementProto.ArcLayoutElement child : getArcChildren(element)) {
addNodeToParent(child, currentFingerprintBuilder);
}
+
NodeFingerprint currentFingerprint = currentFingerprintBuilder.build();
if (parentFingerprintBuilder != null) {
addNodeToParent(currentFingerprint, parentFingerprintBuilder);
@@ -106,12 +109,14 @@
private void addNodeToParent(
LayoutElementProto.ArcLayoutElement element,
NodeFingerprint.Builder parentFingerprintBuilder) {
- addNodeToParent(
+ NodeFingerprint.Builder currentFingerprint =
NodeFingerprint.newBuilder()
.setSelfTypeValue(getSelfTypeFingerprint(element))
- .setSelfPropsValue(getSelfPropsFingerprint(element))
- .build(),
- parentFingerprintBuilder);
+ .setSelfPropsValue(getSelfPropsFingerprint(element));
+ if (element.getInnerCase() == ADAPTER) {
+ addNodeToParent(element.getAdapter().getContent(), currentFingerprint);
+ }
+ addNodeToParent(currentFingerprint.build(), parentFingerprintBuilder);
}
private void addNodeToParent(
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestFingerprinterTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestFingerprinterTest.java
index 15f0481..b0d7c9c 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestFingerprinterTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestFingerprinterTest.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package androidx.wear.protolayout.renderer.helper;
import static androidx.wear.protolayout.renderer.helper.TestDsl.arc;
+import static androidx.wear.protolayout.renderer.helper.TestDsl.arcAdapter;
import static androidx.wear.protolayout.renderer.helper.TestDsl.arcText;
import static androidx.wear.protolayout.renderer.helper.TestDsl.column;
import static androidx.wear.protolayout.renderer.helper.TestDsl.layout;
@@ -43,57 +43,62 @@
TestFingerprinter.getDefault()
.buildLayoutWithFingerprints(referenceLayout().getRoot());
NodeFingerprint root = layout.getFingerprint().getRoot();
-
Set<Integer> selfPropsFingerprints = new HashSet<>();
Set<Integer> childFingerprints = new HashSet<>();
-
// 1
NodeFingerprint node = root;
assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
assertThat(node.getChildNodesCount()).isEqualTo(4);
assertThat(childFingerprints.add(node.getChildNodesValue())).isTrue();
-
// 1.1
node = root.getChildNodes(0);
assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
assertThat(node.getChildNodesCount()).isEqualTo(2);
assertThat(childFingerprints.add(node.getChildNodesValue())).isTrue();
-
// 1.1.1
node = root.getChildNodes(0).getChildNodes(0);
assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
assertThat(node.getChildNodesList()).isEmpty();
assertThat(node.getChildNodesValue()).isEqualTo(0);
-
// 1.1.2
node = root.getChildNodes(0).getChildNodes(1);
assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
assertThat(node.getChildNodesList()).isEmpty();
assertThat(node.getChildNodesValue()).isEqualTo(0);
-
// 1.2
node = root.getChildNodes(1);
assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
assertThat(node.getChildNodesCount()).isEqualTo(1);
assertThat(childFingerprints.add(node.getChildNodesValue())).isTrue();
-
// 1.2.1
node = root.getChildNodes(1).getChildNodes(0);
assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
assertThat(node.getChildNodesList()).isEmpty();
assertThat(node.getChildNodesValue()).isEqualTo(0);
-
// 1.3
node = root.getChildNodes(2);
assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
assertThat(node.getChildNodesList()).isEmpty();
assertThat(node.getChildNodesValue()).isEqualTo(0);
-
// 1.4
node = root.getChildNodes(3);
assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
+ assertThat(node.getChildNodesCount()).isEqualTo(2);
+ assertThat(childFingerprints.add(node.getChildNodesValue())).isTrue();
+ // 1.4.1
+ node = root.getChildNodes(3).getChildNodes(0);
+ assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
+ assertThat(node.getChildNodesCount()).isEqualTo(0);
+ assertThat(childFingerprints.add(node.getChildNodesValue())).isTrue();
+ // 1.4.2
+ node = root.getChildNodes(3).getChildNodes(1);
+ assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
assertThat(node.getChildNodesCount()).isEqualTo(1);
assertThat(childFingerprints.add(node.getChildNodesValue())).isTrue();
+ // 1.4.2.1
+ node = root.getChildNodes(3).getChildNodes(1).getChildNodes(0);
+ assertThat(selfPropsFingerprints.add(node.getSelfPropsValue())).isTrue();
+ assertThat(node.getChildNodesCount()).isEqualTo(0);
}
@Test
@@ -102,12 +107,10 @@
TestFingerprinter.getDefault()
.buildLayoutWithFingerprints(referenceLayout().getRoot());
NodeFingerprint refRoot = refLayout.getFingerprint().getRoot();
-
Layout layout =
TestFingerprinter.getDefault()
.buildLayoutWithFingerprints(layoutWithDifferentColumnHeight().getRoot());
NodeFingerprint root = layout.getFingerprint().getRoot();
-
// 1
NodeFingerprint refNode = refRoot;
NodeFingerprint node = root;
@@ -116,7 +119,6 @@
.isNotEqualTo(refNode.getSelfPropsValue()); // Only difference
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.1
refNode = refRoot.getChildNodes(0);
node = root.getChildNodes(0);
@@ -124,7 +126,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.1.1
refNode = refRoot.getChildNodes(0).getChildNodes(0);
node = root.getChildNodes(0).getChildNodes(0);
@@ -132,7 +133,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.1.2
refNode = refRoot.getChildNodes(0).getChildNodes(1);
node = root.getChildNodes(0).getChildNodes(1);
@@ -140,7 +140,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.2
refNode = refRoot.getChildNodes(1);
node = root.getChildNodes(1);
@@ -148,7 +147,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.2.1
refNode = refRoot.getChildNodes(1).getChildNodes(0);
node = root.getChildNodes(1).getChildNodes(0);
@@ -156,7 +154,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.3
refNode = refRoot.getChildNodes(2);
node = root.getChildNodes(2);
@@ -164,7 +161,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.4
refNode = refRoot.getChildNodes(3);
node = root.getChildNodes(3);
@@ -180,12 +176,10 @@
TestFingerprinter.getDefault()
.buildLayoutWithFingerprints(referenceLayout().getRoot());
NodeFingerprint refRoot = refLayout.getFingerprint().getRoot();
-
Layout layout =
TestFingerprinter.getDefault()
.buildLayoutWithFingerprints(layoutWithDifferentText().getRoot());
NodeFingerprint root = layout.getFingerprint().getRoot();
-
// 1
NodeFingerprint refNode = refRoot;
NodeFingerprint node = root;
@@ -193,7 +187,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isNotEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.1
refNode = refRoot.getChildNodes(0);
node = root.getChildNodes(0);
@@ -201,7 +194,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.1.1
refNode = refRoot.getChildNodes(0).getChildNodes(0);
node = root.getChildNodes(0).getChildNodes(0);
@@ -209,7 +201,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.1.2
refNode = refRoot.getChildNodes(0).getChildNodes(1);
node = root.getChildNodes(0).getChildNodes(1);
@@ -217,7 +208,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.2
refNode = refRoot.getChildNodes(1);
node = root.getChildNodes(1);
@@ -225,7 +215,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isNotEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.2.1
refNode = refRoot.getChildNodes(1).getChildNodes(0);
node = root.getChildNodes(1).getChildNodes(0);
@@ -234,7 +223,6 @@
.isNotEqualTo(refNode.getSelfPropsValue()); // Updated text
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.3
refNode = refRoot.getChildNodes(2);
node = root.getChildNodes(2);
@@ -242,7 +230,6 @@
assertThat(node.getSelfPropsValue()).isEqualTo(refNode.getSelfPropsValue());
assertThat(node.getChildNodesValue()).isEqualTo(refNode.getChildNodesValue());
assertThat(node.getChildNodesCount()).isEqualTo(refNode.getChildNodesCount());
-
// 1.4
refNode = refRoot.getChildNodes(3);
node = root.getChildNodes(3);
@@ -273,8 +260,10 @@
text("blah blah"), // 1.3
arc( // 1.4
props -> props.anchorAngleDegrees = 15, // arc props
- arcText("arctext") // 1.4.1
- )));
+ arcText("arctext"), // 1.4.1
+ arcAdapter( // 1.4.2
+ text("text") // 1.4.2.1
+ ))));
}
private static Layout layoutWithDifferentColumnHeight() {
@@ -298,8 +287,10 @@
text("blah blah"), // 1.3
arc( // 1.4
props -> props.anchorAngleDegrees = 15, // arc props
- arcText("arctext") // 1.4.1
- )));
+ arcText("arctext"), // 1.4.1
+ arcAdapter( // 1.4.2
+ text("text") // 1.4.2.1
+ ))));
}
private static Layout layoutWithDifferentText() {
@@ -323,7 +314,9 @@
text("blah blah"), // 1.3
arc( // 1.4
props -> props.anchorAngleDegrees = 15, // arc props
- arcText("arctext") // 1.4.1
- )));
+ arcText("arctext"), // 1.4.1
+ arcAdapter( // 1.4.2
+ text("text") // 1.4.2.1
+ ))));
}
}
diff --git a/wear/tiles/tiles-renderer/lint-baseline.xml b/wear/tiles/tiles-renderer/lint-baseline.xml
index 0177db4..2d98f2c 100644
--- a/wear/tiles/tiles-renderer/lint-baseline.xml
+++ b/wear/tiles/tiles-renderer/lint-baseline.xml
@@ -85,7 +85,7 @@
<issue
id="RestrictedApiAndroidX"
message="Resources can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" @Nullable ResourceProto.Resources resources) {"
+ errorLine1=" @Nullable ResourceProto.Resources resources,"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
@@ -174,96 +174,6 @@
<issue
id="RestrictedApiAndroidX"
- message="Layout can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" @Nullable private final LayoutElementProto.Layout mLayout;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="Resources can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" @Nullable private final ResourceProto.Resources mResources;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="Layout can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" @Nullable LayoutElementProto.Layout layout,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="Resources can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" @Nullable ResourceProto.Resources resources) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="Layout can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" public LayoutElementProto.Layout getLayout() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="Resources can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" public ResourceProto.Resources getResources() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="Layout can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" @Nullable private LayoutElementProto.Layout mLayout;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="Resources can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" @Nullable private ResourceProto.Resources mResources;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="Layout can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" public Builder setLayout(@NonNull LayoutElementProto.Layout layout) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="Resources can only be accessed from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
- errorLine1=" public Builder setResources(@NonNull ResourceProto.Resources resources) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/wear/tiles/renderer/TileRenderer.java"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
message="Timeline.toProto can only be called from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
errorLine1=" mCache = new TilesTimelineCacheInternal(timeline.toProto());"
errorLine2=" ~~~~~~~">
diff --git a/wear/tiles/tiles/api/current.txt b/wear/tiles/tiles/api/current.txt
index cbec097..7cd96a0 100644
--- a/wear/tiles/tiles/api/current.txt
+++ b/wear/tiles/tiles/api/current.txt
@@ -255,22 +255,22 @@
public final class EventBuilders {
}
- public static final class EventBuilders.TileAddEvent {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class EventBuilders.TileAddEvent {
method public int getTileId();
}
public static final class EventBuilders.TileAddEvent.Builder {
- ctor public EventBuilders.TileAddEvent.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public EventBuilders.TileAddEvent.Builder();
method public androidx.wear.tiles.EventBuilders.TileAddEvent build();
method public androidx.wear.tiles.EventBuilders.TileAddEvent.Builder setTileId(int);
}
- public static final class EventBuilders.TileEnterEvent {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class EventBuilders.TileEnterEvent {
method public int getTileId();
}
public static final class EventBuilders.TileEnterEvent.Builder {
- ctor public EventBuilders.TileEnterEvent.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public EventBuilders.TileEnterEvent.Builder();
method public androidx.wear.tiles.EventBuilders.TileEnterEvent build();
method public androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder setTileId(int);
}
@@ -290,22 +290,22 @@
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public androidx.wear.tiles.EventBuilders.TileInteractionEvent.Builder setTimestamp(java.time.Instant);
}
- public static final class EventBuilders.TileLeaveEvent {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class EventBuilders.TileLeaveEvent {
method public int getTileId();
}
public static final class EventBuilders.TileLeaveEvent.Builder {
- ctor public EventBuilders.TileLeaveEvent.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public EventBuilders.TileLeaveEvent.Builder();
method public androidx.wear.tiles.EventBuilders.TileLeaveEvent build();
method public androidx.wear.tiles.EventBuilders.TileLeaveEvent.Builder setTileId(int);
}
- public static final class EventBuilders.TileRemoveEvent {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class EventBuilders.TileRemoveEvent {
method public int getTileId();
}
public static final class EventBuilders.TileRemoveEvent.Builder {
- ctor public EventBuilders.TileRemoveEvent.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public EventBuilders.TileRemoveEvent.Builder();
method public androidx.wear.tiles.EventBuilders.TileRemoveEvent build();
method public androidx.wear.tiles.EventBuilders.TileRemoveEvent.Builder setTileId(int);
}
@@ -914,7 +914,7 @@
public final class RequestBuilders {
}
- public static final class RequestBuilders.ResourcesRequest {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class RequestBuilders.ResourcesRequest {
method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters getDeviceConfiguration();
method @Deprecated public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
method public java.util.List<java.lang.String!> getResourceIds();
@@ -923,16 +923,16 @@
}
public static final class RequestBuilders.ResourcesRequest.Builder {
- ctor public RequestBuilders.ResourcesRequest.Builder();
- method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public RequestBuilders.ResourcesRequest.Builder();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
method public androidx.wear.tiles.RequestBuilders.ResourcesRequest build();
- method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceConfiguration(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceConfiguration(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
- method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setTileId(int);
- method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setTileId(int);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
}
- public static final class RequestBuilders.TileRequest {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class RequestBuilders.TileRequest {
method public androidx.wear.protolayout.StateBuilders.State getCurrentState();
method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters getDeviceConfiguration();
method @Deprecated public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
@@ -941,13 +941,13 @@
}
public static final class RequestBuilders.TileRequest.Builder {
- ctor public RequestBuilders.TileRequest.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public RequestBuilders.TileRequest.Builder();
method public androidx.wear.tiles.RequestBuilders.TileRequest build();
- method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setCurrentState(androidx.wear.protolayout.StateBuilders.State);
- method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceConfiguration(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setCurrentState(androidx.wear.protolayout.StateBuilders.State);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceConfiguration(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State);
- method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setTileId(int);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setTileId(int);
}
@Deprecated public final class ResourceBuilders {
@@ -1022,7 +1022,7 @@
public final class TileBuilders {
}
- public static final class TileBuilders.Tile {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class TileBuilders.Tile {
method public long getFreshnessIntervalMillis();
method public String getResourcesVersion();
method public androidx.wear.protolayout.StateBuilders.State? getState();
@@ -1031,12 +1031,12 @@
}
public static final class TileBuilders.Tile.Builder {
- ctor public TileBuilders.Tile.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public TileBuilders.Tile.Builder();
method public androidx.wear.tiles.TileBuilders.Tile build();
- method public androidx.wear.tiles.TileBuilders.Tile.Builder setFreshnessIntervalMillis(long);
- method public androidx.wear.tiles.TileBuilders.Tile.Builder setResourcesVersion(String);
- method public androidx.wear.tiles.TileBuilders.Tile.Builder setState(androidx.wear.protolayout.StateBuilders.State);
- method public androidx.wear.tiles.TileBuilders.Tile.Builder setTileTimeline(androidx.wear.protolayout.TimelineBuilders.Timeline);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.TileBuilders.Tile.Builder setFreshnessIntervalMillis(long);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.TileBuilders.Tile.Builder setResourcesVersion(String);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.tiles.TileBuilders.Tile.Builder setState(androidx.wear.protolayout.StateBuilders.State);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.TileBuilders.Tile.Builder setTileTimeline(androidx.wear.protolayout.TimelineBuilders.Timeline);
method @Deprecated public androidx.wear.tiles.TileBuilders.Tile.Builder setTimeline(androidx.wear.tiles.TimelineBuilders.Timeline);
}
diff --git a/wear/tiles/tiles/api/restricted_current.txt b/wear/tiles/tiles/api/restricted_current.txt
index cbec097..7cd96a0 100644
--- a/wear/tiles/tiles/api/restricted_current.txt
+++ b/wear/tiles/tiles/api/restricted_current.txt
@@ -255,22 +255,22 @@
public final class EventBuilders {
}
- public static final class EventBuilders.TileAddEvent {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class EventBuilders.TileAddEvent {
method public int getTileId();
}
public static final class EventBuilders.TileAddEvent.Builder {
- ctor public EventBuilders.TileAddEvent.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public EventBuilders.TileAddEvent.Builder();
method public androidx.wear.tiles.EventBuilders.TileAddEvent build();
method public androidx.wear.tiles.EventBuilders.TileAddEvent.Builder setTileId(int);
}
- public static final class EventBuilders.TileEnterEvent {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class EventBuilders.TileEnterEvent {
method public int getTileId();
}
public static final class EventBuilders.TileEnterEvent.Builder {
- ctor public EventBuilders.TileEnterEvent.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public EventBuilders.TileEnterEvent.Builder();
method public androidx.wear.tiles.EventBuilders.TileEnterEvent build();
method public androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder setTileId(int);
}
@@ -290,22 +290,22 @@
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public androidx.wear.tiles.EventBuilders.TileInteractionEvent.Builder setTimestamp(java.time.Instant);
}
- public static final class EventBuilders.TileLeaveEvent {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class EventBuilders.TileLeaveEvent {
method public int getTileId();
}
public static final class EventBuilders.TileLeaveEvent.Builder {
- ctor public EventBuilders.TileLeaveEvent.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public EventBuilders.TileLeaveEvent.Builder();
method public androidx.wear.tiles.EventBuilders.TileLeaveEvent build();
method public androidx.wear.tiles.EventBuilders.TileLeaveEvent.Builder setTileId(int);
}
- public static final class EventBuilders.TileRemoveEvent {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class EventBuilders.TileRemoveEvent {
method public int getTileId();
}
public static final class EventBuilders.TileRemoveEvent.Builder {
- ctor public EventBuilders.TileRemoveEvent.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public EventBuilders.TileRemoveEvent.Builder();
method public androidx.wear.tiles.EventBuilders.TileRemoveEvent build();
method public androidx.wear.tiles.EventBuilders.TileRemoveEvent.Builder setTileId(int);
}
@@ -914,7 +914,7 @@
public final class RequestBuilders {
}
- public static final class RequestBuilders.ResourcesRequest {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class RequestBuilders.ResourcesRequest {
method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters getDeviceConfiguration();
method @Deprecated public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
method public java.util.List<java.lang.String!> getResourceIds();
@@ -923,16 +923,16 @@
}
public static final class RequestBuilders.ResourcesRequest.Builder {
- ctor public RequestBuilders.ResourcesRequest.Builder();
- method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public RequestBuilders.ResourcesRequest.Builder();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder addResourceId(String);
method public androidx.wear.tiles.RequestBuilders.ResourcesRequest build();
- method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceConfiguration(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceConfiguration(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
- method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setTileId(int);
- method public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setTileId(int);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.ResourcesRequest.Builder setVersion(String);
}
- public static final class RequestBuilders.TileRequest {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class RequestBuilders.TileRequest {
method public androidx.wear.protolayout.StateBuilders.State getCurrentState();
method public androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters getDeviceConfiguration();
method @Deprecated public androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters? getDeviceParameters();
@@ -941,13 +941,13 @@
}
public static final class RequestBuilders.TileRequest.Builder {
- ctor public RequestBuilders.TileRequest.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public RequestBuilders.TileRequest.Builder();
method public androidx.wear.tiles.RequestBuilders.TileRequest build();
- method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setCurrentState(androidx.wear.protolayout.StateBuilders.State);
- method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceConfiguration(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setCurrentState(androidx.wear.protolayout.StateBuilders.State);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceConfiguration(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setDeviceParameters(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
method @Deprecated public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setState(androidx.wear.tiles.StateBuilders.State);
- method public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setTileId(int);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.RequestBuilders.TileRequest.Builder setTileId(int);
}
@Deprecated public final class ResourceBuilders {
@@ -1022,7 +1022,7 @@
public final class TileBuilders {
}
- public static final class TileBuilders.Tile {
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public static final class TileBuilders.Tile {
method public long getFreshnessIntervalMillis();
method public String getResourcesVersion();
method public androidx.wear.protolayout.StateBuilders.State? getState();
@@ -1031,12 +1031,12 @@
}
public static final class TileBuilders.Tile.Builder {
- ctor public TileBuilders.Tile.Builder();
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public TileBuilders.Tile.Builder();
method public androidx.wear.tiles.TileBuilders.Tile build();
- method public androidx.wear.tiles.TileBuilders.Tile.Builder setFreshnessIntervalMillis(long);
- method public androidx.wear.tiles.TileBuilders.Tile.Builder setResourcesVersion(String);
- method public androidx.wear.tiles.TileBuilders.Tile.Builder setState(androidx.wear.protolayout.StateBuilders.State);
- method public androidx.wear.tiles.TileBuilders.Tile.Builder setTileTimeline(androidx.wear.protolayout.TimelineBuilders.Timeline);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.TileBuilders.Tile.Builder setFreshnessIntervalMillis(long);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.TileBuilders.Tile.Builder setResourcesVersion(String);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public androidx.wear.tiles.TileBuilders.Tile.Builder setState(androidx.wear.protolayout.StateBuilders.State);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=0) public androidx.wear.tiles.TileBuilders.Tile.Builder setTileTimeline(androidx.wear.protolayout.TimelineBuilders.Timeline);
method @Deprecated public androidx.wear.tiles.TileBuilders.Tile.Builder setTimeline(androidx.wear.tiles.TimelineBuilders.Timeline);
}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java
index 0dd9ce1..37ad14f 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/ActionBuilders.java
@@ -1,19 +1,18 @@
/*
-* 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.
-*/
+ * 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.wear.tiles;
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
index b870020..7e72ffb 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
@@ -33,6 +33,7 @@
private EventBuilders() {}
/** Event fired when a tile has been added to the carousel. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public static final class TileAddEvent {
private final EventProto.TileAddEvent mImpl;
@@ -74,6 +75,8 @@
private final EventProto.TileAddEvent.Builder mImpl =
EventProto.TileAddEvent.newBuilder();
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public Builder() {}
/**
@@ -96,6 +99,7 @@
}
/** Event fired when a tile has been removed from the carousel. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public static final class TileRemoveEvent {
private final EventProto.TileRemoveEvent mImpl;
@@ -137,6 +141,8 @@
private final EventProto.TileRemoveEvent.Builder mImpl =
EventProto.TileRemoveEvent.newBuilder();
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public Builder() {}
/**
@@ -158,6 +164,7 @@
}
/** Event fired when a tile is swiped to by the user (i.e. it's visible on screen). */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public static final class TileEnterEvent {
private final EventProto.TileEnterEvent mImpl;
@@ -199,6 +206,8 @@
private final EventProto.TileEnterEvent.Builder mImpl =
EventProto.TileEnterEvent.newBuilder();
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public Builder() {}
/**
@@ -224,6 +233,7 @@
* Event fired when a tile is swiped away from by the user (i.e. it's no longer visible on
* screen).
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
public static final class TileLeaveEvent {
private final EventProto.TileLeaveEvent mImpl;
@@ -265,6 +275,8 @@
private final EventProto.TileLeaveEvent.Builder mImpl =
EventProto.TileLeaveEvent.newBuilder();
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public Builder() {}
/**
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java
index a0b6980..b12b8ff2 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/RequestBuilders.java
@@ -22,6 +22,7 @@
import androidx.annotation.RestrictTo.Scope;
import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
import androidx.wear.protolayout.StateBuilders.State;
+import androidx.wear.protolayout.expression.RequiresSchemaVersion;
import androidx.wear.protolayout.proto.DeviceParametersProto;
import androidx.wear.protolayout.proto.StateProto;
import androidx.wear.tiles.proto.RequestProto;
@@ -36,6 +37,7 @@
* Parameters passed to a {@link androidx.wear.tiles.TileBuilders.Tile} Service when the
* renderer is requesting a new version of the tile.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
public static final class TileRequest {
private final RequestProto.TileRequest mImpl;
@@ -147,6 +149,8 @@
private final RequestProto.TileRequest.Builder mImpl =
RequestProto.TileRequest.newBuilder();
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public Builder() {}
/**
@@ -154,6 +158,7 @@
* object describing the device requesting the tile update. If not set, a default empty
* instance is used.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder setDeviceConfiguration(@NonNull DeviceParameters deviceConfiguration) {
mImpl.setDeviceConfiguration(deviceConfiguration.toProto());
@@ -164,15 +169,15 @@
* Sets the {@link androidx.wear.protolayout.StateBuilders.State} that should be used
* when building the tile.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder setCurrentState(@NonNull State currentState) {
mImpl.setCurrentState(currentState.toProto());
return this;
}
- /**
- * Sets the ID of the tile being requested.
- */
+ /** Sets the ID of the tile being requested. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder setTileId(int tileId) {
mImpl.setTileId(tileId);
@@ -220,6 +225,7 @@
* Parameters passed to a {@link androidx.wear.tiles.TileBuilders.Tile} Service when the
* renderer is requesting a specific resource version.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
public static final class ResourcesRequest {
private final RequestProto.ResourcesRequest mImpl;
@@ -325,13 +331,15 @@
private final RequestProto.ResourcesRequest.Builder mImpl =
RequestProto.ResourcesRequest.newBuilder();
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public Builder() {}
/**
* Sets the version of the resources being fetched. This is the same as the requested
* resource version, passed in {@link androidx.wear.tiles.TileBuilders.Tile}.
- *
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder setVersion(@NonNull String version) {
mImpl.setVersion(version);
@@ -348,6 +356,7 @@
* used in {@link androidx.wear.protolayout.ResourceBuilders.Resources}.idToImage), not
* Android resource names or similar.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder addResourceId(@NonNull String resourceId) {
mImpl.addResourceIds(resourceId);
@@ -358,15 +367,15 @@
* Sets the {@link androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters}
* object describing the device requesting the resources.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder setDeviceConfiguration(@NonNull DeviceParameters deviceConfiguration) {
mImpl.setDeviceConfiguration(deviceConfiguration.toProto());
return this;
}
- /**
- * Sets the ID of the tile for which resources are being requested.
- */
+ /** Sets the ID of the tile for which resources are being requested. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder setTileId(int tileId) {
mImpl.setTileId(tileId);
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
index f81f833..7d9a635 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
@@ -22,6 +22,7 @@
import androidx.annotation.RestrictTo.Scope;
import androidx.wear.protolayout.StateBuilders.State;
import androidx.wear.protolayout.TimelineBuilders.Timeline;
+import androidx.wear.protolayout.expression.RequiresSchemaVersion;
import androidx.wear.protolayout.expression.proto.VersionProto.VersionInfo;
import androidx.wear.tiles.proto.TileProto;
@@ -33,6 +34,7 @@
* A holder for a tile. This specifies the resources to use for this delivery of the tile, and
* the timeline for the tile.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
public static final class Tile {
private final TileProto.Tile mImpl;
@@ -81,9 +83,7 @@
return mImpl.getFreshnessIntervalMillis();
}
- /**
- * Gets {@link androidx.wear.protolayout.StateBuilders.State} for this tile.
- */
+ /** Gets {@link androidx.wear.protolayout.StateBuilders.State} for this tile. */
@Nullable
public State getState() {
if (mImpl.hasState()) {
@@ -144,6 +144,8 @@
public static final class Builder {
private final TileProto.Tile.Builder mImpl = TileProto.Tile.newBuilder();
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 0)
public Builder() {}
/**
@@ -152,6 +154,7 @@
* androidx.wear.tiles.RequestBuilders.ResourcesRequest} if the system does not have a
* copy of the specified resource version.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder setResourcesVersion(@NonNull String resourcesVersion) {
mImpl.setResourcesVersion(resourcesVersion);
@@ -162,6 +165,7 @@
* Sets the {@link androidx.wear.protolayout.TimelineBuilders.Timeline} containing the
* layouts for the tiles to show in the carousel, along with their validity periods.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder setTileTimeline(@NonNull Timeline tileTimeline) {
mImpl.setTileTimeline(tileTimeline.toProto());
@@ -181,15 +185,15 @@
* tile if it is on-screen, or about to be on-screen, although this is not guaranteed
* due to system-level optimizations.
*/
+ @RequiresSchemaVersion(major = 1, minor = 0)
@NonNull
public Builder setFreshnessIntervalMillis(long freshnessIntervalMillis) {
mImpl.setFreshnessIntervalMillis(freshnessIntervalMillis);
return this;
}
- /**
- * Sets {@link androidx.wear.protolayout.StateBuilders.State} for this tile.
- */
+ /** Sets {@link androidx.wear.protolayout.StateBuilders.State} for this tile. */
+ @RequiresSchemaVersion(major = 1, minor = 200)
@NonNull
public Builder setState(@NonNull State state) {
mImpl.setState(state.toProto());
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileLeaveEventData.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileLeaveEventData.java
index 97005c8..476cd9a 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileLeaveEventData.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileLeaveEventData.java
@@ -20,7 +20,7 @@
import androidx.annotation.RestrictTo;
/**
- * Holder for Tiles' TileLeaveEvent class, to be parceled and transferred to a tile service.
+ * Holder for Tiles' TileLeaveEvent class, to be parceled and transferred to a Tile Service.
*
* <p>All this does is to serialize TileLeaveEvent as a protobuf and transmit it.
*/
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileUpdateRequestData.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileUpdateRequestData.java
index 3198815..1907b70 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileUpdateRequestData.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileUpdateRequestData.java
@@ -26,6 +26,7 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public final class TileUpdateRequestData extends ProtoParcelable {
public static final int VERSION_PLACEHOLDER = 1;
+
public static final Creator<TileUpdateRequestData> CREATOR =
newCreator(TileUpdateRequestData.class, TileUpdateRequestData::new);
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java
index 500a3ed..5c0133e 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/CompositeTileUpdateRequesterTest.java
@@ -24,19 +24,18 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.concurrent.futures.ResolvableFuture;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.wear.protolayout.ResourceBuilders;
+import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListenableFuture;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
+@RunWith(AndroidJUnit4.class)
@DoNotInstrument
public class CompositeTileUpdateRequesterTest {
private FakeUpdateRequester mFakeUpdateRequester1;
@@ -51,7 +50,7 @@
mCompositeTileUpdateRequesterUnderTest =
new CompositeTileUpdateRequester(
- List.of(mFakeUpdateRequester1, mFakeUpdateRequester2));
+ ImmutableList.of(mFakeUpdateRequester1, mFakeUpdateRequester2));
}
@Test
@@ -62,7 +61,7 @@
assertThat(mFakeUpdateRequester2.mCalledService).isEqualTo(FakeService.class);
}
- private class FakeUpdateRequester implements TileUpdateRequester {
+ private static class FakeUpdateRequester implements TileUpdateRequester {
@Nullable Class<? extends TileService> mCalledService = null;
@Override
@@ -71,7 +70,7 @@
}
}
- private class FakeService extends TileService {
+ private static class FakeService extends TileService {
@NonNull
@Override
protected ListenableFuture<TileBuilders.Tile> onTileRequest(
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ProtoParcelableTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ProtoParcelableTest.java
index 17a2330..d0032c7 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ProtoParcelableTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ProtoParcelableTest.java
@@ -20,15 +20,16 @@
import android.os.Parcel;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.wear.tiles.proto.RequestProto;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
-@RunWith(RobolectricTestRunner.class)
-@DoNotInstrument
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument // See http://g/robolectric-users/fTi2FRXgyGA/m/PkB0wYuwBgAJ
public final class ProtoParcelableTest {
public static class Wrapper extends ProtoParcelable {
public static final int VERSION = 1;
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ResourcesDataTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ResourcesDataTest.java
index cdbb5a9..7dfa440 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ResourcesDataTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ResourcesDataTest.java
@@ -20,14 +20,14 @@
import android.os.Parcel;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.wear.protolayout.proto.ResourceProto.Resources;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
-@RunWith(RobolectricTestRunner.class)
+@RunWith(AndroidJUnit4.class)
@DoNotInstrument
public final class ResourcesDataTest {
@Test
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ResourcesRequestDataTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ResourcesRequestDataTest.java
index 61dd9f1..3837ce0 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ResourcesRequestDataTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/ResourcesRequestDataTest.java
@@ -20,14 +20,14 @@
import android.os.Parcel;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.wear.tiles.proto.RequestProto;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
-@RunWith(RobolectricTestRunner.class)
+@RunWith(AndroidJUnit4.class)
@DoNotInstrument
public final class ResourcesRequestDataTest {
@Test
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileDataTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileDataTest.java
index f1938b6..100c0d3 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileDataTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileDataTest.java
@@ -20,14 +20,14 @@
import android.os.Parcel;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.wear.tiles.proto.TileProto.Tile;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
-@RunWith(RobolectricTestRunner.class)
+@RunWith(AndroidJUnit4.class)
@DoNotInstrument
public final class TileDataTest {
@Test
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileRequestDataTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileRequestDataTest.java
index bf8ad22..6128a33 100644
--- a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileRequestDataTest.java
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/TileRequestDataTest.java
@@ -20,14 +20,14 @@
import android.os.Parcel;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.wear.tiles.proto.RequestProto;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
-@RunWith(RobolectricTestRunner.class)
+@RunWith(AndroidJUnit4.class)
@DoNotInstrument
public final class TileRequestDataTest {
@Test
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
index 704f5fd..821d610 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
@@ -526,6 +526,7 @@
testContext: Context,
private var surfaceHolderOverride: SurfaceHolder
) : WatchFaceService() {
+ var lastComplicationType: ComplicationType? = null
init {
attachBaseContext(testContext)
@@ -605,7 +606,10 @@
CanvasType.HARDWARE,
16
) {
- override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
+ override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
+ lastComplicationType =
+ complicationSlotsManager[123]!!.complicationData.value.type
+ }
override fun renderHighlightLayer(
canvas: Canvas,
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
index 2afa5d96..b77902c 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
@@ -1344,6 +1344,73 @@
assertTrue(ObservableServiceC.awaitForServiceToBeBound(UPDATE_TIMEOUT_MILLIS))
}
+
+ @Test
+ @RequiresApi(Build.VERSION_CODES.O_MR1)
+ fun overrideComplicationData() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
+ return
+ }
+ val wallpaperService =
+ TestComplicationProviderDefaultsWatchFaceService(context, surfaceHolder)
+ val interactiveInstance = getOrCreateTestSubject(wallpaperService)
+ interactiveInstance.updateComplicationData(
+ mapOf(123 to rangedValueComplicationBuilder().build())
+ )
+
+ interactiveInstance.overrideComplicationData(
+ mapOf(
+ 123 to
+ ShortTextComplicationData.Builder(
+ PlainComplicationText.Builder("TEST").build(),
+ ComplicationText.EMPTY
+ )
+ .build()
+ )
+ )
+
+ interactiveInstance.renderWatchFaceToBitmap(
+ RenderParameters(DrawMode.INTERACTIVE, WatchFaceLayer.ALL_WATCH_FACE_LAYERS, null),
+ Instant.ofEpochMilli(1234567),
+ null,
+ null
+ )
+ assertThat(wallpaperService.lastComplicationType).isEqualTo(ComplicationType.SHORT_TEXT)
+ }
+
+ @Test
+ @RequiresApi(Build.VERSION_CODES.O_MR1)
+ fun clearComplicationDataOverride() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
+ return
+ }
+ val wallpaperService =
+ TestComplicationProviderDefaultsWatchFaceService(context, surfaceHolder)
+ val interactiveInstance = getOrCreateTestSubject(wallpaperService)
+ interactiveInstance.updateComplicationData(
+ mapOf(123 to rangedValueComplicationBuilder().build())
+ )
+ interactiveInstance.overrideComplicationData(
+ mapOf(
+ 123 to
+ ShortTextComplicationData.Builder(
+ PlainComplicationText.Builder("TEST").build(),
+ ComplicationText.EMPTY
+ )
+ .build(),
+ )
+ )
+
+ interactiveInstance.clearComplicationDataOverride()
+
+ interactiveInstance.renderWatchFaceToBitmap(
+ RenderParameters(DrawMode.INTERACTIVE, WatchFaceLayer.ALL_WATCH_FACE_LAYERS, null),
+ Instant.ofEpochMilli(1234567),
+ null,
+ null
+ )
+ assertThat(wallpaperService.lastComplicationType).isEqualTo(ComplicationType.RANGED_VALUE)
+ }
}
@RunWith(AndroidJUnit4::class)
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
index 6112bfe..44bd7b7 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
@@ -141,6 +141,23 @@
public fun updateComplicationData(slotIdToComplicationData: Map<Int, ComplicationData>)
/**
+ * Sets override complications which are displayed until [clearComplicationDataOverride] is
+ * called. For editors this is more efficient than repeatedly calling [renderWatchFaceToBitmap]
+ * with complication data.
+ *
+ * While there are overrides [updateComplicationData] has no effect until
+ * [clearComplicationDataOverride] is called.
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public fun overrideComplicationData(slotIdToComplicationData: Map<Int, ComplicationData>) {}
+
+ /**
+ * Clears any overrides set by [overrideComplicationData].
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public fun clearComplicationDataOverride() {}
+
+ /**
* Renders the watchface to a shared memory backed [Bitmap] with the given settings. Note this
* will be fairly slow since either software canvas or glReadPixels will be invoked.
*
@@ -433,6 +450,7 @@
private var lastWatchFaceColors: WatchFaceColors? = null
private var disconnectReason: Int? = null
private var closed = false
+ private var overrideSlotIdToComplicationData = HashMap<Int, ComplicationData>()
private val iWatchFaceListener =
object : IWatchfaceListener.Stub() {
@@ -505,6 +523,31 @@
)
}
+ override fun overrideComplicationData(slotIdToComplicationData: Map<Int, ComplicationData>) {
+ if (iInteractiveWatchFace.apiVersion >= 11) {
+ iInteractiveWatchFace.overrideComplicationData(
+ slotIdToComplicationData.map {
+ IdAndComplicationDataWireFormat(
+ it.key,
+ it.value.asWireComplicationData()
+ )
+ }
+ )
+ } else {
+ for ((id, complicationData) in slotIdToComplicationData) {
+ overrideSlotIdToComplicationData[id] = complicationData
+ }
+ }
+ }
+
+ override fun clearComplicationDataOverride() {
+ if (iInteractiveWatchFace.apiVersion >= 11) {
+ iInteractiveWatchFace.clearComplicationDataOverride()
+ } else {
+ overrideSlotIdToComplicationData.clear()
+ }
+ }
+
@RequiresApi(27)
override fun renderWatchFaceToBitmap(
renderParameters: RenderParameters,
@@ -519,7 +562,11 @@
renderParameters.toWireFormat(),
instant.toEpochMilli(),
userStyle?.toWireFormat(),
- idAndComplicationData?.map {
+ if (iInteractiveWatchFace.apiVersion >= 11) {
+ idAndComplicationData
+ } else {
+ mergeWithOverrideComplicationData(idAndComplicationData)
+ }?.map {
IdAndComplicationDataWireFormat(
it.key,
it.value.asWireComplicationData()
@@ -530,6 +577,27 @@
)
}
+ private fun mergeWithOverrideComplicationData(
+ idAndComplicationData: Map<Int, ComplicationData>?
+ ): Map<Int, ComplicationData>? {
+ if (overrideSlotIdToComplicationData.isEmpty()) {
+ return idAndComplicationData
+ }
+
+ if (idAndComplicationData.isNullOrEmpty()) {
+ return overrideSlotIdToComplicationData
+ }
+
+ val merged = HashMap(overrideSlotIdToComplicationData)
+ for ((id, complicationData) in idAndComplicationData) {
+ if (merged.contains(id)) {
+ continue
+ }
+ merged[id] = complicationData
+ }
+ return merged
+ }
+
override val isRemoteWatchFaceViewHostSupported = iInteractiveWatchFace.apiVersion >= 9
@RequiresApi(Build.VERSION_CODES.R)
diff --git a/wear/watchface/watchface-complications-data-source-ktx/build.gradle b/wear/watchface/watchface-complications-data-source-ktx/build.gradle
index 6d0a95c..dcd380b 100644
--- a/wear/watchface/watchface-complications-data-source-ktx/build.gradle
+++ b/wear/watchface/watchface-complications-data-source-ktx/build.gradle
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -45,7 +45,7 @@
androidx {
name = "Android Wear Complications Data Source Ktx"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2021"
description = "Kotlin suspend wrapper for Android Wear Complications Data Source"
metalavaK2UastEnabled = true
diff --git a/wear/watchface/watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFace.aidl b/wear/watchface/watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFace.aidl
index c06fdab8..7e61727 100644
--- a/wear/watchface/watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFace.aidl
+++ b/wear/watchface/watchface-data/src/main/aidl/androidx/wear/watchface/control/IInteractiveWatchFace.aidl
@@ -37,12 +37,12 @@
interface IInteractiveWatchFace {
// IMPORTANT NOTE: All methods must be given an explicit transaction id that must never change
// in the future to remain binary backwards compatible.
- // Next Id: 25
+ // Next Id: 28
/**
* API version number. This should be incremented every time a new method is added.
*/
- const int API_VERSION = 10;
+ const int API_VERSION = 11;
/** Indicates a "down" touch event on the watch face. */
const int TAP_TYPE_DOWN = 0;
@@ -243,4 +243,22 @@
* @since API version 10.
*/
UserStyleFlavorsWireFormat getUserStyleFlavors() = 25;
+
+ /**
+ * Send override ComplicationData to be used until clearComplicationDataOverride is called.
+ * While overrides, any calls to updateComplicationData are deferred until
+ * clearComplicationDataOverride is called.
+ *
+ * @since API version 11.
+ */
+ oneway void overrideComplicationData(
+ in List<IdAndComplicationDataWireFormat> complicationData) = 26;
+
+ /**
+ * Clears any complicaton data set by overrideComplicationData, and activates any complications
+ * set by updateComplicationData.
+ *
+ * @since API version 11.
+ */
+ oneway void clearComplicationDataOverride() = 27;
}
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt
index 2f02cf3..aec9b5c 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsReceiver.kt
@@ -92,10 +92,7 @@
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
- Intent.ACTION_BATTERY_LOW -> observer.onActionBatteryLow()
- Intent.ACTION_BATTERY_OKAY -> observer.onActionBatteryOkay()
- Intent.ACTION_POWER_CONNECTED -> observer.onActionPowerConnected()
- Intent.ACTION_POWER_DISCONNECTED -> observer.onActionPowerDisconnected()
+ Intent.ACTION_BATTERY_CHANGED -> processBatteryStatus(intent)
Intent.ACTION_TIME_CHANGED -> observer.onActionTimeChanged()
Intent.ACTION_TIME_TICK -> observer.onActionTimeTick()
Intent.ACTION_TIMEZONE_CHANGED -> observer.onActionTimeZoneChanged()
@@ -112,15 +109,11 @@
init {
context.registerReceiver(
receiver,
- IntentFilter(Intent.ACTION_SCREEN_OFF).apply {
- addAction(Intent.ACTION_SCREEN_ON)
+ IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply {
+ addAction(Intent.ACTION_TIME_CHANGED)
addAction(Intent.ACTION_TIME_TICK)
addAction(Intent.ACTION_TIMEZONE_CHANGED)
- addAction(Intent.ACTION_TIME_CHANGED)
- addAction(Intent.ACTION_BATTERY_LOW)
- addAction(Intent.ACTION_BATTERY_OKAY)
- addAction(Intent.ACTION_POWER_CONNECTED)
- addAction(Intent.ACTION_POWER_DISCONNECTED)
+ addAction(Intent.ACTION_SCREEN_OFF)
addAction(Intent.ACTION_USER_PRESENT)
addAction(WatchFaceImpl.MOCK_TIME_INTENT)
addAction(ACTION_AMBIENT_STARTED)
@@ -137,7 +130,6 @@
)
}
- /** Called to send observers initial battery state in advance of receiving any broadcasts. */
internal fun processBatteryStatus(batteryStatus: Intent?) {
val status = batteryStatus?.getIntExtra(BatteryManager.EXTRA_STATUS, -1) ?: -1
if (
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveWatchFaceImpl.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveWatchFaceImpl.kt
index 3c7ec5c..7cd7f68 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveWatchFaceImpl.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveWatchFaceImpl.kt
@@ -24,6 +24,7 @@
import androidx.annotation.RequiresApi
import androidx.wear.watchface.TapEvent
import androidx.wear.watchface.WatchFaceService
+import androidx.wear.watchface.complications.data.toApiComplicationData
import androidx.wear.watchface.control.data.WatchFaceRenderParams
import androidx.wear.watchface.data.IdAndComplicationDataWireFormat
import androidx.wear.watchface.data.IdAndComplicationStateWireFormat
@@ -292,6 +293,22 @@
}
}
+ override fun overrideComplicationData(
+ complicationDatumWireFormats: List<IdAndComplicationDataWireFormat>
+ ): Unit = aidlMethod(TAG, "overrideComplicationData") {
+ engine?.overrideComplications(
+ complicationDatumWireFormats.associateBy(
+ { it.id },
+ { it.complicationData.toApiComplicationData() }
+ )
+ )
+ }
+
+ override fun clearComplicationDataOverride(): Unit =
+ aidlMethod(TAG, "overrideComplicationData") {
+ engine?.removeAnyComplicationOverrides()
+ }
+
fun onDestroy() {
// Note this is almost certainly called on the ui thread, from release() above.
runBlocking {
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index cf39775..470a7e1 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -1426,14 +1426,35 @@
// The delay should change when battery is low.
watchFaceImpl.broadcastsReceiver!!
.receiver
- .onReceive(context, Intent(Intent.ACTION_BATTERY_LOW))
+ .onReceive(
+ context,
+ Intent(Intent.ACTION_BATTERY_CHANGED).apply {
+ putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING)
+ putExtra(
+ BatteryManager.EXTRA_LEVEL,
+ (BroadcastsReceiver.INITIAL_LOW_BATTERY_THRESHOLD - 1).toInt()
+ )
+ putExtra(BatteryManager.EXTRA_SCALE, 100)
+ }
+ )
+
assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH))
.isEqualTo(WatchFaceImpl.MAX_LOW_POWER_INTERACTIVE_UPDATE_RATE_MS)
// And go back to normal when battery is OK.
watchFaceImpl.broadcastsReceiver!!
.receiver
- .onReceive(context, Intent(Intent.ACTION_BATTERY_OKAY))
+ .onReceive(
+ context,
+ Intent(Intent.ACTION_BATTERY_CHANGED).apply {
+ putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING)
+ putExtra(
+ BatteryManager.EXTRA_LEVEL,
+ (BroadcastsReceiver.INITIAL_LOW_BATTERY_THRESHOLD + 1).toInt()
+ )
+ putExtra(BatteryManager.EXTRA_SCALE, 100)
+ }
+ )
assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH))
.isEqualTo(INTERACTIVE_UPDATE_RATE_MS)
}
@@ -1453,14 +1474,34 @@
// The delay should change when battery is low.
watchFaceImpl.broadcastsReceiver!!
.receiver
- .onReceive(context, Intent(Intent.ACTION_BATTERY_LOW))
+ .onReceive(
+ context,
+ Intent(Intent.ACTION_BATTERY_CHANGED).apply {
+ putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING)
+ putExtra(
+ BatteryManager.EXTRA_LEVEL,
+ (BroadcastsReceiver.INITIAL_LOW_BATTERY_THRESHOLD - 1).toInt()
+ )
+ putExtra(BatteryManager.EXTRA_SCALE, 100)
+ }
+ )
assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH))
.isEqualTo(WatchFaceImpl.MAX_LOW_POWER_INTERACTIVE_UPDATE_RATE_MS)
// And go back to normal when power is connected.
watchFaceImpl.broadcastsReceiver!!
.receiver
- .onReceive(context, Intent(Intent.ACTION_POWER_CONNECTED))
+ .onReceive(
+ context,
+ Intent(Intent.ACTION_BATTERY_CHANGED).apply {
+ putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING)
+ putExtra(
+ BatteryManager.EXTRA_LEVEL,
+ (BroadcastsReceiver.INITIAL_LOW_BATTERY_THRESHOLD - 1).toInt()
+ )
+ putExtra(BatteryManager.EXTRA_SCALE, 100)
+ }
+ )
assertThat(watchFaceImpl.computeDelayTillNextFrame(0, 0, Instant.EPOCH))
.isEqualTo(INTERACTIVE_UPDATE_RATE_MS)
}
diff --git a/webkit/integration-tests/instrumentation/lint-baseline.xml b/webkit/integration-tests/instrumentation/lint-baseline.xml
new file mode 100644
index 0000000..d08c143
--- /dev/null
+++ b/webkit/integration-tests/instrumentation/lint-baseline.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@SdkSuppress(minSdkVersion = VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidTestTargetSdk32/java/androidx/webkit/WebSettingsCompatForceDarkTest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidTestTargetSdkLatest/java/androidx/webkit/WebSettingsCompatDarkThemeTest.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/androidTestTargetSdkLatest/java/androidx/webkit/WebSettingsCompatLightThemeTest.java"/>
+ </issue>
+
+</issues>
diff --git a/webkit/integration-tests/testapp/build.gradle b/webkit/integration-tests/testapp/build.gradle
index 9b9342f..3cbe3e4 100644
--- a/webkit/integration-tests/testapp/build.gradle
+++ b/webkit/integration-tests/testapp/build.gradle
@@ -56,7 +56,6 @@
android {
defaultConfig {
- minSdkVersion 19
}
lintOptions {
disable "UnusedResources"
diff --git a/webkit/integration-tests/testapp/lint-baseline.xml b/webkit/integration-tests/testapp/lint-baseline.xml
index f1e21ab..9eb73549 100644
--- a/webkit/integration-tests/testapp/lint-baseline.xml
+++ b/webkit/integration-tests/testapp/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha02" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha02)" variant="all" version="8.3.0-alpha02">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="RestrictedApiAndroidX"
@@ -10,4 +10,103 @@
file="src/main/java/com/example/androidx/webkit/RequestedWithHeaderActivity.java"/>
</issue>
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/Api21Impl.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/AssetLoaderSimpleActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // This won't be called on < L, so we can safely apply @RequiresApi."
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/CustomInterstitialActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21) // for WebResourceRequest"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/CustomInterstitialActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/DocumentStartJavaScriptActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/ErrorLoggingWebViewClient.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/UserAgentMetadataActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/WebMessageListenerActivity.java"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/com/example/androidx/webkit/WebMessageListenerMaliciousWebsiteActivity.java"/>
+ </issue>
+
</issues>
diff --git a/window/window/proguard-rules.pro b/window/window/proguard-rules.pro
index b8cf236..b5faff0 100644
--- a/window/window/proguard-rules.pro
+++ b/window/window/proguard-rules.pro
@@ -24,4 +24,8 @@
public *** onWindowLayoutChanged(android.os.IBinder, androidx.window.sidecar.SidecarWindowLayoutInfo);
}
# Required for window area API reflection guard
--keep interface androidx.window.area.reflectionguard.* {*;}
\ No newline at end of file
+-keep interface androidx.window.area.reflectionguard.* {*;}
+# Required to support kotlin-reflect
+-keep,allowshrinking class androidx.window.layout.adapter.extensions.MulticastConsumer {
+ void accept(androidx.window.extensions.layout.WindowLayoutInfo);
+}
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/core/ConsumerAdapter.kt b/window/window/src/main/java/androidx/window/core/ConsumerAdapter.kt
index dae0489..faf3228 100644
--- a/window/window/src/main/java/androidx/window/core/ConsumerAdapter.kt
+++ b/window/window/src/main/java/androidx/window/core/ConsumerAdapter.kt
@@ -157,6 +157,10 @@
consumer.hashCode()
}
method.isToString(parameters) -> {
+ // MulticastConsumer#accept must not be obfuscated by proguard if kotlin-reflect
+ // is included. Otherwise, invocation of consumer#toString (e.g. by the library
+ // or by the on-device implementation) will crash due to kotlin-reflect not
+ // finding MulticastConsumer#accept.
consumer.toString()
}
else -> {
diff --git a/work/integration-tests/testapp/lint-baseline.xml b/work/integration-tests/testapp/lint-baseline.xml
index 90a35b4..a4a2489 100644
--- a/work/integration-tests/testapp/lint-baseline.xml
+++ b/work/integration-tests/testapp/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanThreadSleep"
@@ -20,75 +20,12 @@
</issue>
<issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.work.integration-tests`)"
- errorLine1=" remoteWorkManager.enqueue(listOf(request)).await()"
- errorLine2=" ~~~~~">
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is never < 21"
+ errorLine1=" if (Build.VERSION.SDK_INT < 21) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="src/main/java/androidx/work/integration/testapp/RemoteService.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.work.integration-tests`)"
- errorLine1=" ).await()"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/work/integration/testapp/RemoteService.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.work.integration-tests`)"
- errorLine1=" ).await()"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/work/integration/testapp/RemoteService.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.work.integration-tests`)"
- errorLine1=" .await()"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/work/integration/testapp/RemoteService.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.work.integration-tests`)"
- errorLine1=" remoteWorkManager.cancelAllWorkByTag(WORK_TAG).await()"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/work/integration/testapp/RemoteService.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.work.integration-tests`)"
- errorLine1=" remoteWorkManager.cancelAllWork().await()"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/work/integration/testapp/RemoteService.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.work.integration-tests`)"
- errorLine1=" val workInfoList: List<WorkInfo> = remoteWorkManager.getWorkInfos(query).await()"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/work/integration/testapp/RemoteService.kt"/>
- </issue>
-
- <issue
- id="RestrictedApiAndroidX"
- message="ListenableFutureKt.await can only be called from within the same library group (referenced groupId=`androidx.work` from groupId=`androidx.work.integration-tests`)"
- errorLine1=" setProgressAsync(progress).await()"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/work/integration/testapp/RemoteWorker.kt"/>
+ file="src/main/java/androidx/work/integration/testapp/MainActivity.kt"/>
</issue>
<issue
diff --git a/work/work-multiprocess/lint-baseline.xml b/work/work-multiprocess/lint-baseline.xml
new file mode 100644
index 0000000..31b21b0
--- /dev/null
+++ b/work/work-multiprocess/lint-baseline.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/work/multiprocess/parcelable/ParcelableRuntimeExtras.java"/>
+ </issue>
+
+</issues>
diff --git a/work/work-runtime-ktx/build.gradle b/work/work-runtime-ktx/build.gradle
index ffc1117..8a60f47 100644
--- a/work/work-runtime-ktx/build.gradle
+++ b/work/work-runtime-ktx/build.gradle
@@ -21,7 +21,7 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.Publish
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -35,7 +35,7 @@
androidx {
name = "WorkManager Kotlin Extensions"
- publish = Publish.SNAPSHOT_AND_RELEASE
+ type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
inceptionYear = "2018"
description = "Android WorkManager Kotlin Extensions"
metalavaK2UastEnabled = true
diff --git a/work/work-runtime/lint-baseline.xml b/work/work-runtime/lint-baseline.xml
index 503fac4..3e286da 100644
--- a/work/work-runtime/lint-baseline.xml
+++ b/work/work-runtime/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
<issue
id="BanSynchronizedMethods"
@@ -121,8 +121,8 @@
<issue
id="ForegroundServiceType"
message="To call `Service.startForeground()`, the `<service>` element of manifest file must have the `foregroundServiceType` attribute specified"
- errorLine1=" startForeground(notificationId, notification);"
- errorLine2=" ~~~~~~~~~~~~~~~">
+ errorLine1=" startForeground(notificationId, notification);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/androidx/work/impl/foreground/SystemForegroundService.java"/>
</issue>
@@ -146,6 +146,78 @@
</issue>
<issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @get:RequiresApi(21) // NetworkRequest class is available since 21"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/work/Constraints.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/work/Constraints.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@get:RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/work/impl/background/systemjob/JobSchedulerExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/work/impl/background/systemjob/JobSchedulerExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@get:RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/work/impl/background/systemjob/JobSchedulerExt.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@file:RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/work/impl/utils/NetworkApi21.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1=" @get:RequiresApi(21)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/work/impl/utils/NetworkRequestCompat.kt"/>
+ </issue>
+
+ <issue
+ id="ObsoleteSdkInt"
+ message="Unnecessary; SDK_INT is always >= 21"
+ errorLine1="@RequiresApi(21)"
+ errorLine2="~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/androidx/work/impl/WorkDatabasePathHelper.kt"/>
+ </issue>
+
+ <issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
errorLine1=" public final void addListener(Runnable listener, Executor executor) {"